diff --git a/.gitignore b/.gitignore index 6f875a66..8102018d 100644 --- a/.gitignore +++ b/.gitignore @@ -44,6 +44,7 @@ src/libmncc/libmncc.a src/libsound/libsound.a src/libsdr/libsdr.a src/libsample/libsample.a +src/libam/libam.a src/libclipper/libclipper.a src/anetz/libgermanton.a src/anetz/anetz diff --git a/configure.ac b/configure.ac index 470e46ef..62e67a3e 100644 --- a/configure.ac +++ b/configure.ac @@ -65,6 +65,7 @@ AC_OUTPUT( src/libscrambler/Makefile src/libemphasis/Makefile src/libfsk/Makefile + src/libam/Makefile src/libfm/Makefile src/libfilter/Makefile src/libwave/Makefile diff --git a/src/Makefile.am b/src/Makefile.am index 0de7471d..594dcf98 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 00000000..a139c71b --- /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 00000000..9c8bf587 --- /dev/null +++ b/src/libam/am.c @@ -0,0 +1,133 @@ +/* AM modulation and de-modulation + * + * (C) 2018 by Andreas Eversberg + * 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 . + */ + +#include +#include +#include +#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 00000000..e9e9889c --- /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); +