summaryrefslogtreecommitdiff
path: root/numpy_fm_demod.py
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2018-03-26 19:47:08 +0200
committerYuval Adam <_@yuv.al>2018-03-26 19:47:08 +0200
commita340c14bdf9e877b6496d0c0df034b1b64e50713 (patch)
tree4267e664beeafb3baa4ef4e80553b298b06663eb /numpy_fm_demod.py
parent73d1989f569ebfa5908ac1fce4bb1d0c67703b98 (diff)
Finalize demod
Diffstat (limited to 'numpy_fm_demod.py')
-rw-r--r--numpy_fm_demod.py46
1 files changed, 42 insertions, 4 deletions
diff --git a/numpy_fm_demod.py b/numpy_fm_demod.py
index fb2c46b..44d232d 100644
--- a/numpy_fm_demod.py
+++ b/numpy_fm_demod.py
@@ -10,7 +10,7 @@ class NumpyFmDemod():
Based on the great tutorial by Fraida Fund
https://witestlab.poly.edu/blog/capture-and-decode-fm-radio/
'''
- def __init__(frequency, sample_rate=1140000, sample_count=8192000, dc_offset=250000):
+ def __init__(self, frequency, sample_rate=1140000, sample_count=8192000, dc_offset=250000):
self.freq = int(frequency)
self.sample_rate = sample_rate
self.sample_count = sample_count
@@ -21,12 +21,50 @@ class NumpyFmDemod():
sdr.sample_rate = self.sample_rate
sdr.center_freq = self.freq - self.dc_offset
sdr.gain = 'auto'
-
- samples = sdr.read_samples(self.sample_count)
+ self.samples = sdr.read_samples(self.sample_count)
sdr.close()
- self.raw_samples = np.array(samples).astype('complex64')
+ def demod(self):
+ # Convert samples to a numpy array
+ x1 = np.array(self.samples).astype('complex64')
+
+ # Mix the samples back down to avoid DC offset
+ fc1 = np.exp(-1.0j * 2.0 * np.pi * self.dc_offset / self.sample_rate * np.arange(len(x1)))
+ x2 = x1 * fc1
+
+ # Downsample the signal to catch only the target frequency
+ BANDWIDTH = 200000 # wideband FM signal is always 200kHz
+ TAPS = 64
+
+ # Use Remez algorithm to design filter coefficients
+ lpf = signal.remez(TAPS, [0, BANDWIDTH, BANDWIDTH + (self.sample_rate / 2 - BANDWIDTH) / 4, self.sample_rate / 2], [1,0], Hz=self.sample_rate)
+ x3 = signal.lfilter(lpf, 1.0, x2)
+
+ decimation_rate = int(self.sample_rate / BANDWIDTH)
+ x4 = x3[0::decimation_rate]
+ decimated_rate = self.sample_rate / decimation_rate
+
+ y5 = x4[1:] * np.conj(x4[:-1])
+ x5 = np.angle(y5)
+
+ d = decimated_rate * 75e-6 # Calculate the # of samples to hit the -3dB point
+ x = np.exp(-1/d) # Calculate the decay between each sample
+ b = [1-x] # Create the filter coefficients
+ a = [1,-x]
+ x6 = signal.lfilter(b,a,x5)
+
+ audio_freq = 44100.0
+ dec_audio = int(decimated_rate / audio_freq)
+ Fs_audio = decimated_rate / dec_audio
+
+ x7 = signal.decimate(x6, dec_audio)
+
+ x7 *= 10000 / np.max(np.abs(x7))
+ x7.astype('int16').tofile('wbfm-mono.raw')
+ def run(self):
+ self.capture_samples()
+ self.demod()
if __name__ == '__main__':
nfd = NumpyFmDemod(frequency=96.3e6)