rtl_fm: degraded demodulation caused by self-introduced DC !?

Hayati Ayguen h_ayguen at web.de
Sun Jul 19 11:15:07 UTC 2015


Hi,

i could find the problem last night.

The problem was caused by the DC spike, from the analog front-end!
In my prior mail(s), i rejected this explanation .. but now i found out,
that the digital anti-alias filter is such bad quality (a simple
averaging), that the DC is folded into the receive band when decimating
samplerate!

I could fix the problem my moving the new DC filter ("-E rdc") to full
input at capture_rate. That eliminates the DC problem completely and
therefor also removes the extra noise produced on demodulation.

For being able to apply DC filter before mixing, i had to do conversion
of captured 8 bit samples to 16 bit first, before mixing up the desired
band to zero with rotate_90. Therefore i also changed rotate_90 to work
with 16 bit samples.

The above works fine with default parameters (-F 0). It does NOT work
when using option "-F 9". Could not figure out why!?

Find attached the patch file. Please consider merging it upstream.

kind regards,
Hayati



Am 18.07.2015 um 10:54 schrieb Hayati Ayguen:
> 
> Hi Murat and all others,
> 
> what i have hear is definitely NOT the DC spike coming from the analog
> front-end!
> 
> Whilst my experiments, trying to get rtl_fm run on Raspi 2, i added an
> option to rtl_fm to be verbose ("-v 1"). See
> https://github.com/hayguen/librtlsdr
> 
> When calling rtl_fm with "-s 24000 -f 433.25M -M raw" i get following
> outputs:
> "capture_rate = 42 * 24000 = 1008000"
> "capture_freq = freq + capture_rate / 4 = 433502000"
> 
> 
> From capture_rate you see, that the RTL's ADC works at a samplerate of
> 1.008 MHz. And the RTL is parametrized to 433.502 MHz.
> So, the RTL's DC is at 433.502 MHz - far away from the desired 433.25 MHz.
> On digital signal preprocessing the rtl_fm mixes the desired frequency
> up to baseband 0 Hz and the does low pass filtering and decimation to
> the samplerate of 24 kHz.
> This result is what i recorded in my previous mail and had a look with
> SDR#, the screenshot.
> 
> At the moment, I had no look into the source of low pass filter. I
> suppose that the DC is introduced here.
> 
> kind regards,
> Hayati
> 
> 
> 
> Am 18.07.2015 um 08:45 schrieb Murat Tologlu:
>> Dear Hayati,
>>
>> I am glad to see that my Debian-Jessie suggestion helped you to progress.
>>
>> Regarding your recent question: If I don't understand wrong, This is a well-known problem called as DC Spike which exist more or less in all SDR receivers. . Dr. Wickert of UCCS has an excellent laboratory note on RTL-SDR also explains this nature (see page 7) :  http://www.eas.uccs.edu/wickert/ece4670/lecture_notes/Lab6.pdf You can reach Dr. Wickert's other DSP notes from here: http://www.eas.uccs.edu/wickert/index.shtml 
>>
>> DC Spike comes from analog front end of the RTL-SDR dongle and we can talk about several solutions. First and the easiest solution is to move the center frequency a bit up and down to avoid interference with DC spike (this is called as offset tuning). Some hardware manufacturer claim that they manufacture better quality thus lower DC spike sdr chips and boards. You may also consider a software "notch filter" to reduce the DC spike !
>>
>> Kind regards,
>> Murat 
>>
>> -----Original Message-----
>> From: osmocom-sdr [mailto:osmocom-sdr-bounces at lists.osmocom.org] On Behalf Of Hayati Ayguen
>> Sent: Saturday, July 18, 2015 2:02 AM
>> To: osmocom-sdr at lists.osmocom.org
>> Subject: rtl_fm: degraded demodulation caused by self-introduced DC !?
>>
>>
>> Hi,
>>
>> after i got rtl_fm run on Raspi 2 with Debian Jessie .. now i have some additional noise in the FM demodulated audio!
>>
>> With a "raw" recording (see attachment) i can see an additional carrier at the DC frequency of the demodulated output. That corresponds to the tuned frequency (= 433.25 MHz) parametrized to rtl_fm.
>> Due to calibration error, the FM carrier has some offset: ~ -1.3 kHz as visible in screenshot.
>> The DC carrier does demodulate to some distortion!
>>
>> Option "-E dc" does not help, cause that removes a DC in the demodulated output. An additional option to filter DC before demodulation does help a bit .. but does not solve the problem, which looks to be introduced earlier ..
>>
>> I would not have expected such a DC, cause IMHO it's produced whilst downconversion or filtering.
>> It's not the RTL dongle's DC, which should be far far away by 1/4 of the high samplerate.
>>
>> Someone else seen this problem?
>> Does anyone have a useful solution?
>>
>> kind regards,
>> Hayati
>>
>>
-------------- next part --------------
From 5e063d82042922d7620e48e4e1a805d383412cc9 Mon Sep 17 00:00:00 2001
From: Hayati Ayguen <h_ayguen at web.de>
Date: Sun, 19 Jul 2015 12:43:04 +0200
Subject: [PATCH 3/3] "rdc" filter in rtl_fm now at capture rate, before mixing
 or filtering:   DC is now totally removed, when not using option "-F 9"   due
 to bad anti-alias rejection the RTL dongle's DC folded into the decimated
 band   therefore swapped conversion to 16 bit and up-mixing by fs/4
 (rotate_90)

---
 src/rtl_fm.c | 65 +++++++++++++++++++++++++++++++++++++++++++-----------------
 1 file changed, 47 insertions(+), 18 deletions(-)

