Linux HAM/Amateur Radio development
 help / color / mirror / Atom feed
From: Guido Trentalancia <iz6rdb@trentalancia.com>
To: linux-hams@vger.kernel.org
Cc: t.sailer@alumni.ethz.ch
Subject: soundmodem 0.16: fix the AFSK modulator for 300 baud operation (was: RE: 300bps Packet)
Date: Sat, 03 Mar 2012 01:54:27 +0100	[thread overview]
Message-ID: <1330736067.25864.15.camel@vortex> (raw)

Hello !

Although the soundmodem code has been removed from the kernel and moved
to userspace, there was a report still open about a bug for 300 baud
AFSK on the linux-hams list, so I am also carbon-copying the list...

I have created a patch for the AFSK modulator so that it is made to work
at 300 baud and later on I am possibly going to create a supplemental
one for the AFSK demodulator (as it appears still a bit buggy in my
setup).

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 lower than 1200 baud,
although I have not done any testing at other bitrates).

It is meant to be a temporary patch for further revision or for users
that cannot use 300 baud AFSK at all as it needs further testing and
improvement especially because, at least in my setup, it does not
entirely fix the demodulation at 300 baud AFSK (reception).

First, the patch modifies the AFSK modulator so that it always uses at
least a (pre-defined) minimum sampling rate of 11.025kHz (instead of
always using a supposedly variable sampling rate calculated from the
bitrate). This should make the code work on most soundcards (at the
expense of a few extra CPU cycles), including some buggy ones which
might not deal well with the low sampling rates that are the theoretical
minimum for low bitrates (such as 300 baud). Also, the modified code
makes sure that the sampling rate is always the same for both the
modulator and the demodulator (provided that they operate at the same
bitrate) and it introduces a different (supposedly better) method of
determining the optimal sampling rate.

Then, the patch removes the probably buggy code that always selects a
bitrate of 1200 baud for the modulator during the initial internal
configuration, so that the AFSK modem effectively honours the bitrate
selected by the user in the configuration file.

Finally, the patch removes the buggy code that changes the AFSK tone
frequencies if they are greater than four times the bitrate, thus
honouring the AFSK tone frequencies selected in the configuration file.

I have also introduced 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.

After applying this patch, the AFSK modulator should work normally for
300 baud (or in general bitrates different/less than 1200 baud) and the
user should be able to select any AFSK tone frequencies.

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

I shall note that the trick suggested there of using the original
soundmodem code with tones below 1200 Hz for 300 baud, did not work for
me.

As already explained this patch is for testing mainly, so please do not
apply it directly to new releases. Although the fixes described above for
the AFSK modulator have also been applied to the AFSK demodulator, the
latter needs further modifications and the whole patch needs to be tested,
reviewed and eventually improved so that it is stable on most setups.
I suppose the RX filters, or at least their parameters, should be
double-checked and eventually optimized for 300 baud demodulation (at the
moment I am doing some testing with increased values for RXFILTLEN and/or
RXFILTOVERBITS but so far I am still missing the optimal results that I
would like to achieve).

In the meanwhile, I would be grateful if somebody could test this on
other setups and then report.

Signed-off-by: Guido Trentalancia <iz6rdb@trentalancia.com>
---
 afsk/modem.c |  210 +++++++++++++++++++++++++++++++++++++++++++++++++----------
 1 file changed, 176 insertions(+), 34 deletions(-)

--- soundmodem-0.16-orig/afsk/modem.c	2012-02-29 20:31:24.356287000 +0100
+++ soundmodem-0.16/afsk/modem.c	2012-03-03 01:08:17.728744850 +0100
@@ -32,8 +32,58 @@
 #include "modem.h"
 #include "costab.h"
 
