summaryrefslogtreecommitdiffstats
path: root/src/ss5/mf.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/ss5/mf.c')
-rw-r--r--src/ss5/mf.c202
1 files changed, 202 insertions, 0 deletions
diff --git a/src/ss5/mf.c b/src/ss5/mf.c
new file mode 100644
index 0000000..273146e
--- /dev/null
+++ b/src/ss5/mf.c
@@ -0,0 +1,202 @@
+/* multi frequency coder and decoder
+ *
+ * (C) 2020 by Andreas Eversberg <jolly@eversberg.eu>
+ * All Rights Reserved
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <stdint.h>
+#include <string.h>
+#include <errno.h>
+#include <math.h>
+#include "../libsample/sample.h"
+#include "mf.h"
+
+static int has_init = 0;
+static int fast_math = 0;
+static float *sin_tab = NULL, *cos_tab = NULL;
+
+/* global init */
+int mf_init(int _fast_math)
+{
+ fast_math = _fast_math;
+
+ if (fast_math) {
+ int i;
+
+ sin_tab = calloc(65536+16384, sizeof(*sin_tab));
+ if (!sin_tab) {
+ fprintf(stderr, "No mem!\n");
+ return -ENOMEM;
+ }
+ cos_tab = sin_tab + 16384;
+
+ /* generate sine and cosine */
+ for (i = 0; i < 65536+16384; i++)
+ sin_tab[i] = sin(2.0 * M_PI * (double)i / 65536.0);
+ }
+
+ has_init = 1;
+
+ return 0;
+}
+
+/* global exit */
+void mf_exit(void)
+{
+ if (sin_tab) {
+ free(sin_tab);
+ sin_tab = cos_tab = NULL;
+ }
+
+ has_init = 0;
+}
+
+mf_mod_t *mf_mod_init(double samplerate, int tones, double *freq, double *amplitude)
+{
+ mf_mod_t *mod;
+ int t;
+
+ if (!has_init) {
+ fprintf(stderr, "libmf was not initialized, plese fix!\n");
+ abort();
+ }
+
+ mod = calloc(1, sizeof(*mod) + sizeof(*mod->tone) * tones);
+ if (!mod) {
+ fprintf(stderr, "No mem!\n");
+ return NULL;
+ }
+ mod->tones = tones;
+
+ for (t = 0; t < tones; t++) {
+ /* set rotation of IQ vector */
+ if (fast_math)
+ mod->tone[t].rot = 65536.0 * freq[t] / samplerate;
+ else
+ mod->tone[t].rot = 2 * M_PI * freq[t] / samplerate;
+ mod->tone[t].amplitude = amplitude[t];
+ }
+
+ return mod;
+}
+
+void mf_mod_exit(mf_mod_t *mod)
+{
+ free(mod);
+}
+
+void mf_mod(mf_mod_t *mod, uint32_t *mask, sample_t *samples, int length)
+{
+ int t, s;
+ double phase;
+
+ for (s = 0; s < length; s++) {
+ samples[s] = 0;
+ for (t = 0; t < mod->tones; t++) {
+ /* continue phase even on muted tones */
+ phase = (mod->tone[t].phase += mod->tone[t].rot);
+ if (!((1 << t) & mask[s]))
+ continue;
+ if (fast_math) {
+ if (phase >= 65536.0)
+ phase -= 65536.0;
+ samples[s] += sin_tab[(uint16_t)phase] * mod->tone[t].amplitude;
+ } else {
+ if (phase >= 2.0 * M_PI)
+ phase -= 2.0 * M_PI;
+ samples[s] += cos(phase) * mod->tone[t].amplitude;
+ }
+ }
+ }
+}
+
+mf_demod_t *mf_demod_init(double samplerate, int tones, double *freq, double *width)
+{
+ mf_demod_t *demod;
+ int t;
+
+ if (!has_init) {
+ fprintf(stderr, "libmf was not initialized, plese fix!\n");
+ abort();
+ }
+
+ demod = calloc(1, sizeof(*demod) + sizeof(*demod->tone) * tones);
+ if (!demod) {
+ fprintf(stderr, "No mem!\n");
+ return NULL;
+ }
+ demod->tones = tones;
+
+ for (t = 0; t < tones; t++) {
+ /* set rotation of IQ vector */
+ if (fast_math)
+ demod->tone[t].rot = 65536.0 * -freq[t] / samplerate;
+ else
+ demod->tone[t].rot = 2 * M_PI * -freq[t] / samplerate;
+
+ /* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
+ iir_lowpass_init(&demod->tone[t].lp[0], width[t], samplerate, 2);
+ iir_lowpass_init(&demod->tone[t].lp[1], width[t], samplerate, 2);
+ }
+
+ return demod;
+}
+
+void mf_demod_exit(mf_demod_t *demod)
+{
+ free(demod);
+}
+
+void mf_demod(mf_demod_t *demod, sample_t *samples, int length, sample_t **levels_squared)
+{
+ sample_t I[length], Q[length];
+ double phase, rot;
+ int t, s;
+ double i, _sin, _cos;
+ sample_t *level_squared;
+
+ /* we apply filters and get the squared levels as a result */
+ for (t = 0; t < demod->tones; t++) {
+ phase = demod->tone[t].phase;
+ rot = demod->tone[t].rot;
+ for (s = 0; s < length; s++) {
+ phase += rot;
+ i = samples[s];
+ if (fast_math) {
+ if (phase >= 65536.0)
+ phase -= 65536.0;
+ _sin = sin_tab[(uint16_t)phase];
+ _cos = cos_tab[(uint16_t)phase];
+ } else {
+ if (phase >= 2.0 * M_PI)
+ phase -= 2.0 * M_PI;
+ _sin = sin(phase);
+ _cos = cos(phase);
+ }
+ I[s] = i * _cos;
+ Q[s] = i * _sin;
+ }
+ demod->tone[t].phase = phase;
+ iir_process(&demod->tone[t].lp[0], I, length); /* filter single side band */
+ iir_process(&demod->tone[t].lp[1], Q, length);
+ level_squared = levels_squared[t];
+ for (s = 0; s < length; s++)
+ level_squared[s] = 4.0 * (I[s] * I[s] + Q[s] * Q[s]); /* x2 because of single side band */
+ }
+}
+