diff --git a/src/rtl_fm.c b/src/rtl_fm.c
index f607e4e..2b98f5f 100644
--- a/src/rtl_fm.c
+++ b/src/rtl_fm.c
@@ -109,7 +109,7 @@ struct dongle_state
 	uint32_t freq;
 	uint32_t rate;
 	int      gain;
-	uint16_t buf16[MAXIMUM_BUF_LENGTH];
+	int16_t  buf16[MAXIMUM_BUF_LENGTH];
 	uint32_t buf_len;
 	int      ppm_error;
 	int      offset_tuning;
@@ -215,7 +215,7 @@ void usage(void)
 		"\t[-E enable_option (default: none)]\n"
 		"\t    use multiple -E to enable multiple options\n"
 		"\t    edge:   enable lower edge tuning\n"
-		"\t    rdc:     enable dc blocking filter on raw I/Q data\n"
+		"\t    rdc:     enable dc blocking filter on raw I/Q data at capture rate\n"
 		"\t    adc:     enable dc blocking filter on demodulated audio\n"
 		"\t    dc:      same as adc\n"
 		"\t    deemp:  enable de-emphasis filter\n"
@@ -295,6 +295,27 @@ double log2(double n)
 }
 #endif
 
+void rotate16_90(int16_t *buf, uint32_t len)
+/* 90 rotation is 1+0j, 0+1j, -1+0j, 0-1j
+   or [0, 1, -3, 2, -4, -5, 7, -6] */
+{
+	uint32_t i;
+	int16_t tmp;
+	for (i=0; i<len; i+=8) {
+		tmp = - buf[i+3];
+		buf[i+3] = buf[i+2];
+		buf[i+2] = tmp;
+
+		buf[i+4] = - buf[i+4];
+		buf[i+5] = - buf[i+5];
+
+		tmp = - buf[i+6];
+		buf[i+6] = buf[i+7];
+		buf[i+7] = tmp;
+	}
+}
+
+
 void rotate_90(unsigned char *buf, uint32_t len)
 /* 90 rotation is 1+0j, 0+1j, -1+0j, 0-1j
    or [0, 1, -3, 2, -4, -5, 7, -6] */
@@ -642,26 +663,25 @@ void dc_block_audio_filter(struct demod_state *fm)
 	fm->dc_avg = avg;
 }
 
-void dc_block_raw_filter(struct demod_state *fm)
+void dc_block_raw_filter(struct demod_state *fm, int16_t *buf, int len)
 {
 	/* derived from dc_block_audio_filter,
 		running over the raw I/Q components
 	*/
-	int16_t *lp = fm->lowpassed;
 	int i, avgI, avgQ;
 	int64_t sumI = 0;
 	int64_t sumQ = 0;
-	for (i = 0; i < fm->lp_len; i += 2) {
-		sumI += lp[i];
-		sumQ += lp[i+1];
+	for (i = 0; i < len; i += 2) {
+		sumI += buf[i];
+		sumQ += buf[i+1];
 	}
-	avgI = sumI / ( fm->lp_len / 2 );
-	avgQ = sumQ / ( fm->lp_len / 2 );
+	avgI = sumI / ( len / 2 );
+	avgQ = sumQ / ( len / 2 );
 	avgI = (avgI + fm->dc_avgI * fm->rdc_block_const) / ( fm->rdc_block_const + 1 );
 	avgQ = (avgQ + fm->dc_avgQ * fm->rdc_block_const) / ( fm->rdc_block_const + 1 );
-	for (i = 0; i < fm->lp_len; i += 2) {
-		lp[i] -= avgI;
-		lp[i+1] -= avgQ;
+	for (i = 0; i < len; i += 2) {
+		buf[i] -= avgI;
+		buf[i+1] -= avgQ;
 	}
 	fm->dc_avgI = avgI;
 	fm->dc_avgQ = avgQ;
@@ -817,9 +837,6 @@ void full_demod(struct demod_state *d)
 			}
 		}
 	}
-	if (d->dc_block_raw) {
-		dc_block_raw_filter(d);
-	}
 	d->mode_demod(d);  /* lowpassed -> result */
 	if (d->mode_demod == &raw_demod) {
 		return;
@@ -853,10 +870,19 @@ static void rtlsdr_callback(unsigned char *buf, uint32_t len, void *ctx)
 			buf[i] = 127;}
 		s->mute = 0;
 	}
-	if (!s->offset_tuning) {
-		rotate_90(buf, len);}
+	/* 1st: convert to 16 bit - to allow easier calculation of DC */
 	for (i=0; i<(int)len; i++) {
-		s->buf16[i] = (int16_t)buf[i] - 127;}
+		s->buf16[i] = ( (int16_t)buf[i] - 127 );
+	}
+	/* 2nd: do DC filtering BEFORE up-mixing */
+	if (d->dc_block_raw) {
+		dc_block_raw_filter(d, s->buf16, (int)len);
+	}
+	/* 3rd: up-mixing */
+	if (!s->offset_tuning) {
+		rotate16_90(s->buf16, (int)len);
+		/* rotate_90(buf, len); */
+	}
 	pthread_rwlock_wrlock(&d->rw);
 	memcpy(d->lowpassed, s->buf16, 2*len);
 	d->lp_len = len;
@@ -923,6 +949,9 @@ static void optimal_settings(int freq, int rate)
 		dm->downsample_passes = (int)log2(dm->downsample) + 1;
 		dm->downsample = 1 << dm->downsample_passes;
 	}
+	if (verbosity) {
+		fprintf(stderr, "downsample_passes = %d (= # of fifth_order() iterations), downsample = %d\n", dm->downsample_passes, dm->downsample );
+	}
 	capture_freq = freq;
 	capture_rate = dm->downsample * dm->rate_in;
 	if (verbosity)
-- 
1.9.5.msysgit.1



More information about the osmocom-sdr mailing list