aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/Makefile.am1
-rw-r--r--src/libam/Makefile.am6
-rw-r--r--src/libam/am.c133
-rw-r--r--src/libam/am.h26
4 files changed, 166 insertions, 0 deletions
diff --git a/src/Makefile.am b/src/Makefile.am
index 0de7471..594dcf9 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -18,6 +18,7 @@ SUBDIRS = \
libscrambler \
libemphasis \
libfsk \
+ libam \
libfm \
libfilter \
libwave \
diff --git a/src/libam/Makefile.am b/src/libam/Makefile.am
new file mode 100644
index 0000000..a139c71
--- /dev/null
+++ b/src/libam/Makefile.am
@@ -0,0 +1,6 @@
+AM_CPPFLAGS = -Wall -Wextra -g $(all_includes)
+
+noinst_LIBRARIES = libam.a
+
+libam_a_SOURCES = \
+ am.c
diff --git a/src/libam/am.c b/src/libam/am.c
new file mode 100644
index 0000000..9c8bf58
--- /dev/null
+++ b/src/libam/am.c
@@ -0,0 +1,133 @@
+/* AM modulation and de-modulation
+ *
+ * (C) 2018 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 <stdint.h>
+#include <string.h>
+#include <math.h>
+#include "../libsample/sample.h"
+#include "am.h"
+
+#define CARRIER_FILTER 30.0
+
+/* Amplitude modulation in SDR:
+ * Just use the base band (audio signal) as real value, and 0.0 as imaginary
+ * value. The you have two side bands. Be sure to have a DC level, so you
+ * have a carrier.
+ */
+
+int am_mod_init(am_mod_t *mod, double samplerate, double offset, double gain, double bias)
+{
+ memset(mod, 0, sizeof(*mod));
+ mod->gain = gain;
+ mod->bias = bias;
+ mod->phasestep = 2.0 * M_PI * offset / samplerate;
+
+ return 0;
+}
+
+void am_mod_exit(am_mod_t __attribute__((unused)) *mod)
+{
+}
+
+void am_modulate_complex(am_mod_t *mod, sample_t *amplitude, int num, float *baseband)
+{
+ int s;
+ double vector;
+ double phasestep = mod->phasestep;
+ double phase = mod->phase;
+ double gain = mod->gain;
+ double bias = mod->bias;
+
+ for (s = 0; s < num; s++) {
+ vector = *amplitude++ * gain + bias;
+ *baseband++ = cos(phase) * vector;
+ *baseband++ = sin(phase) * vector;
+ phase += phasestep;
+ if (phase < 0.0)
+ phase += 2.0 * M_PI;
+ else if (phase >= 2.0 * M_PI)
+ phase -= 2.0 * M_PI;
+ }
+
+ mod->phase = phase;
+}
+
+/* init AM demodulator */
+int am_demod_init(am_demod_t *demod, double samplerate, double offset, double bandwidth, double gain)
+{
+ memset(demod, 0, sizeof(*demod));
+ demod->gain = gain;
+ demod->phasestep = 2 * M_PI * -offset / samplerate;
+
+ /* use fourth order (2 iter) filter, since it is as fast as second order (1 iter) filter */
+ iir_lowpass_init(&demod->lp[0], bandwidth, samplerate, 2);
+ iir_lowpass_init(&demod->lp[1], bandwidth, samplerate, 2);
+
+ /* filter carrier */
+ iir_lowpass_init(&demod->lp[2], CARRIER_FILTER, samplerate, 1);
+
+ return 0;
+}
+
+void am_demod_exit(am_demod_t __attribute__((unused)) *demod)
+{
+}
+
+/* do amplitude demodulation of baseband and write them to samples */
+void am_demodulate_complex(am_demod_t *demod, sample_t *amplitude, int length, float *baseband, sample_t *I, sample_t *Q, sample_t *carrier)
+{
+ int s, ss;
+ double phasestep = demod->phasestep;
+ double phase = demod->phase;
+ double gain = demod->gain;
+ double i, q;
+ double _sin, _cos;
+
+ /* rotate spectrum */
+ for (s = 0, ss = 0; s < length; s++) {
+ i = baseband[ss++];
+ q = baseband[ss++];
+ _sin = sin(phase);
+ _cos = cos(phase);
+ phase += phasestep;
+ if (phase < 0.0)
+ phase += 2.0 * M_PI;
+ else if (phase >= 2.0 * M_PI)
+ phase -= 2.0 * M_PI;
+ I[s] = i * _cos - q * _sin;
+ Q[s] = i * _sin + q * _cos;
+ }
+ demod->phase = phase;
+
+ /* filter bandwidth */
+ iir_process(&demod->lp[0], I, length);
+ iir_process(&demod->lp[1], Q, length);
+
+ /* demod */
+ for (s = 0; s < length; s++)
+ amplitude[s] = carrier[s] = sqrt(I[s] * I[s] + Q[s] * Q[s]);
+
+ /* filter carrier */
+ iir_process(&demod->lp[2], carrier, length);
+
+ /* normalize */
+ for (s = 0; s < length; s++)
+ amplitude[s] = (amplitude[s] - carrier[s]) / carrier[s] * gain;
+}
+
diff --git a/src/libam/am.h b/src/libam/am.h
new file mode 100644
index 0000000..e9e9889
--- /dev/null
+++ b/src/libam/am.h
@@ -0,0 +1,26 @@
+#include "../libfilter/iir_filter.h"
+
+typedef struct am_mod {
+ double phasestep; /* angle to rotate vector per sample */
+ double phase; /* current phase */
+ double gain; /* gain to be multiplied to amplitude */
+ double bias; /* DC offset to add (carrier amplitude) */
+} am_mod_t;
+
+int am_mod_init(am_mod_t *mod, double samplerate, double offset, double gain, double bias);
+void am_mod_exit(am_mod_t *mod);
+void am_modulate_complex(am_mod_t *mod, sample_t *amplitude, int num, float *baseband);
+
+typedef struct am_demod {
+ double phasestep; /* angle to rotate vector per sample */
+ double phase; /* current rotation phase (used to shift) */
+ double last_phase; /* last phase of FM (used to demodulate) */
+ iir_filter_t lp[3]; /* filters received IQ signal/carrier */
+ double gain; /* gain to be expected from amplitude */
+ double bias; /* DC offset to be expected (carrier amplitude) */
+} am_demod_t;
+
+int am_demod_init(am_demod_t *demod, double samplerate, double offset, double gain, double bias);
+void am_demod_exit(am_demod_t *demod);
+void am_demodulate_complex(am_demod_t *demod, sample_t *amplitude, int length, float *baseband, sample_t *I, sample_t *Q, sample_t *carrier);
+