Hello. Here is the latest version of a set of two patches for the soundmodem application that I have created to fix AFSK operation at bit-rates lower than 1200 baud (e.g. 300 baud for HF packet radio or APRS).
Patch 1/2 follows: This patch modifies the AFSK modulator in version 0.16 of the soundmodem application as it appears to be problematic, in particular at 300 baud (in general, it might be buggy for bitrates slower than 1200 baud, although proper testing has not been carried out at other bitrates). In other words, this patch should fix the AFSK modulator for the problem reported, for example, here: - http://he.fi/archive/linux-hams/200608/0005.html - http://marc.info/?l=linux-hams&m=129059468102018&w=2 This patch also introduces optional windowing functions (other than the Hamming one) for experimentation, but at the moment they are completely untested and they can only be selected during compilation. Finally, this patch introduces tone filters at the receiver. After applying this patch, the AFSK modulator should work normally at 300 baud (or in general at bitrates less than 1200 baud) and the user should be able to select any value for the AFSK tone frequencies. Similar fixes are also introduced by this patch for the AFSK demodulator although, for successful reception at 300 baud, longer RX filters might be needed. Signed-off-by: Guido Trentalancia <[email protected]> --- afsk/modem.c | 284 +++++++++++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 201 insertions(+), 83 deletions(-) --- soundmodem-0.16-orig/afsk/modem.c 2004-04-13 17:44:40.000000000 +0200 +++ soundmodem-0.16/afsk/modem.c 2016-02-05 15:41:36.282279870 +0100 @@ -6,6 +6,9 @@ * Copyright (C) 1998-2000, 2003 * Thomas Sailer <[email protected]> * + * Copyright (C) 2012, 2016 + * Guido Trentalancia <[email protected]> + * * 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 2 of the License, or @@ -32,11 +35,85 @@ #include "modem.h" #include "costab.h" +#define max(a, b) (((a) > (b)) ? (a) : (b)) + +/* Rx FIR Filter parameters */ + +/* + * Rx FIR Filter length is normally equal + * to 16. Longer filters require more CPU. + */ +#define RXFILTLEN 16 + +#define RXFILTOVERBITS 3 + +#define RXFILTOVER (1<<(RXFILTOVERBITS)) +#define RXFILTFIDX(x) (((x)>>(16-(RXFILTOVERBITS)))&(RXFILTOVER-1)) +#define RXFILTFSAMP(x) ((x)>>16) + /* --------------------------------------------------------------------- */ +/* + * IZ6RDB 10/01/2016 + * Force the use of a minimum value for the sampling + * rate, because otherwise some sound cards might + * not work properly, when using the old OSS (i.e. + * "soundcard") driver and bitrates slower than 1200 + * baud (such as 300 baud for HF). + * + * The Alsa driver does not suffer this limitation + * and should be preferred. + * + * Higher values require more CPU time. + * + * The recommended minimum value is 11025 Hz for most + * sound cards. + */ +#define MINIMUM_SAMPLERATE 11025 + +/* + * IZ6RDB 01/03/2012: define different possible + * window types for the FIR filters and choose + * one of the available types: + * - NONE (minimum transition width) + * - HAMMING (window used up to version 0.16) + * - HANNING (low transition width) + * - BLACKMAN (maximum stopband attenuation) + */ +#define NONE 0 +#define HAMMING 1 +#define HANNING 2 +#define BLACKMAN 3 + +#define FIR_WINDOW HAMMING + +/* --------------------------------------------------------------------- */ + +#ifdef FIR_WINDOW +#if (FIR_WINDOW == NONE) +#define WINDOW_FUNCTION(x) no_window(x) +#define TRANSITION_WIDTH_CONST 0.92 +#elif (FIR_WINDOW == HAMMING) +#define WINDOW_FUNCTION(x) hamming_window(x) +#define TRANSITION_WIDTH_CONST 3.32 +#elif (FIR_WINDOW == HANNING) +#define WINDOW_FUNCTION(x) hanning_window(x) +#define TRANSITION_WIDTH_CONST 3.11 +#elif (FIR_WINDOW == BLACKMAN) +#define WINDOW_FUNCTION(x) blackman_window(x) +#define TRANSITION_WIDTH_CONST 5.565 +#else +#error "A valid FIR Window must be selected !" +#endif +#else // default to Hamming window +#define WINDOW_FUNCTION(x) hamming_window(x) +#define TRANSITION_WIDTH_CONST 3.32 +#endif + struct modstate { struct modemchannel *chan; - unsigned int bps, f0, f1, notdiff, maxbitlen; + unsigned int samplerate; + unsigned int bps, f0, f1, notdiff; unsigned int bitinc, bitph; unsigned int dds, ddsinc[2]; unsigned int bit; @@ -53,7 +130,10 @@ static const struct modemparams modparam static void *modconfig(struct modemchannel *chan, unsigned int *samplerate, const char *params[]) { struct modstate *s; - + unsigned int freq; + unsigned int required_samplerate; + unsigned int f_carrier, df; + if (!(s = malloc(sizeof(struct modstate)))) logprintf(MLOG_FATAL, "out of memory\n"); s->chan = chan; @@ -63,22 +143,36 @@ static void *modconfig(struct modemchann s->bps = 100; if (s->bps > 9600) s->bps= 9600; - } else - s->bps = 1200; - if (params[1]) { + } + if (params[1]) s->f0 = strtoul(params[1], NULL, 0); - if (s->f0 > s->bps * 4) - s->f0 = s->bps * 4; - } else - s->f0 = 1200; - if (params[2]) { + if (params[2]) s->f1 = strtoul(params[2], NULL, 0); - if (s->f1 > s->bps * 4) - s->f1 = s->bps * 4; - } else - s->f1 = 2200; s->notdiff = params[3] ? !strtoul(params[3], NULL, 0) : 0; - *samplerate = 8 * s->bps; + + /* Swap symbol frequencies, if necessary */ + if (s->f0 > s->f1) { + freq = s->f0; + s->f0 = s->f1; + s->f1 = freq; + } + + /* Calculate the (audio) carrier frequency */ + f_carrier = (s->f0 + s->f1)/2; + + /* Calculate symbol spacing */ + df = abs(s->f1 - s->f0); + + /* Nyquist criteria (minimum sampling rate) */ + required_samplerate = 2 * f_carrier + df + 2 * s->bps; + + /* + * Make sure that the sampling rate is at least + * equal to the minimum recommended value. + */ + *samplerate = max(required_samplerate, MINIMUM_SAMPLERATE); + s->samplerate = *samplerate; + return s; } @@ -86,16 +180,18 @@ static void modinit(void *state, unsigne { struct modstate *s = (struct modstate *)state; - s->maxbitlen = (samplerate + s->bps - 1) / s->bps; s->bitinc = (s->bps << 16) / samplerate; s->ddsinc[0] = (s->f0 << 16) / samplerate; s->ddsinc[1] = (s->f1 << 16) / samplerate; s->bit = 0; + s->dds = 0; + s->bitph = 0; } static void modsendbits(struct modstate *s, unsigned int bits, unsigned int nrsyms) { - int16_t sbuf[512]; + unsigned int bufsize = ((s->samplerate*512)/48000)%2 == 0 ? ((s->samplerate*512)/48000) : ((s->samplerate*512)/48000) + 1; + int16_t sbuf[bufsize]; int16_t *sptr = sbuf, *eptr = sbuf + sizeof(sbuf)/sizeof(sbuf[0]); while (nrsyms > 0) { @@ -145,20 +241,9 @@ struct modulator afskmodulator = { /* --------------------------------------------------------------------- */ -#define max(a, b) (((a) > (b)) ? (a) : (b)) - -/* RxFilter */ -#define WINDOWEXPAND 1.5 -#define RXFILTLEN 16 -#define RXFILTOVERBITS 3 -#define RXFILTOVER (1<<(RXFILTOVERBITS)) -#define RXFILTFIDX(x) (((x)>>(16-(RXFILTOVERBITS)))&(RXFILTOVER-1)) -#define RXFILTFSAMP(x) ((x)>>16) - struct demodstate { struct modemchannel *chan; unsigned int bps, f0, f1, notdiff, firlen; - unsigned int srate; unsigned int rxbits; unsigned int rxphase; unsigned int rxphinc; @@ -201,40 +286,50 @@ static const struct modemparams demodpar static void *demodconfig(struct modemchannel *chan, unsigned int *samplerate, const char *params[]) { - struct demodstate *s; - unsigned int f; + struct demodstate *s; + unsigned int freq; + unsigned int required_samplerate; + unsigned int f_carrier, df; + + if (!(s = malloc(sizeof(struct demodstate)))) + logprintf(MLOG_FATAL, "out of memory\n"); + s->chan = chan; + if (params[0]) { + s->bps = strtoul(params[0], NULL, 0); + if (s->bps < 100) + s->bps = 100; + if (s->bps > 9600) + s->bps= 9600; + } + if (params[1]) + s->f0 = strtoul(params[1], NULL, 0); + if (params[2]) + s->f1 = strtoul(params[2], NULL, 0); + s->notdiff = params[3] ? !strtoul(params[3], NULL, 0) : 0; + + /* Swap symbol frequencies, if necessary */ + if (s->f0 > s->f1) { + freq = s->f0; + s->f0 = s->f1; + s->f1 = freq; + } - if (!(s = malloc(sizeof(struct demodstate)))) - logprintf(MLOG_FATAL, "out of memory\n"); - s->chan = chan; - if (params[0]) { - s->bps = strtoul(params[0], NULL, 0); - if (s->bps < 100) - s->bps = 100; - if (s->bps > 9600) - s->bps= 9600; - } else - s->bps = 1200; - if (params[1]) { - s->f0 = strtoul(params[1], NULL, 0); - if (s->f0 > s->bps * 4) - s->f0 = s->bps * 4; - } else - s->f0 = 1200; - if (params[2]) { - s->f1 = strtoul(params[2], NULL, 0); - if (s->f1 > s->bps * 4) - s->f1 = s->bps * 4; - } else - s->f1 = 2200; - s->notdiff = params[3] ? !strtoul(params[3], NULL, 0) : 0; - f = s->f0; - if (s->f1 > f) - f = s->f1; - f += s->bps/2; - f = (2*f) + (f >> 1); - *samplerate = f; - return s; + /* Calculate the (audio) carrier frequency */ + f_carrier = (s->f0 + s->f1)/2; + + /* Calculate symbol spacing */ + df = abs(s->f1 - s->f0); + + /* Nyquist criteria (minimum sampling rate) */ + required_samplerate = 2 * f_carrier + df + 2 * s->bps; + + /* + * Make sure that the sampling rate is at least + * equal to the minimum recommended value. + */ + *samplerate = max(required_samplerate, MINIMUM_SAMPLERATE); + + return s; } static int demfilter(struct demodstate *state, const int16_t *val, unsigned int phase) @@ -317,7 +412,6 @@ static void demod8bits(struct demodstate s->rxphase += phase; } - static void demoddemodulate(void *state) { struct demodstate *s = (struct demodstate *)state; @@ -355,18 +449,28 @@ static void demoddemodulate(void *state) } } -static inline double sinc(double x) +/* + * The Hamming window was the only window + * available in the original version. + */ +static inline double no_window(double x) +{ + return 1; +} + +static inline double hamming_window(double x) { - double arg = x * M_PI; + return 0.54+0.46*cos((2*M_PI)*x); +} - if (arg == 0) - return 1; - return sin(arg) / arg; +static inline double hanning_window(double x) +{ + return 0.5+0.5*cos((2*M_PI)*x); } -static inline double hamming(double x) +static inline double blackman_window(double x) { - return 0.54-0.46*cos((2*M_PI)*x); + return 0.42+0.5*cos((2*M_PI)*x)+0.08*cos((4*M_PI)*x); } static void demodinit(void *state, unsigned int samplerate, unsigned int *bitrate) @@ -376,24 +480,39 @@ static void demodinit(void *state, unsig float f0i[RXFILTLEN*RXFILTOVER]; float f1r[RXFILTLEN*RXFILTOVER]; float f1i[RXFILTLEN*RXFILTOVER]; - double ph0, ph1, w, tscale; + double ph0, ph1, w; + float index, scaled_index; float max, max0, max1, max2, max3; unsigned int i, j; - tscale = (float)s->bps / (float)samplerate / RXFILTOVER / WINDOWEXPAND; - ph0 = 2.0 * M_PI * (float)s->f0 / (float)samplerate / RXFILTOVER; - ph1 = 2.0 * M_PI * (float)s->f1 / (float)samplerate / RXFILTOVER; + ph0 = 2.0 * M_PI * (float)s->f0 / (float)samplerate; + ph1 = 2.0 * M_PI * (float)s->f1 / (float)samplerate; for (i = 0; i < RXFILTLEN * RXFILTOVER; i++) { - w = i * tscale; - if (w > 1) - w = 0; - else - w = hamming(w); - f0r[i] = w * cos(ph0 * i); - f0i[i] = w * sin(ph0 * i); - f1r[i] = w * cos(ph1 * i); - f1i[i] = w * sin(ph1 * i); + index = i - (RXFILTLEN * RXFILTOVER)/2.0; + scaled_index = index / RXFILTOVER; + + w = WINDOW_FUNCTION(scaled_index / RXFILTLEN); + + if (index == 0) { + f0r[i] = w * cos(ph0 * scaled_index) * s->bps / samplerate; + f0i[i] = w * sin(ph0 * scaled_index) * s->bps / samplerate; + f1r[i] = w * cos(ph1 * scaled_index) * s->bps / samplerate; + f1i[i] = w * sin(ph1 * scaled_index) * s->bps / samplerate; + } else { + f0r[i] = w * cos(ph0 * scaled_index) * sin(M_PI * scaled_index * s->bps / samplerate) / M_PI / scaled_index; + f0i[i] = w * sin(ph0 * scaled_index) * sin(M_PI * scaled_index * s->bps / samplerate) / M_PI / scaled_index; + f1r[i] = w * cos(ph1 * scaled_index) * sin(M_PI * scaled_index * s->bps / samplerate) / M_PI / scaled_index; + f1i[i] = w * sin(ph1 * scaled_index) * sin(M_PI * scaled_index * s->bps / samplerate) / M_PI / scaled_index; + } + } + + if (RXFILTLEN % 2 == 0) { + f0r[0] = 0.0; + f0i[0] = 0.0; + f1r[0] = 0.0; + f1i[0] = 0.0; } + /* determine maximum */ max = 0; for (i = 0; i < RXFILTOVER; i++) { @@ -427,7 +546,6 @@ static void demodinit(void *state, unsig s->f.f32.f1r[RXFILTOVER-1-i][j] = f1r[j*RXFILTOVER+i]; s->f.f32.f1i[RXFILTOVER-1-i][j] = f1i[j*RXFILTOVER+i]; } - s->srate = samplerate; s->rxphinc = ((samplerate << 16) + s->bps / 2) / s->bps; *bitrate = s->bps; } -- To unsubscribe from this list: send the line "unsubscribe linux-hams" in the body of a message to [email protected] More majordomo info at http://vger.kernel.org/majordomo-info.html
