root / gnuradio-core / src / python / gnuradio / blks2impl / wfm_rcv_fmdet.py @ 58bd4cea
History | View | Annotate | Download (9.7 kB)
| 1 | #
|
|---|---|
| 2 | # Copyright 2005,2006 Free Software Foundation, Inc.
|
| 3 | #
|
| 4 | # This file is part of GNU Radio
|
| 5 | #
|
| 6 | # GNU Radio is free software; you can redistribute it and/or modify
|
| 7 | # it under the terms of the GNU General Public License as published by
|
| 8 | # the Free Software Foundation; either version 3, or (at your option)
|
| 9 | # any later version.
|
| 10 | #
|
| 11 | # GNU Radio is distributed in the hope that it will be useful,
|
| 12 | # but WITHOUT ANY WARRANTY; without even the implied warranty of
|
| 13 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
| 14 | # GNU General Public License for more details.
|
| 15 | #
|
| 16 | # You should have received a copy of the GNU General Public License
|
| 17 | # along with GNU Radio; see the file COPYING. If not, write to
|
| 18 | # the Free Software Foundation, Inc., 51 Franklin Street,
|
| 19 | # Boston, MA 02110-1301, USA.
|
| 20 | #
|
| 21 | |
| 22 | from gnuradio import gr |
| 23 | from gnuradio.blks2impl.fm_emph import fm_deemph |
| 24 | import math |
| 25 | |
| 26 | class wfm_rcv_fmdet(gr.hier_block2): |
| 27 | def __init__ (self, demod_rate, audio_decimation): |
| 28 | """
|
| 29 | Hierarchical block for demodulating a broadcast FM signal.
|
| 30 |
|
| 31 | The input is the downconverted complex baseband signal (gr_complex).
|
| 32 | The output is two streams of the demodulated audio (float) 0=Left, 1=Right.
|
| 33 |
|
| 34 | @param demod_rate: input sample rate of complex baseband input.
|
| 35 | @type demod_rate: float
|
| 36 | @param audio_decimation: how much to decimate demod_rate to get to audio.
|
| 37 | @type audio_decimation: integer
|
| 38 | """ |
| 39 | gr.hier_block2.__init__(self, "wfm_rcv_fmdet", |
| 40 | gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature |
| 41 | gr.io_signature(2, 2, gr.sizeof_float)) # Output signature |
| 42 | lowfreq = -125e3
|
| 43 | highfreq = 125e3
|
| 44 | audio_rate = demod_rate / audio_decimation |
| 45 | |
| 46 | |
| 47 | # We assign to self so that outsiders can grab the demodulator
|
| 48 | # if they need to. E.g., to plot its output.
|
| 49 | #
|
| 50 | # input: complex; output: float
|
| 51 | |
| 52 | self.fm_demod = gr.fmdet_cf (demod_rate, lowfreq, highfreq, 0.05) |
| 53 | |
| 54 | # input: float; output: float
|
| 55 | self.deemph_Left = fm_deemph (audio_rate)
|
| 56 | self.deemph_Right = fm_deemph (audio_rate)
|
| 57 | |
| 58 | # compute FIR filter taps for audio filter
|
| 59 | width_of_transition_band = audio_rate / 32
|
| 60 | audio_coeffs = gr.firdes.low_pass (1.0 , # gain |
| 61 | demod_rate, # sampling rate
|
| 62 | 15000 ,
|
| 63 | width_of_transition_band, |
| 64 | gr.firdes.WIN_HAMMING) |
| 65 | # input: float; output: float
|
| 66 | self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs)
|
| 67 | if 1: |
| 68 | # Pick off the stereo carrier/2 with this filter. It attenuated 10 dB so apply 10 dB gain
|
| 69 | # We pick off the negative frequency half because we want to base band by it!
|
| 70 | ## NOTE THIS WAS HACKED TO OFFSET INSERTION LOSS DUE TO DEEMPHASIS
|
| 71 | |
| 72 | stereo_carrier_filter_coeffs = gr.firdes.complex_band_pass(10.0,
|
| 73 | demod_rate, |
| 74 | -19020,
|
| 75 | -18980,
|
| 76 | width_of_transition_band, |
| 77 | gr.firdes.WIN_HAMMING) |
| 78 | |
| 79 | #print "len stereo carrier filter = ",len(stereo_carrier_filter_coeffs)
|
| 80 | #print "stereo carrier filter ", stereo_carrier_filter_coeffs
|
| 81 | #print "width of transition band = ",width_of_transition_band, " audio rate = ", audio_rate
|
| 82 | |
| 83 | # Pick off the double side band suppressed carrier Left-Right audio. It is attenuated 10 dB so apply 10 dB gain
|
| 84 | |
| 85 | stereo_dsbsc_filter_coeffs = gr.firdes.complex_band_pass(20.0,
|
| 86 | demod_rate, |
| 87 | 38000-15000/2, |
| 88 | 38000+15000/2, |
| 89 | width_of_transition_band, |
| 90 | gr.firdes.WIN_HAMMING) |
| 91 | #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs)
|
| 92 | #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs
|
| 93 | # construct overlap add filter system from coefficients for stereo carrier
|
| 94 | |
| 95 | self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs)
|
| 96 | |
| 97 | # carrier is twice the picked off carrier so arrange to do a commplex multiply
|
| 98 | |
| 99 | self.stereo_carrier_generator = gr.multiply_cc();
|
| 100 | |
| 101 | # Pick off the rds signal
|
| 102 | |
| 103 | stereo_rds_filter_coeffs = gr.firdes.complex_band_pass(30.0,
|
| 104 | demod_rate, |
| 105 | 57000 - 1500, |
| 106 | 57000 + 1500, |
| 107 | width_of_transition_band, |
| 108 | gr.firdes.WIN_HAMMING) |
| 109 | #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs)
|
| 110 | #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs
|
| 111 | # construct overlap add filter system from coefficients for stereo carrier
|
| 112 | |
| 113 | self.rds_signal_filter = gr.fir_filter_fcc(audio_decimation, stereo_rds_filter_coeffs)
|
| 114 | |
| 115 | |
| 116 | |
| 117 | |
| 118 | |
| 119 | |
| 120 | self.rds_carrier_generator = gr.multiply_cc();
|
| 121 | self.rds_signal_generator = gr.multiply_cc();
|
| 122 | self_rds_signal_processor = gr.null_sink(gr.sizeof_gr_complex); |
| 123 | |
| 124 | |
| 125 | |
| 126 | alpha = 5 * 0.25 * math.pi / (audio_rate) |
| 127 | beta = alpha * alpha / 4.0
|
| 128 | max_freq = -2.0*math.pi*18990/audio_rate; |
| 129 | min_freq = -2.0*math.pi*19010/audio_rate; |
| 130 | |
| 131 | self.stereo_carrier_pll_recovery = gr.pll_refout_cc(alpha,beta,max_freq,min_freq);
|
| 132 | #self.stereo_carrier_pll_recovery.squelch_enable(False) #pll_refout does not have squelch yet, so disabled for now
|
| 133 | |
| 134 | |
| 135 | # set up mixer (multiplier) to get the L-R signal at baseband
|
| 136 | |
| 137 | self.stereo_basebander = gr.multiply_cc();
|
| 138 | |
| 139 | # pick off the real component of the basebanded L-R signal. The imaginary SHOULD be zero
|
| 140 | |
| 141 | self.LmR_real = gr.complex_to_real();
|
| 142 | self.Make_Left = gr.add_ff();
|
| 143 | self.Make_Right = gr.sub_ff();
|
| 144 | |
| 145 | self.stereo_dsbsc_filter = gr.fir_filter_fcc(audio_decimation, stereo_dsbsc_filter_coeffs)
|
| 146 | |
| 147 | |
| 148 | if 1: |
| 149 | |
| 150 | # send the real signal to complex filter to pick off the carrier and then to one side of a multiplier
|
| 151 | self.connect (self, self.fm_demod,self.stereo_carrier_filter,self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,0)) |
| 152 | # send the already filtered carrier to the otherside of the carrier
|
| 153 | self.connect (self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,1)) |
| 154 | # the resulting signal from this multiplier is the carrier with correct phase but at -38000 Hz.
|
| 155 | |
| 156 | # send the new carrier to one side of the mixer (multiplier)
|
| 157 | self.connect (self.stereo_carrier_generator, (self.stereo_basebander,0)) |
| 158 | # send the demphasized audio to the DSBSC pick off filter, the complex
|
| 159 | # DSBSC signal at +38000 Hz is sent to the other side of the mixer/multiplier
|
| 160 | self.connect (self.fm_demod,self.stereo_dsbsc_filter, (self.stereo_basebander,1)) |
| 161 | # the result is BASEBANDED DSBSC with phase zero!
|
| 162 | |
| 163 | # Pick off the real part since the imaginary is theoretically zero and then to one side of a summer
|
| 164 | self.connect (self.stereo_basebander, self.LmR_real, (self.Make_Left,0)) |
| 165 | #take the same real part of the DSBSC baseband signal and send it to negative side of a subtracter
|
| 166 | self.connect (self.LmR_real,(self.Make_Right,1)) |
| 167 | |
| 168 | # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone
|
| 169 | self.connect (self.stereo_basebander,(self.rds_carrier_generator,0)) |
| 170 | self.connect (self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1)) |
| 171 | # take signal, filter off rds, send into mixer 0 channel
|
| 172 | self.connect (self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0)) |
| 173 | # take rds_carrier_generator output and send into mixer 1 channel
|
| 174 | self.connect (self.rds_carrier_generator,(self.rds_signal_generator,1)) |
| 175 | # send basebanded rds signal and send into "processor" which for now is a null sink
|
| 176 | self.connect (self.rds_signal_generator,self_rds_signal_processor) |
| 177 | |
| 178 | |
| 179 | if 1: |
| 180 | # pick off the audio, L+R that is what we used to have and send it to the summer
|
| 181 | self.connect(self.fm_demod, self.audio_filter, (self.Make_Left, 1)) |
| 182 | # take the picked off L+R audio and send it to the PLUS side of the subtractor
|
| 183 | self.connect(self.audio_filter,(self.Make_Right, 0)) |
| 184 | # The result of Make_Left gets (L+R) + (L-R) and results in 2*L
|
| 185 | # The result of Make_Right gets (L+R) - (L-R) and results in 2*R
|
| 186 | self.connect(self.Make_Left , self.deemph_Left, (self, 0)) |
| 187 | self.connect(self.Make_Right, self.deemph_Right, (self, 1)) |
| 188 | # NOTE: mono support will require variable number of outputs in hier_block2s
|
| 189 | # See ticket:174 in Trac database
|
| 190 | #else:
|
| 191 | # self.connect (self.fm_demod, self.audio_filter, self)
|