summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2018-03-27 19:53:58 +0200
committerYuval Adam <_@yuv.al>2018-03-27 19:53:58 +0200
commite9a4e0abb625cc0f65be62e8cab148d3d0c639cb (patch)
tree198233c3547aca046b55e8e75258170f03ee325c
parenta340c14bdf9e877b6496d0c0df034b1b64e50713 (diff)
Finalize demod flow
-rw-r--r--numpy_fm_demod.py81
1 files changed, 53 insertions, 28 deletions
diff --git a/numpy_fm_demod.py b/numpy_fm_demod.py
index 44d232d..f689064 100644
--- a/numpy_fm_demod.py
+++ b/numpy_fm_demod.py
@@ -24,47 +24,72 @@ class NumpyFmDemod():
self.samples = sdr.read_samples(self.sample_count)
sdr.close()
- def demod(self):
- # Convert samples to a numpy array
- x1 = np.array(self.samples).astype('complex64')
+ def samples_to_np(self):
+ '''
+ Convert samples to a complex numpy array
+ '''
+ self.samples = 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
+ def mix_down_dc_offset(self):
+ '''
+ Mix the samples back down to account for DC offset
+ '''
+ fc1 = np.exp(-1.0j * 2.0 * np.pi * self.dc_offset / self.sample_rate * np.arange(len(self.samples)))
+ self.samples *= fc1
- # Downsample the signal to catch only the target frequency
+ def lowpass_filter(self):
+ '''
+ Apply low-pass filter 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
+ self.samples = signal.decimate(self.samples, decimation_rate)
+ self.sample_rate /= dec_rate
- y5 = x4[1:] * np.conj(x4[:-1])
- x5 = np.angle(y5)
+ def polar_discriminator(self):
+ '''
+ Apply a polar discriminator to demodulate the FM signal
+ '''
+ self.samples = np.angle(self.samples[1:] * np.conj(self.samples[:-1]))
- 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)
+ def de_emphasis_filter(self):
+ '''
+ Apply a de-emphasis filter
+ Still need to figure out exactly what's going on here
+ '''
+ d = self.sample_rate * 75e-6
+ x = np.exp(-1/d)
+ b, a = [1-x], [1,-x]
+ self.samples = signal.lfilter(b, a, self.samples)
+ def mono_decimate(self):
+ '''
+ Decimate the signal to catch the mono transmission
+ '''
audio_freq = 44100.0
- dec_audio = int(decimated_rate / audio_freq)
- Fs_audio = decimated_rate / dec_audio
+ decimation_rate = int(self.sample_rate / audio_freq)
+ audio_rate = self.sample_rate / decimation_rate
+ self.samples = signal.decimate(self.samples, decimation_rate)
- x7 = signal.decimate(x6, dec_audio)
+ def scale_volume(self):
+ '''
+ Scale samples to adjust volume
+ '''
+ self.samples *= 10000 / np.max(np.abs(self.samples))
- x7 *= 10000 / np.max(np.abs(x7))
- x7.astype('int16').tofile('wbfm-mono.raw')
+ def output_file(self, filename):
+ self.samples.astype('int16').tofile(filename)
def run(self):
self.capture_samples()
- self.demod()
+ self.samples_to_np()
+ self.mix_down_dc_offset()
+ self.lowpass_filter()
+ self.polar_discriminator()
+ self.de_emphasis_filter()
+ self.mono_decimate()
+ self.scale_volume()
+ self.output_file()
if __name__ == '__main__':
nfd = NumpyFmDemod(frequency=96.3e6)