+/*
+ * IZ6RDB 29/02/2012: always use a minimum samplerate
+ * otherwise some sound cards, might not work well
+ * especially at bitrates lower than 1200 baud (such
+ * as 300 baud for HF).
+ *
+ * The value is defined in Hz and it should never be
+ * less than 9600 Hz to avoid problems, although the
+ * recommended value is 11025 Hz for most soundcards.
+ *
+ * Some cards, might even require that only their
+ * maximum allowed sampling rate of 48kHz is used
+ * (for example, according to online reports some
+ * embedded SiS 701x AC'97 controllers).
+ */
+#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
+ * - HAMMING (window used up to version 0.16)
+ * - HANNING
+ * - BLACKMAN
+ * - WELCH
+ */
+#define FIR_WINDOW	HAMMING
+
+/*
+ * IZ6RDB 01/03/2012: introduce a parameter
+ * called "bandwidth expansion factor", which
+ * is a real positive number slightly greater
+ * than unity (up to 2.0) that accounts for
+ * guard-bands and non-ideal filtering.
+ *
+ * This value is only used for estimating the
+ * required sampling rate.
+ *
+ * Values are usually between 1.125 and 1.4.
+ *
+ * The recommended value is 1.36.
+ */
+#define BANDWIDTH_EXPANSION_FACTOR	1.36
+
 /* --------------------------------------------------------------------- */
 
