From f469879ed03b294b675c886182dbf70030761417 Mon Sep 17 00:00:00 2001 From: Andreas Eversberg Date: Sun, 24 Sep 2017 15:59:24 +0200 Subject: SDR: Use filter to remove harmonics caused by downsampling/upsampling This drastically increases the signal-noise-ratio and removes co-channel interferences. It gains CPU usage, but only on SDR threads, not on the main thread. --- src/common/iir_filter.c | 34 ++++++++++++++++++++++++++++++++++ src/common/iir_filter.h | 1 + src/common/sdr.c | 29 ++++++++++++++++++++++++++++- 3 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/common/iir_filter.c b/src/common/iir_filter.c index d080728..3d1e15c 100644 --- a/src/common/iir_filter.c +++ b/src/common/iir_filter.c @@ -115,6 +115,7 @@ void iir_process(iir_filter_t *filter, sample_t *samples, int length) b1 = filter->b1; b2 = filter->b2; + /* these are state pointers, so no need to write back */ z1 = filter->z1; z2 = filter->z2; @@ -131,3 +132,36 @@ void iir_process(iir_filter_t *filter, sample_t *samples, int length) } } +void iir_process_baseband(iir_filter_t *filter, float *baseband, int length) +{ + double a0, a1, a2, b1, b2; + double *z1, *z2; + double in, out; + int iterations = filter->iter; + int i, j; + + /* get states */ + a0 = filter->a0; + a1 = filter->a1; + a2 = filter->a2; + b1 = filter->b1; + b2 = filter->b2; + + /* these are state pointers, so no need to write back */ + z1 = filter->z1; + z2 = filter->z2; + + /* process filter */ + for (i = 0; i < length; i++) { + in = *baseband; + for (j = 0; j < iterations; j++) { + out = in * a0 + z1[j]; + z1[j] = in * a1 + z2[j] - b1 * out; + z2[j] = in * a2 - b2 * out; + in = out; + } + *baseband = in; + baseband += 2; + } +} + diff --git a/src/common/iir_filter.h b/src/common/iir_filter.h index 9d237eb..a5956c8 100644 --- a/src/common/iir_filter.h +++ b/src/common/iir_filter.h @@ -12,5 +12,6 @@ void iir_highpass_init(iir_filter_t *filter, double frequency, int samplerate, i void iir_bandpass_init(iir_filter_t *filter, double frequency, int samplerate, int iterations); void iir_notch_init(iir_filter_t *filter, double frequency, int samplerate, int iterations); void iir_process(iir_filter_t *filter, sample_t *samples, int length); +void iir_process_baseband(iir_filter_t *filter, float *baseband, int length); #endif /* _FILTER_H */ diff --git a/src/common/sdr.c b/src/common/sdr.c index 3b768a2..1e32c74 100644 --- a/src/common/sdr.c +++ b/src/common/sdr.c @@ -47,6 +47,10 @@ enum paging_signal; /* enable to debug buffer handling */ //#define DEBUG_BUFFER +/* enable to test without oversampling filter */ +//#define DISABLE_FILTER + + typedef struct sdr_thread { int use; volatile int running, exit; /* flags to control exit of threads */ @@ -56,6 +60,7 @@ typedef struct sdr_thread { volatile int in, out; /* in and out pointers (atomic, so no locking required) */ int max_fill; /* measure maximum buffer fill */ double max_fill_timer; /* timer to display/reset maximum fill */ + iir_filter_t lp[2]; /* filter for upsample/downsample IQ data */ } sdr_thread_t; typedef struct sdr_chan { @@ -148,6 +153,10 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq return NULL; } sdr->thread_read.in = sdr->thread_read.out = 0; + if (oversample > 1) { + iir_lowpass_init(&sdr->thread_read.lp[0], samplerate / 2.0, sdr_config->samplerate, 2); + iir_lowpass_init(&sdr->thread_read.lp[1], samplerate / 2.0, sdr_config->samplerate, 2); + } memset(&sdr->thread_write, 0, sizeof(sdr->thread_write)); sdr->thread_write.buffer_size = sdr->latspl * 2 + 2; sdr->thread_write.buffer = calloc(sdr->thread_write.buffer_size, sizeof(*sdr->thread_write.buffer)); @@ -161,6 +170,10 @@ void *sdr_open(const char __attribute__((__unused__)) *audiodev, double *tx_freq return NULL; } sdr->thread_write.in = sdr->thread_write.out = 0; + if (oversample > 1) { + iir_lowpass_init(&sdr->thread_write.lp[0], samplerate / 2.0, sdr_config->samplerate, 2); + iir_lowpass_init(&sdr->thread_write.lp[1], samplerate / 2.0, sdr_config->samplerate, 2); + } } /* alloc fm modulation buffers */ @@ -397,6 +410,14 @@ static void *sdr_write_child(void *arg) } out = (out + 2) % sdr->thread_write.buffer_size; } + sdr->thread_write.out = out; +#ifndef DISABLE_FILTER + /* filter spectrum */ + if (sdr->oversample > 1) { + iir_process_baseband(&sdr->thread_write.lp[0], sdr->thread_write.buffer2, num * sdr->oversample); + iir_process_baseband(&sdr->thread_write.lp[1], sdr->thread_write.buffer2 + 1, num * sdr->oversample); + } +#endif #ifdef HAVE_UHD if (sdr_config->uhd) uhd_send(sdr->thread_write.buffer2, num * sdr->oversample); @@ -405,7 +426,6 @@ static void *sdr_write_child(void *arg) if (sdr_config->soapy) soapy_send(sdr->thread_write.buffer2, num * sdr->oversample); #endif - sdr->thread_write.out = out; } /* delay some time */ @@ -440,6 +460,13 @@ static void *sdr_read_child(void *arg) if (count > 0) { #ifdef DEBUG_BUFFER printf("Thread read %d samples from SDR and writes them to read buffer.\n", count); +#endif +#ifndef DISABLE_FILTER + /* filter spectrum */ + if (sdr->oversample > 1) { + iir_process_baseband(&sdr->thread_read.lp[0], sdr->thread_read.buffer2, count); + iir_process_baseband(&sdr->thread_read.lp[1], sdr->thread_read.buffer2 + 1, count); + } #endif in = sdr->thread_read.in; for (s = 0, ss = 0; s < count; s++) { -- cgit v1.2.3