#!/usr/bin/env python3 # -*- coding: utf-8 -*- # Copyright 2007-2020 Free Software Foundation, Inc. # # SPDX-License-Identifier: GPL-3.0 # # GNU Radio Python Flow Graph # Title: FM stereo demod block import math from gnuradio import gr from gnuradio import blocks from gnuradio import filter, fft from gnuradio.filter import firdes from gnuradio import analog class wfm_rcv_pll(gr.hier_block2): def __init__(self, demod_rate, audio_decimation, deemph_tau): """ Hierarchical block for demodulating a broadcast FM signal. The input is the downconverted complex baseband signal (gr_complex). The output is two streams of the demodulated audio (float) 0=Left, 1=Right. Args: demod_rate: input sample rate of complex baseband input. (float) audio_decimation: how much to decimate demod_rate to get to audio. (integer) deemph_tau: deemphasis ime constant in seconds (75us in US, 50us in EUR). (float) """ gr.hier_block2.__init__(self, "wfm_rcv_pll", # Input signature gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(2, 2, gr.sizeof_float)) # Output signature if audio_decimation != int(audio_decimation): raise ValueError("audio_decimation needs to be an integer") audio_decimation = int(audio_decimation) ################################################## # Variables ################################################## self.demod_rate = demod_rate self.deemph_tau = deemph_tau self.stereo_carrier_filter_coeffs = stereo_carrier_filter_coeffs = firdes.band_pass( -2.0, demod_rate, 37600, 38400, 400, fft.window.WIN_HAMMING, 6.76) self.pilot_carrier_filter_coeffs = pilot_carrier_filter_coeffs = firdes.complex_band_pass( 1.0, demod_rate, 18980, 19020, 1500, fft.window.WIN_HAMMING, 6.76) self.deviation = deviation = 75000 self.audio_filter_coeffs = audio_filter_coeffs = firdes.low_pass( 1, demod_rate, 15000, 1500, fft.window.WIN_HAMMING, 6.76) self.audio_decim = audio_decim = audio_decimation self.audio_rate = audio_rate = demod_rate / audio_decim self.samp_delay = samp_delay = (len( pilot_carrier_filter_coeffs) - 1) // 2 + (len(stereo_carrier_filter_coeffs) - 1) // 2 ################################################## # Blocks ################################################## self.pilot_carrier_bpf = filter.fir_filter_fcc( 1, pilot_carrier_filter_coeffs) self.pilot_carrier_bpf.declare_sample_delay(0) self.stereo_carrier_bpf = filter.fft_filter_fff( 1, stereo_carrier_filter_coeffs, 1) self.stereo_carrier_bpf.declare_sample_delay(0) self.stereo_audio_lpf = filter.fft_filter_fff( audio_decim, audio_filter_coeffs, 1) self.stereo_audio_lpf.declare_sample_delay(0) self.mono_audio_lpf = filter.fft_filter_fff( audio_decim, audio_filter_coeffs, 1) self.mono_audio_lpf.declare_sample_delay(0) self.blocks_stereo_multiply = blocks.multiply_ff(1) self.blocks_pilot_multiply = blocks.multiply_cc(1) self.blocks_complex_to_imag = blocks.complex_to_imag(1) self.blocks_right_sub = blocks.sub_ff(1) self.blocks_left_add = blocks.add_ff(1) self.analog_quadrature_demod_cf = analog.quadrature_demod_cf( demod_rate / (2 * math.pi * deviation)) self.analog_pll_refout_cc = analog.pll_refout_cc( 0.001, 2 * math.pi * 19200 / demod_rate, 2 * math.pi * 18800 / demod_rate) self.analog_right_fm_deemph = analog.fm_deemph( fs=audio_rate, tau=deemph_tau) self.analog_left_fm_deemph = analog.fm_deemph( fs=audio_rate, tau=deemph_tau) self.blocks_delay_0 = blocks.delay(gr.sizeof_float * 1, samp_delay) ################################################## # Connections ################################################## self.connect((self.analog_left_fm_deemph, 0), (self, 0)) self.connect((self.analog_right_fm_deemph, 0), (self, 1)) self.connect((self.analog_pll_refout_cc, 0), (self.blocks_pilot_multiply, 1)) self.connect((self.analog_pll_refout_cc, 0), (self.blocks_pilot_multiply, 0)) self.connect((self.analog_quadrature_demod_cf, 0), (self.blocks_delay_0, 0)) self.connect((self.blocks_delay_0, 0), (self.blocks_stereo_multiply, 0)) self.connect((self.blocks_delay_0, 0), (self.mono_audio_lpf, 0)) self.connect((self.analog_quadrature_demod_cf, 0), (self.pilot_carrier_bpf, 0)) self.connect((self.blocks_left_add, 0), (self.analog_left_fm_deemph, 0)) self.connect((self.blocks_right_sub, 0), (self.analog_right_fm_deemph, 0)) self.connect((self.blocks_complex_to_imag, 0), (self.stereo_carrier_bpf, 0)) self.connect((self.blocks_pilot_multiply, 0), (self.blocks_complex_to_imag, 0)) self.connect((self.blocks_stereo_multiply, 0), (self.stereo_audio_lpf, 0)) # L - R path self.connect((self.mono_audio_lpf, 0), (self.blocks_left_add, 1)) self.connect((self.mono_audio_lpf, 0), (self.blocks_right_sub, 0)) self.connect((self.stereo_audio_lpf, 0), (self.blocks_left_add, 0)) self.connect((self.stereo_audio_lpf, 0), (self.blocks_right_sub, 1)) self.connect((self.stereo_carrier_bpf, 0), (self.blocks_stereo_multiply, 1)) self.connect((self.pilot_carrier_bpf, 0), (self.analog_pll_refout_cc, 0)) self.connect((self, 0), (self.analog_quadrature_demod_cf, 0))