Simulated effects of IF filter bandwidth.
This commit is contained in:
parent
13219ed6ba
commit
4619295d9a
35
NOTES.txt
35
NOTES.txt
|
@ -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
|
||||
----------------------------------
|
||||
|
||||
|
|
3
TODO.txt
3
TODO.txt
|
@ -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
19
pyfm.py
|
@ -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))
|
||||
|
|
Loading…
Reference in New Issue