* Soundmodem patch for AFSK operation a low bit-rates (1/2)
@ 2016-04-03 16:03 Guido Trentalancia
2016-04-03 16:48 ` David Ranch
[not found] ` <57014908.6020708@trinnet.net>
0 siblings, 2 replies; 4+ messages in thread
From: Guido Trentalancia @ 2016-04-03 16:03 UTC (permalink / raw)
To: linux-hams
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 <iz6rdb@trentalancia.net>
---
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 <t.sailer@alumni.ethz.ch>
*
+ * Copyright (C) 2012, 2016
+ * Guido Trentalancia <iz6rdb@trentalancia.net>
+ *
* 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 majordomo@vger.kernel.org
More majordomo info at http://vger.kernel.org/majordomo-info.html
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: Soundmodem patch for AFSK operation a low bit-rates (1/2)
2016-04-03 16:03 Soundmodem patch for AFSK operation a low bit-rates (1/2) Guido Trentalancia
@ 2016-04-03 16:48 ` David Ranch
[not found] ` <57014908.6020708@trinnet.net>
1 sibling, 0 replies; 4+ messages in thread
From: David Ranch @ 2016-04-03 16:48 UTC (permalink / raw)
To: Guido Trentalancia, linux-hams
Hello Guido,
Thanks for working on those fixes. I think the best place for this
would be the soundmodem repository at:
http://gna.org/projects/soundmodem
Interestingly enough, Thomas Sailer has been updating the soundmodem
code in recent times for new automake related fixes.
Ps. What version of soundmodem should these patches be applied against?
--David
KI6ZHD
On 04/03/2016 09:03 AM, Guido Trentalancia wrote:
> 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:
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: Soundmodem patch for AFSK operation a low bit-rates (1/2)
[not found] ` <57014908.6020708@trinnet.net>
@ 2016-04-03 17:05 ` Guido Trentalancia
2016-04-03 18:21 ` David Ranch
0 siblings, 1 reply; 4+ messages in thread
From: Guido Trentalancia @ 2016-04-03 17:05 UTC (permalink / raw)
To: David Ranch, linux-hams
Hello David!
My reply follows your quoted text...
On 03 april 2016 18:47:04 CEST, David Ranch <linux-hams@trinnet.net> wrote:
>
>Hello Guido,
>
>Thanks for working on those fixes. I think the best place for this
>would be the soundmodem repository at:
>
> http://gna.org/projects/soundmodem
>
>Interestingly enough, Thomas Sailer has been updating the soundmodem
>code in recent times for new automake related fixes.
Yes, I've posted my patch there too, but it seems they don't want to fix the broken source code!...
>Ps. What version of soundmodem should these patches be applied
>against?
I've tested the patches successfully on releases from 0.16 upto the latest one. They might work with releases prior to 0.16, but I have not tested that case.
>--David
>KI6ZHD
73, IZ6RDB
>
>
>On 04/03/2016 09:03 AM, Guido Trentalancia wrote:
>
>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:
^ permalink raw reply [flat|nested] 4+ messages in thread
* Re: Soundmodem patch for AFSK operation a low bit-rates (1/2)
2016-04-03 17:05 ` Guido Trentalancia
@ 2016-04-03 18:21 ` David Ranch
0 siblings, 0 replies; 4+ messages in thread
From: David Ranch @ 2016-04-03 18:21 UTC (permalink / raw)
To: Guido Trentalancia, linux-hams
Hey Guido,
> Yes, I've posted my patch there too, but it seems they don't want to fix the broken source code!...
That doesn't surprise me as the soundmodem program has been essentially
on life-support for years now. I personally recommend people use use
Direwolf these days (works on Mac and Windows too). It supports
superior decodes, does 300/1200/9600 packet as well.
--David
KI6ZHD
^ permalink raw reply [flat|nested] 4+ messages in thread
end of thread, other threads:[~2016-04-03 18:21 UTC | newest]
Thread overview: 4+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2016-04-03 16:03 Soundmodem patch for AFSK operation a low bit-rates (1/2) Guido Trentalancia
2016-04-03 16:48 ` David Ranch
[not found] ` <57014908.6020708@trinnet.net>
2016-04-03 17:05 ` Guido Trentalancia
2016-04-03 18:21 ` David Ranch
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).