diff options
| author | Yuval Adam <_@yuv.al> | 2018-03-27 19:53:58 +0200 |
|---|---|---|
| committer | Yuval Adam <_@yuv.al> | 2018-03-27 19:53:58 +0200 |
| commit | e9a4e0abb625cc0f65be62e8cab148d3d0c639cb (patch) | |
| tree | 198233c3547aca046b55e8e75258170f03ee325c | |
| parent | a340c14bdf9e877b6496d0c0df034b1b64e50713 (diff) | |
Finalize demod flow
| -rw-r--r-- | numpy_fm_demod.py | 81 |
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) |
