aboutsummaryrefslogtreecommitdiffstats
path: root/src/common
diff options
context:
space:
mode:
authorAndreas Eversberg <jolly@eversberg.eu>2017-09-24 15:59:24 +0200
committerAndreas Eversberg <jolly@eversberg.eu>2017-09-24 15:59:24 +0200
commitf469879ed03b294b675c886182dbf70030761417 (patch)
tree0593170a830cf3060e851c03929691f2e2600cc3 /src/common
parentff5faa8697eb31e9a8b02734dac0261b66757c78 (diff)
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.
Diffstat (limited to 'src/common')
-rw-r--r--src/common/iir_filter.c34
-rw-r--r--src/common/iir_filter.h1
-rw-r--r--src/common/sdr.c29
3 files changed, 63 insertions, 1 deletions
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 */
@@ -441,6 +461,13 @@ static void *sdr_read_child(void *arg)
#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++) {
sdr->thread_read.buffer[in++] = sdr->thread_read.buffer2[ss++];