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. Sample rates between 300001 Hz and 900000 Hz (inclusive) are not supported.
They cause an invalid configuration of the RTL chip. 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 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 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 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) consider downsampling IF signal before FM detection
* (experiment) measure effect of IF gain on baseband SNR * (experiment) measure effect of IF gain on baseband SNR
* (experiment) measure effect of IF gain linearity 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 * (experiment) try if RTL AGC mode improves FM decoding
* (feature) support 'M' 'k' suffixes for sample rates and tuning frequency * (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 * (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 * (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 * 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) 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. """Create a pure sine wave, modulate to FM, add noise, filter, demodulate.
sigfreq :: frequency of sine wave in Hz 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 noisebw :: calculate noise after demodulation over this bandwidth
ifbw :: IF filter bandwidth in Hz, or None for no filtering ifbw :: IF filter bandwidth in Hz, or None for no filtering
ifnoise :: IF noise level ifnoise :: IF noise level
ifdownsamp :: downsample factor before demodulation
Return (ampl, phase, noise) Return (ampl, phase, noise)
where ampl is the amplitude of the reconstructed sine wave (~ sigampl) 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. # Filter IF.
if ifbw is not None: 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 = scipy.signal.lfilter(b, 1, fm)
fm = fm[61:] fm = fm[61:]
# Downsample IF.
fs1 = fs
if ifdownsamp != 1:
fm = fm[::ifdownsamp]
fs1 = fs / ifdownsamp
# Demodulate. # Demodulate.
sig1 = quadratureDetector(fm, fs=fs) sig1 = quadratureDetector(fm, fs=fs1)
# Fit original sine wave. # Fit original sine wave.
k = len(sig1) k = len(sig1)
m = numpy.zeros((k, 3)) m = numpy.zeros((k, 3))
m[:,0] = numpy.sin(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/fs * (numpy.arange(k) + nsampl - k)) m[:,1] = numpy.cos(2*numpy.pi*sigfreq/fs1 * (numpy.arange(k) + nsampl - k))
m[:,2] = 1 m[:,2] = 1
fit = numpy.linalg.lstsq(m, sig1) fit = numpy.linalg.lstsq(m, sig1)
csin, ccos, coffset = fit[0] 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 res1 = sig1 - m[:,0] * csin - m[:,1] * ccos
if noisebw is not None: 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) res1 = scipy.signal.lfilter(b, 1, res1)
noise1 = numpy.sqrt(numpy.mean(res1 ** 2)) noise1 = numpy.sqrt(numpy.mean(res1 ** 2))