1
0
Fork 0

Simulated effects of IF filter bandwidth.

This commit is contained in:
Joris van Rantwijk 2014-01-12 18:12:53 +01:00
parent 13219ed6ba
commit 4619295d9a
3 changed files with 47 additions and 10 deletions

View File

@ -8,8 +8,9 @@ Valid sample rates
Sample rates between 300001 Hz and 900000 Hz (inclusive) are not supported.
They cause an invalid configuration of the RTL chip.
rsamp_ratio = 28.8 MHz * 2**22 / sample_rate
If bit 27 and bit 28 of rsamp_ratio are different, the RTL chip malfunctions.
rsamp_ratio = 28.8 MHz * 2**22 / sample_rate
If bit 27 and bit 28 of rsamp_ratio are different, the RTL chip malfunctions.
Behaviour of RTL and Elonics tuner
@ -54,6 +55,36 @@ Elonics IF filters: matched to sample rate (note this may not be optimal)
RTL AGC mode off
Effect of IF signal filtering
-----------------------------
Carson bandwidth rule:
IF_half_bandwidth = peak_freq_devation + modulating_freq
In case of broadcast FM, this is
75 kHz + 53 kHz = 128 kHz (worst case)
19 kHz + 53 kHz = 72 kHz (typical case)
Simulations of IF filtering show:
* narrow IF filter reduces noise in the baseband
* narrow IF filter causes gain roll-off for high modulating frequencies
* narrow IF filter causes harmonic distortion at high modulating deviation
IF filter with 100 kHz half-bandwidth:
* baseband gain >= -1 dB up to 75 kHz
* less than 0.1% distortion of modulating signal at 19 kHz peak deviation
* ~ 2% distortion of modulating signal at 75 kHz peak devation
IF filter with 75 kHz half-bandwidth:
* baseband gain ~ -3 dB at 60 kHz, ~ -8 dB at 75 kHz
* ~ 1% distortion of modulating signal at 19 kHz peak deviation
Optimal IF bandwidth is probably somewhere between 75 and 100 kHz, with
roll-off not too steep.
Weak stations benefit from a narrow IF filter to reduce noise.
Strong stations benefit from a wider IF filter to reduce harmonics.
Effect of settings on baseband SNR
----------------------------------

View File

@ -1,4 +1,4 @@
* (experiment) make nice plot of baseband distortion due to IF filtering
* (experiment) consider reducing IF filter bandwidth to ~ 80 kHz
* (experiment) consider downsampling IF signal before FM detection
* (experiment) measure effect of IF gain on baseband SNR
* (experiment) measure effect of IF gain linearity on baseband SNR
@ -6,7 +6,6 @@
* (experiment) try if RTL AGC mode improves FM decoding
* (feature) support 'M' 'k' suffixes for sample rates and tuning frequency
* (feature) implement off-line FM decoder in Python for experimentation
* (feature) implement stereo pilot pulse-per-second
* (speedup) maybe replace high-order FIR downsampling filter with 2nd order butterworth followed by lower order FIR filter
* figure out why we sometimes lose stereo lock

19
pyfm.py
View File

@ -295,7 +295,7 @@ def pilotLevel(d, fs, freqshift, nfft=None, bw=150.0e3):
return (p19db, guarddb, guarddb - p19db)
def modulateAndReconstruct(sigfreq, sigampl, nsampl, fs, noisebw=None, ifbw=None, ifnoise=0):
def modulateAndReconstruct(sigfreq, sigampl, nsampl, fs, noisebw=None, ifbw=None, ifnoise=0, ifdownsamp=1):
"""Create a pure sine wave, modulate to FM, add noise, filter, demodulate.
sigfreq :: frequency of sine wave in Hz
@ -305,6 +305,7 @@ def modulateAndReconstruct(sigfreq, sigampl, nsampl, fs, noisebw=None, ifbw=None
noisebw :: calculate noise after demodulation over this bandwidth
ifbw :: IF filter bandwidth in Hz, or None for no filtering
ifnoise :: IF noise level
ifdownsamp :: downsample factor before demodulation
Return (ampl, phase, noise)
where ampl is the amplitude of the reconstructed sine wave (~ sigampl)
@ -325,18 +326,24 @@ def modulateAndReconstruct(sigfreq, sigampl, nsampl, fs, noisebw=None, ifbw=None
# Filter IF.
if ifbw is not None:
b = scipy.signal.firwin(61, 2.0 * ifbw / fs, window='nuttall')
b = scipy.signal.firwin(101, 2.0 * ifbw / fs, window='nuttall')
fm = scipy.signal.lfilter(b, 1, fm)
fm = fm[61:]
# Downsample IF.
fs1 = fs
if ifdownsamp != 1:
fm = fm[::ifdownsamp]
fs1 = fs / ifdownsamp
# Demodulate.
sig1 = quadratureDetector(fm, fs=fs)
sig1 = quadratureDetector(fm, fs=fs1)
# Fit original sine wave.
k = len(sig1)
m = numpy.zeros((k, 3))
m[:,0] = numpy.sin(2*numpy.pi*sigfreq/fs * (numpy.arange(k) + nsampl - k))
m[:,1] = numpy.cos(2*numpy.pi*sigfreq/fs * (numpy.arange(k) + nsampl - k))
m[:,0] = numpy.sin(2*numpy.pi*sigfreq/fs1 * (numpy.arange(k) + nsampl - k))
m[:,1] = numpy.cos(2*numpy.pi*sigfreq/fs1 * (numpy.arange(k) + nsampl - k))
m[:,2] = 1
fit = numpy.linalg.lstsq(m, sig1)
csin, ccos, coffset = fit[0]
@ -350,7 +357,7 @@ def modulateAndReconstruct(sigfreq, sigampl, nsampl, fs, noisebw=None, ifbw=None
res1 = sig1 - m[:,0] * csin - m[:,1] * ccos
if noisebw is not None:
b = scipy.signal.firwin(61, 2.0 * noisebw / fs, window='nuttall')
b = scipy.signal.firwin(101, 2.0 * noisebw / fs1, window='nuttall')
res1 = scipy.signal.lfilter(b, 1, res1)
noise1 = numpy.sqrt(numpy.mean(res1 ** 2))