+#ifndef BANDWIDTH_EXPANSION_FACTOR
+#define BANDWIDTH_EXPANSION_FACTOR      1.36
+#endif
+
 struct modstate {
 	struct modemchannel *chan;
 	unsigned int bps, f0, f1, notdiff, maxbitlen;
@@ -53,6 +103,9 @@ static const struct modemparams modparam
 static void *modconfig(struct modemchannel *chan, unsigned int *samplerate, const char *params[])
 {
 	struct modstate *s;
+	unsigned int bandwidth, expanded_bandwidth;
+	unsigned int min_samplerate, optimized_samplerate;
+	unsigned int f_carrier;
 	
 	if (!(s = malloc(sizeof(struct modstate))))
 		logprintf(MLOG_FATAL, "out of memory\n");
@@ -63,22 +116,45 @@ 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;
+
+	/*
+	 * Calculate the bandwidth of the baseband (audio) signal:
+	 * basically this is the highest frequency of the two
+	 * possible tones.
+	 */
+	bandwidth = (s->f0 > s->f1) ? s->f0 : s->f1;
+
+	/*
+	 * Calculate the expanded bandwidth (with guard-bands)
+	 * to get a better estimate.
+	 */
+	expanded_bandwidth = BANDWIDTH_EXPANSION_FACTOR * bandwidth;
+
+	/* Nyquist criteria (minimum sampling rate) */
+	min_samplerate = 2 * expanded_bandwidth;
+
+	/* Calculate the (audio) carrier frequency */
+	f_carrier = (s->f0 + s->f1)/2;
+
+	/* Calculate intermediate operating point sampling rate */
+	optimized_samplerate = 4 * f_carrier;
+	if (optimized_samplerate > min_samplerate)
+		min_samplerate = optimized_samplerate;
+
+	/*
+	 * Make sure that the minimum recommended sampling
+	 * rate for the soundcard is exceeded.
+	 */
+	if (min_samplerate < MINIMUM_SAMPLERATE)
+		min_samplerate = MINIMUM_SAMPLERATE;
+
+	*samplerate = min_samplerate;
 	return s;
 }
 
@@ -202,7 +278,9 @@ static const struct modemparams demodpar
 static void *demodconfig(struct modemchannel *chan, unsigned int *samplerate, const char *params[])
 {
 	struct demodstate *s;
-	unsigned int f;
+	unsigned int bandwidth, expanded_bandwidth;
+	unsigned int min_samplerate, optimized_samplerate;
+	unsigned int f_carrier;
 
 	if (!(s = malloc(sizeof(struct demodstate))))
 		logprintf(MLOG_FATAL, "out of memory\n");
@@ -213,27 +291,45 @@ static void *demodconfig(struct modemcha
 			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;
-	f = s->f0;
-	if (s->f1 > f)
-		f = s->f1;
-	f += s->bps/2;
-	f = (2*f) + (f >> 1);
-	*samplerate = f;
+
+        /*
+         * Calculate the bandwidth of the baseband (audio) signal:
+         * basically this is the highest frequency of the two
+         * possible tones.
+         */
+        bandwidth = (s->f0 > s->f1) ? s->f0 : s->f1;
+
+	/*
+	 * Calculate the expanded bandwidth (with guard-bands)
+	 * to get a better estimate.
+	 */
+        expanded_bandwidth = BANDWIDTH_EXPANSION_FACTOR * bandwidth;
+
+        /* Nyquist criteria (minimum sampling rate) */
+        min_samplerate = 2 * expanded_bandwidth;
+
+	/* Calculate the (audio) carrier frequency */
+	f_carrier = (s->f0 + s->f1)/2;
+
+	/* Calculate intermediate operating point sampling rate */
+	optimized_samplerate = 4 * f_carrier;
+	if (optimized_samplerate > min_samplerate)
+		min_samplerate = optimized_samplerate;
+
+	/*
+	 * Make sure that the minimum recommended sampling
+	 * rate for the soundcard is exceeded.
+	 */
+	if (min_samplerate < MINIMUM_SAMPLERATE)
+		min_samplerate = MINIMUM_SAMPLERATE;
+
+	*samplerate = min_samplerate;
 	return s;
 }
 
@@ -364,11 +460,41 @@ static inline double sinc(double x)
         return sin(arg) / arg;
 }
 
-static inline double hamming(double x)
+/*
+ * The Hamming window is the original one (used
+ * exclusively up to version 0.16).
+ * Later versions introduced other optional
+ * windows, so that they can be changed before
+ * compilation and tested in order to try
+ * achieving reduced passband ripples at the
+ * expense of slower passband to stopband
+ * roll-off.
+ */
+static inline double no_window(double x)
+{
+	return 1;
+}
+
+static inline double hamming_window(double x)
 {
         return 0.54-0.46*cos((2*M_PI)*x);
 }
 
+static inline double hanning_window(double x)
+{
+        return 0.5-0.5*cos((2*M_PI)*x);
+}
+
+static inline double blackman_window(double x)
+{
+        return 0.42-0.5*cos((2*M_PI)*x)+0.08*cos((4*M_PI)*x);
+}
+
+static inline double welch_window(double x)
+{
+	return 1.0-pow((2*x-1),2);
+}
+
 static void demodinit(void *state, unsigned int samplerate, unsigned int *bitrate)
 {
 	struct demodstate *s = (struct demodstate *)state;
@@ -388,7 +514,23 @@ static void demodinit(void *state, unsig
                 if (w > 1)
                         w = 0;
                 else
-                        w = hamming(w);
+#ifdef FIR_WINDOW
+#if (FIR_WINDOW == NONE)
+			w = no_window(w);
+#elif (FIR_WINDOW == HAMMING)
+			w = hamming_window(w);
+#elif (FIR_WINDOW == HANNING)
+			w = hanning_window(w);
+#elif (FIR_WINDOW == BLACKMAN)
+			w = blackman_window(w);
+#elif (FIR_WINDOW == WELCH)
+			w = welch_window(w);
+#else
+#error "Unknown FIR Window selected !"
+#endif
+#else // default to Hamming window
+                        w = hamming_window(w);
+#endif
                 f0r[i] = w * cos(ph0 * i);
                 f0i[i] = w * sin(ph0 * i);
                 f1r[i] = w * cos(ph1 * i);


             reply	other threads:[~2012-03-03  0:54 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2012-03-03  0:54 Guido Trentalancia [this message]
2012-03-03  9:50 ` soundmodem 0.16: fix the AFSK modulator for 300 baud operation (was: RE: 300bps Packet) walter harms
2012-03-03 14:47   ` Guido Trentalancia
2012-03-04 14:33     ` walter harms
2012-03-04 20:16       ` Guido Trentalancia
     [not found]         ` <4F776E14.7090104@trinnet.net>
     [not found]           ` <1333379832.2112.15.camel@vortex>
     [not found]             ` <4F7B199F.3050104@trinnet.net>
     [not found]               ` <1333647291.2605.34.camel@vortex>
     [not found]                 ` <1333741818.4064.20.camel@vortex>
     [not found]                   ` <4F7F7A2C.2030500@trinnet.net>
     [not found]                     ` <1333809855.2433.10.camel@vortex>
2012-04-07 23:14                       ` Soundmodem hangs all USB keyboard or mouse input -- and soundmodem 0.16: fix the AFSK modulator for 300 baud operation David Ranch
2012-04-08 15:23                         ` Guido Trentalancia
2012-04-09 21:37                         ` Curt, WE7U
2012-03-03 18:48   ` soundmodem 0.16: fix the AFSK modulator for 300 baud operation (was: RE: 300bps Packet) Guido Trentalancia

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=1330736067.25864.15.camel@vortex \
    --to=iz6rdb@trentalancia.com \
    --cc=linux-hams@vger.kernel.org \
    --cc=t.sailer@alumni.ethz.ch \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox