def __init__(self, channel_rate, audio_decim, deviation, audio_pass, audio_stop, gain=1.0, tau=75e-6): gr.hier_block2.__init__( self, "fm_demod_cf", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature k = channel_rate / (2 * pi * deviation) QUAD = gr.quadrature_demod_cf(k) audio_taps = optfir.low_pass( gain, # Filter gain channel_rate, # Sample rate audio_pass, # Audio passband audio_stop, # Audio stopband 0.1, # Passband ripple 60) # Stopband attenuation LPF = gr.fir_filter_fff(audio_decim, audio_taps) if tau is not None: DEEMPH = fm_deemph(channel_rate, tau) self.connect(self, QUAD, DEEMPH, LPF, self) else: self.connect(self, QUAD, LPF, self)
def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=5e3): """ Narrow Band FM Receiver. Takes a single complex baseband input stream and produces a single float output stream of audio sample in the range [-1, +1]. Args: audio_rate: sample rate of audio stream, >= 16k (integer) quad_rate: sample rate of output stream (integer) tau: preemphasis time constant (default 75e-6) (float) max_dev: maximum deviation in Hz (default 5e3) (float) quad_rate must be an integer multiple of audio_rate. Exported sub-blocks (attributes): squelch quad_demod deemph audio_filter """ gr.hier_block2.__init__(self, "nbfm_rx", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature # FIXME audio_rate and quad_rate ought to be exact rationals audio_rate = int(audio_rate) quad_rate = int(quad_rate) if quad_rate % audio_rate != 0: raise ValueError, "quad_rate is not an integer multiple of audio_rate" squelch_threshold = 20 # dB #self.squelch = gr.simple_squelch_cc(squelch_threshold, 0.001) # FM Demodulator input: complex; output: float k = quad_rate/(2*math.pi*max_dev) self.quad_demod = gr.quadrature_demod_cf(k) # FM Deemphasis IIR filter self.deemph = fm_deemph (quad_rate, tau=tau) # compute FIR taps for audio filter audio_decim = quad_rate // audio_rate audio_taps = gr.firdes.low_pass (1.0, # gain quad_rate, # sampling rate 2.7e3, # Audio LPF cutoff 0.5e3, # Transition band gr.firdes.WIN_HAMMING) # filter type print "len(audio_taps) =", len(audio_taps) # Decimating audio filter # input: float; output: float; taps: float self.audio_filter = gr.fir_filter_fff(audio_decim, audio_taps) self.connect(self, self.quad_demod, self.deemph, self.audio_filter, self)
def __init__(self, quad_rate, audio_decimation): """ Hierarchical block for demodulating a broadcast FM signal. The input is the downconverted complex baseband signal (gr_complex). The output is the demodulated audio (float). @param quad_rate: input sample rate of complex baseband input. @type quad_rate: float @param audio_decimation: how much to decimate quad_rate to get to audio. @type audio_decimation: integer """ gr.hier_block2.__init__( self, "wfm_rcv", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature volume = 20. max_dev = 75e3 fm_demod_gain = quad_rate / (2 * math.pi * max_dev) audio_rate = quad_rate / audio_decimation # We assign to self so that outsiders can grab the demodulator # if they need to. E.g., to plot its output. # # input: complex; output: float self.fm_demod = gr.quadrature_demod_cf(fm_demod_gain) # input: float; output: float self.deemph = fm_deemph(audio_rate) # compute FIR filter taps for audio filter width_of_transition_band = audio_rate / 32 audio_coeffs = gr.firdes.low_pass( 1.0, # gain quad_rate, # sampling rate audio_rate / 2 - width_of_transition_band, width_of_transition_band, gr.firdes.WIN_HAMMING) # input: float; output: float self.audio_filter = gr.fir_filter_fff(audio_decimation, audio_coeffs) self.connect(self, self.fm_demod, self.audio_filter, self.deemph, self)
def __init__ (self, quad_rate, audio_decimation): """ Hierarchical block for demodulating a broadcast FM signal. The input is the downconverted complex baseband signal (gr_complex). The output is the demodulated audio (float). @param quad_rate: input sample rate of complex baseband input. @type quad_rate: float @param audio_decimation: how much to decimate quad_rate to get to audio. @type audio_decimation: integer """ gr.hier_block2.__init__(self, "wfm_rcv", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature volume = 20. max_dev = 75e3 fm_demod_gain = quad_rate/(2*math.pi*max_dev) audio_rate = quad_rate / audio_decimation # We assign to self so that outsiders can grab the demodulator # if they need to. E.g., to plot its output. # # input: complex; output: float self.fm_demod = gr.quadrature_demod_cf (fm_demod_gain) # input: float; output: float self.deemph = fm_deemph (audio_rate) # compute FIR filter taps for audio filter width_of_transition_band = audio_rate / 32 audio_coeffs = gr.firdes.low_pass (1.0, # gain quad_rate, # sampling rate audio_rate/2 - width_of_transition_band, width_of_transition_band, gr.firdes.WIN_HAMMING) # input: float; output: float self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) self.connect (self, self.fm_demod, self.audio_filter, self.deemph, self)
def __init__(self, channel_rate, audio_decim, deviation, audio_pass, audio_stop, gain=1.0, tau=75e-6): gr.hier_block2.__init__(self, "fm_demod_cf", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature k = channel_rate/(2*pi*deviation) QUAD = gr.quadrature_demod_cf(k) audio_taps = optfir.low_pass(gain, # Filter gain channel_rate, # Sample rate audio_pass, # Audio passband audio_stop, # Audio stopband 0.1, # Passband ripple 60) # Stopband attenuation LPF = gr.fir_filter_fff(audio_decim, audio_taps) if tau is not None: DEEMPH = fm_deemph(channel_rate, tau) self.connect(self, QUAD, DEEMPH, LPF, self) else: self.connect(self, QUAD, LPF, self)
def __init__(self, freq, subdev_spec, which_USRP, gain, audio_output, debug): gr.hier_block2.__init__(self, "analog_receive_path", gr.io_signature(0, 0, 0), #input signature gr.io_signature(0, 0, 0)) #output signature self.DEBUG = debug self.freq = freq self.rx_gain = gain #Formerly From XML self.fusb_block_size = 2048 self.fusb_nblocks = 8 self.rx_usrp_pga_gain_scaling = 0.5 self.rx_base_band_bw = 5e3 self.rx_freq_deviation = 2.5e3 # acquire USRP via USB 2.0 #self.u = usrp.source_c(fusb_block_size=self.fusb_block_size, # fusb_nblocks=self.fusb_nblocks, # which=which_USRP) self.u = uhd.single_usrp_source( device_addr="", io_type=uhd.io_type_t.COMPLEX_FLOAT32, num_channels=1, ) self.u.get_device # get A/D converter sampling rate #adc_rate = self.u.adc_rate() # 64 MS/s adc_rate = 64e6 # 64 MS/s if self.DEBUG: print " Rx Path ADC rate: %d" %(adc_rate) # setting USRP and GNU Radio decimation rate self.audio_rate = 16e3 self.max_gr_decim_rate = 40 self._usrp_decim = 250 self.gr_rate1 = adc_rate / self._usrp_decim gr_interp = 1 gr_decim = 16 self.gr_rate2 = self.gr_rate1 / gr_decim if self.DEBUG: print " usrp decim: ", self._usrp_decim print " gr rate 1: ", self.gr_rate1 print " gr decim: ", gr_decim print " gr rate 2: ", self.gr_rate2 print " gr interp: ", gr_interp print " audio rate: ", self.audio_rate # ================ Set up flowgraph ================================= # set USRP decimation ratio #self.u.set_decim_rate(self._usrp_decim) self.u.set_samp_rate(self.gr_rate1) self.u.set_antenna("RX2") # set USRP daughterboard subdevice if subdev_spec is None: subdev_spec = usrp.pick_rx_subdevice(self.u) #self.u.set_mux(usrp.determine_rx_mux_value(self.u, subdev_spec)) #self.subdev = usrp.selected_subdev(self.u, subdev_spec) #if self.DEBUG: # print " RX Path use daughterboard: %s" % (self.subdev.side_and_name()) # set USRP RF frequency """ Set the center frequency in Hz. Tuning is a two step process. First we ask the front-end to tune as close to the desired frequency as it can. Then we use the result of that operation and our target_frequency to determine the value for the digital up converter. """ assert(self.freq != None) #r = self.u.tune(0, self.subdev, self.freq) r = self.u.set_center_freq(self.freq, 0) if self.DEBUG: if r: print "----Rx RF frequency set to %f Hz" %(self.freq) else: print "Failed to set Rx frequency to %f Hz" %(self.freq) raise ValueError, eng_notation.num_to_str(self.freq) # set USRP Rx PGA gain #r = self.subdev.gain_range() #_rx_usrp_gain_range = r[1] - r[0] #_rx_usrp_gain = r[0]+_rx_usrp_gain_range * self.rx_usrp_pga_gain_scaling #self.subdev.set_gain(_rx_usrp_gain) #self.u.set_gain(3.25, 0) #if self.DEBUG: # print " USRP Rx PGA Gain Range: min = %g, max = %g, step size = %g" \ # %(r[0], r[1], r[2]) # print " USRP Rx PGA gain set to: %g" %(_rx_usrp_gain) # Do NOT Enable USRP Auto Tx/Rx switching for analog flow graph! #self.subdev.set_enable(False) # Baseband Channel Filter using FM Carson's Rule chan_bw = 2*(self.rx_base_band_bw+self.rx_freq_deviation) #Carson's Rule chan_filt_coeffs_float = optfir.low_pass (1, #gain self.gr_rate1, #sampling rate chan_bw, #passband cutoff chan_bw*1.35, #stopband cutoff 0.1, #passband ripple 60) #stopband attenuation chan_filt_coeffs_fixed = ( 0.000457763671875, 0.000946044921875, 0.00067138671875, 0.001068115234375, 0.00091552734375, 0.0008544921875, 0.000518798828125, 0.0001220703125, -0.000396728515625, -0.0008544921875, -0.00128173828125, -0.00146484375, -0.001434326171875, -0.0010986328125, -0.000518798828125, 0.000274658203125, 0.001129150390625, 0.00189208984375, 0.00238037109375, 0.00250244140625, 0.002166748046875, 0.0013427734375, 0.000152587890625, -0.001220703125, -0.002532958984375, -0.0035400390625, -0.003997802734375, -0.003753662109375, -0.002777099609375, -0.0010986328125, 0.000946044921875, 0.00311279296875, 0.00494384765625, 0.00604248046875, 0.006103515625, 0.005035400390625, 0.00286865234375, -0.0001220703125, -0.00347900390625, -0.006561279296875, -0.008758544921875, -0.00958251953125, -0.008636474609375, -0.005950927734375, -0.001739501953125, 0.00335693359375, 0.00848388671875, 0.0126953125, 0.01507568359375, 0.014862060546875, 0.01171875, 0.00579833984375, -0.002227783203125, -0.01123046875, -0.0196533203125, -0.02587890625, -0.028228759765625, -0.025421142578125, -0.016754150390625, -0.002166748046875, 0.017608642578125, 0.041015625, 0.0660400390625, 0.090240478515625, 0.111083984375, 0.12640380859375, 0.134490966796875, 0.134490966796875, 0.12640380859375, 0.111083984375, 0.090240478515625, 0.0660400390625, 0.041015625, 0.017608642578125, -0.002166748046875, -0.016754150390625, -0.025421142578125, -0.028228759765625, -0.02587890625, -0.0196533203125, -0.01123046875, -0.002227783203125, 0.00579833984375, 0.01171875, 0.014862060546875, 0.01507568359375, 0.0126953125, 0.00848388671875, 0.00335693359375, -0.001739501953125, -0.005950927734375, -0.008636474609375, -0.00958251953125, -0.008758544921875, -0.006561279296875, -0.00347900390625, -0.0001220703125, 0.00286865234375, 0.005035400390625, 0.006103515625, 0.00604248046875, 0.00494384765625, 0.00311279296875, 0.000946044921875, -0.0010986328125, -0.002777099609375, -0.003753662109375, -0.003997802734375, -0.0035400390625, -0.002532958984375, -0.001220703125, 0.000152587890625, 0.0013427734375, 0.002166748046875, 0.00250244140625, 0.00238037109375, 0.00189208984375, 0.001129150390625, 0.000274658203125, -0.000518798828125, -0.0010986328125, -0.001434326171875, -0.00146484375, -0.00128173828125, -0.0008544921875, -0.000396728515625, 0.0001220703125, 0.000518798828125, 0.0008544921875, 0.00091552734375, 0.001068115234375, 0.00067138671875, 0.000946044921875, 0.000457763671875) #r = gr.enable_realtime_scheduling () self.chan_filt = dsp.fir_ccf_fm_demod_decim (chan_filt_coeffs_fixed, 14, gr_decim, 0, 0, 0, 0) print "Tap length of chan FIR: ", len(chan_filt_coeffs_fixed) # Set the software LNA gain on the output of the USRP gain= self.rx_gain self.rx_gain = max(0.0, min(gain, 1e7)) if self.DEBUG: print " Rx Path initial software signal gain: %f (max 1e7)" %(gain) print " Rx Path actual software signal gain : %f (max 1e7)" %(self.rx_gain) #FM Demodulator fm_demod_gain = self.audio_rate / (2*math.pi*self.rx_freq_deviation) self.fm_demod = gr.quadrature_demod_cf (fm_demod_gain) #Compute FIR filter taps for audio filter width_of_transition_band = self.rx_base_band_bw * 0.35 audio_coeffs = gr.firdes.low_pass (1.0, #gain self.gr_rate2, #sampling rate self.rx_base_band_bw, width_of_transition_band, gr.firdes.WIN_HAMMING) self.audio_filter = gr.fir_filter_fff(1, audio_coeffs) passband_cutoff = self.rx_base_band_bw stopband_cutoff = passband_cutoff * 1.35 self.deemph = fm_deemph(self.audio_rate) if self.DEBUG: print "Length Audio FIR ", len(audio_coeffs) # Audio sink audio_sink = audio.sink(int(self.audio_rate),"default") # "", #Audio output pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp # False) # ok_to_block if self.DEBUG: print "Before Connecting Blocks" # Wiring Up #WITH CHANNEL FILTER #self.connect (self.u, self.chan_filt, self.lna, self.fm_demod, self.audio_filter, interpolator, self.deemph, self.volume_control, audio_sink) #self.connect (self.u, self.fm_demod, self.audio_filter, self.deemph, self.volume_control, howto_rx, audio_sink) self.connect (self.u, self.chan_filt, self.audio_filter, self.deemph, audio_sink)
def __init__(self, demod_rate, audio_decimation): """ 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. @param demod_rate: input sample rate of complex baseband input. @type demod_rate: float @param audio_decimation: how much to decimate demod_rate to get to audio. @type audio_decimation: integer """ gr.hier_block2.__init__( self, "wfm_rcv_fmdet", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(2, 2, gr.sizeof_float)) # Output signature lowfreq = -125e3 / demod_rate highfreq = 125e3 / demod_rate audio_rate = demod_rate / audio_decimation # We assign to self so that outsiders can grab the demodulator # if they need to. E.g., to plot its output. # # input: complex; output: float self.fm_demod = gr.fmdet_cf(demod_rate, lowfreq, highfreq, 0.05) # input: float; output: float self.deemph_Left = fm_deemph(audio_rate) self.deemph_Right = fm_deemph(audio_rate) # compute FIR filter taps for audio filter width_of_transition_band = audio_rate / 32 audio_coeffs = gr.firdes.low_pass( 1.0, # gain demod_rate, # sampling rate 15000, width_of_transition_band, gr.firdes.WIN_HAMMING) # input: float; output: float self.audio_filter = gr.fir_filter_fff(audio_decimation, audio_coeffs) if 1: # Pick off the stereo carrier/2 with this filter. It # attenuated 10 dB so apply 10 dB gain We pick off the # negative frequency half because we want to base band by # it! ## NOTE THIS WAS HACKED TO OFFSET INSERTION LOSS DUE TO ## DEEMPHASIS stereo_carrier_filter_coeffs = gr.firdes.complex_band_pass( 10.0, demod_rate, -19020, -18980, width_of_transition_band, gr.firdes.WIN_HAMMING) #print "len stereo carrier filter = ",len(stereo_carrier_filter_coeffs) #print "stereo carrier filter ", stereo_carrier_filter_coeffs #print "width of transition band = ",width_of_transition_band, " audio rate = ", audio_rate # Pick off the double side band suppressed carrier # Left-Right audio. It is attenuated 10 dB so apply 10 dB # gain stereo_dsbsc_filter_coeffs = gr.firdes.complex_band_pass( 20.0, demod_rate, 38000 - 15000 / 2, 38000 + 15000 / 2, width_of_transition_band, gr.firdes.WIN_HAMMING) #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs) #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs # construct overlap add filter system from coefficients # for stereo carrier self.stereo_carrier_filter = gr.fir_filter_fcc( audio_decimation, stereo_carrier_filter_coeffs) # carrier is twice the picked off carrier so arrange to do # a commplex multiply self.stereo_carrier_generator = gr.multiply_cc() # Pick off the rds signal stereo_rds_filter_coeffs = gr.firdes.complex_band_pass( 30.0, demod_rate, 57000 - 1500, 57000 + 1500, width_of_transition_band, gr.firdes.WIN_HAMMING) #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs) #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs # construct overlap add filter system from coefficients for stereo carrier self.rds_signal_filter = gr.fir_filter_fcc( audio_decimation, stereo_rds_filter_coeffs) self.rds_carrier_generator = gr.multiply_cc() self.rds_signal_generator = gr.multiply_cc() self_rds_signal_processor = gr.null_sink(gr.sizeof_gr_complex) loop_bw = 2 * math.pi / 100.0 max_freq = -2.0 * math.pi * 18990 / audio_rate min_freq = -2.0 * math.pi * 19010 / audio_rate self.stereo_carrier_pll_recovery = gr.pll_refout_cc( loop_bw, max_freq, min_freq) #self.stereo_carrier_pll_recovery.squelch_enable(False) ##pll_refout does not have squelch yet, so disabled for #now # set up mixer (multiplier) to get the L-R signal at # baseband self.stereo_basebander = gr.multiply_cc() # pick off the real component of the basebanded L-R # signal. The imaginary SHOULD be zero self.LmR_real = gr.complex_to_real() self.Make_Left = gr.add_ff() self.Make_Right = gr.sub_ff() self.stereo_dsbsc_filter = gr.fir_filter_fcc( audio_decimation, stereo_dsbsc_filter_coeffs) if 1: # send the real signal to complex filter to pick off the # carrier and then to one side of a multiplier self.connect(self, self.fm_demod, self.stereo_carrier_filter, self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator, 0)) # send the already filtered carrier to the otherside of the carrier # the resulting signal from this multiplier is the carrier # with correct phase but at -38000 Hz. self.connect(self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator, 1)) # send the new carrier to one side of the mixer (multiplier) self.connect(self.stereo_carrier_generator, (self.stereo_basebander, 0)) # send the demphasized audio to the DSBSC pick off filter, the complex # DSBSC signal at +38000 Hz is sent to the other side of the mixer/multiplier # the result is BASEBANDED DSBSC with phase zero! self.connect(self.fm_demod, self.stereo_dsbsc_filter, (self.stereo_basebander, 1)) # Pick off the real part since the imaginary is # theoretically zero and then to one side of a summer self.connect(self.stereo_basebander, self.LmR_real, (self.Make_Left, 0)) #take the same real part of the DSBSC baseband signal and #send it to negative side of a subtracter self.connect(self.LmR_real, (self.Make_Right, 1)) # Make rds carrier by taking the squared pilot tone and # multiplying by pilot tone self.connect(self.stereo_basebander, (self.rds_carrier_generator, 0)) self.connect(self.stereo_carrier_pll_recovery, (self.rds_carrier_generator, 1)) # take signal, filter off rds, send into mixer 0 channel self.connect(self.fm_demod, self.rds_signal_filter, (self.rds_signal_generator, 0)) # take rds_carrier_generator output and send into mixer 1 # channel self.connect(self.rds_carrier_generator, (self.rds_signal_generator, 1)) # send basebanded rds signal and send into "processor" # which for now is a null sink self.connect(self.rds_signal_generator, self_rds_signal_processor) if 1: # pick off the audio, L+R that is what we used to have and # send it to the summer self.connect(self.fm_demod, self.audio_filter, (self.Make_Left, 1)) # take the picked off L+R audio and send it to the PLUS # side of the subtractor self.connect(self.audio_filter, (self.Make_Right, 0)) # The result of Make_Left gets (L+R) + (L-R) and results in 2*L # The result of Make_Right gets (L+R) - (L-R) and results in 2*R self.connect(self.Make_Left, self.deemph_Left, (self, 0)) self.connect(self.Make_Right, self.deemph_Right, (self, 1))
def __init__(self, freq, subdev_spec, which_USRP, gain, audio_output, debug): gr.hier_block2.__init__( self, "analog_receive_path", gr.io_signature(0, 0, 0), #input signature gr.io_signature(0, 0, 0)) #output signature self.DEBUG = debug self.freq = freq self.rx_gain = gain #Formerly From XML self.fusb_block_size = 2048 self.fusb_nblocks = 8 self.rx_usrp_pga_gain_scaling = 0.5 self.rx_base_band_bw = 5e3 self.rx_freq_deviation = 2.5e3 # acquire USRP via USB 2.0 #self.u = usrp.source_c(fusb_block_size=self.fusb_block_size, # fusb_nblocks=self.fusb_nblocks, # which=which_USRP) self.u = uhd.single_usrp_source( device_addr="", io_type=uhd.io_type_t.COMPLEX_FLOAT32, num_channels=1, ) self.u.get_device # get A/D converter sampling rate #adc_rate = self.u.adc_rate() # 64 MS/s adc_rate = 64e6 # 64 MS/s if self.DEBUG: print " Rx Path ADC rate: %d" % (adc_rate) # setting USRP and GNU Radio decimation rate self.audio_rate = 16e3 self.max_gr_decim_rate = 40 self._usrp_decim = 250 self.gr_rate1 = adc_rate / self._usrp_decim gr_interp = 1 gr_decim = 16 self.gr_rate2 = self.gr_rate1 / gr_decim if self.DEBUG: print " usrp decim: ", self._usrp_decim print " gr rate 1: ", self.gr_rate1 print " gr decim: ", gr_decim print " gr rate 2: ", self.gr_rate2 print " gr interp: ", gr_interp print " audio rate: ", self.audio_rate # ================ Set up flowgraph ================================= # set USRP decimation ratio #self.u.set_decim_rate(self._usrp_decim) self.u.set_samp_rate(self.gr_rate1) self.u.set_antenna("RX2") # set USRP daughterboard subdevice if subdev_spec is None: subdev_spec = usrp.pick_rx_subdevice(self.u) #self.u.set_mux(usrp.determine_rx_mux_value(self.u, subdev_spec)) #self.subdev = usrp.selected_subdev(self.u, subdev_spec) #if self.DEBUG: # print " RX Path use daughterboard: %s" % (self.subdev.side_and_name()) # set USRP RF frequency """ Set the center frequency in Hz. Tuning is a two step process. First we ask the front-end to tune as close to the desired frequency as it can. Then we use the result of that operation and our target_frequency to determine the value for the digital up converter. """ assert (self.freq != None) #r = self.u.tune(0, self.subdev, self.freq) r = self.u.set_center_freq(self.freq, 0) if self.DEBUG: if r: print "----Rx RF frequency set to %f Hz" % (self.freq) else: print "Failed to set Rx frequency to %f Hz" % (self.freq) raise ValueError, eng_notation.num_to_str(self.freq) # set USRP Rx PGA gain #r = self.subdev.gain_range() #_rx_usrp_gain_range = r[1] - r[0] #_rx_usrp_gain = r[0]+_rx_usrp_gain_range * self.rx_usrp_pga_gain_scaling #self.subdev.set_gain(_rx_usrp_gain) #self.u.set_gain(3.25, 0) #if self.DEBUG: # print " USRP Rx PGA Gain Range: min = %g, max = %g, step size = %g" \ # %(r[0], r[1], r[2]) # print " USRP Rx PGA gain set to: %g" %(_rx_usrp_gain) # Do NOT Enable USRP Auto Tx/Rx switching for analog flow graph! #self.subdev.set_enable(False) # Baseband Channel Filter using FM Carson's Rule chan_bw = 2 * (self.rx_base_band_bw + self.rx_freq_deviation ) #Carson's Rule chan_filt_coeffs_float = optfir.low_pass( 1, #gain self.gr_rate1, #sampling rate chan_bw, #passband cutoff chan_bw * 1.35, #stopband cutoff 0.1, #passband ripple 60) #stopband attenuation chan_filt_coeffs_fixed = ( 0.000457763671875, 0.000946044921875, 0.00067138671875, 0.001068115234375, 0.00091552734375, 0.0008544921875, 0.000518798828125, 0.0001220703125, -0.000396728515625, -0.0008544921875, -0.00128173828125, -0.00146484375, -0.001434326171875, -0.0010986328125, -0.000518798828125, 0.000274658203125, 0.001129150390625, 0.00189208984375, 0.00238037109375, 0.00250244140625, 0.002166748046875, 0.0013427734375, 0.000152587890625, -0.001220703125, -0.002532958984375, -0.0035400390625, -0.003997802734375, -0.003753662109375, -0.002777099609375, -0.0010986328125, 0.000946044921875, 0.00311279296875, 0.00494384765625, 0.00604248046875, 0.006103515625, 0.005035400390625, 0.00286865234375, -0.0001220703125, -0.00347900390625, -0.006561279296875, -0.008758544921875, -0.00958251953125, -0.008636474609375, -0.005950927734375, -0.001739501953125, 0.00335693359375, 0.00848388671875, 0.0126953125, 0.01507568359375, 0.014862060546875, 0.01171875, 0.00579833984375, -0.002227783203125, -0.01123046875, -0.0196533203125, -0.02587890625, -0.028228759765625, -0.025421142578125, -0.016754150390625, -0.002166748046875, 0.017608642578125, 0.041015625, 0.0660400390625, 0.090240478515625, 0.111083984375, 0.12640380859375, 0.134490966796875, 0.134490966796875, 0.12640380859375, 0.111083984375, 0.090240478515625, 0.0660400390625, 0.041015625, 0.017608642578125, -0.002166748046875, -0.016754150390625, -0.025421142578125, -0.028228759765625, -0.02587890625, -0.0196533203125, -0.01123046875, -0.002227783203125, 0.00579833984375, 0.01171875, 0.014862060546875, 0.01507568359375, 0.0126953125, 0.00848388671875, 0.00335693359375, -0.001739501953125, -0.005950927734375, -0.008636474609375, -0.00958251953125, -0.008758544921875, -0.006561279296875, -0.00347900390625, -0.0001220703125, 0.00286865234375, 0.005035400390625, 0.006103515625, 0.00604248046875, 0.00494384765625, 0.00311279296875, 0.000946044921875, -0.0010986328125, -0.002777099609375, -0.003753662109375, -0.003997802734375, -0.0035400390625, -0.002532958984375, -0.001220703125, 0.000152587890625, 0.0013427734375, 0.002166748046875, 0.00250244140625, 0.00238037109375, 0.00189208984375, 0.001129150390625, 0.000274658203125, -0.000518798828125, -0.0010986328125, -0.001434326171875, -0.00146484375, -0.00128173828125, -0.0008544921875, -0.000396728515625, 0.0001220703125, 0.000518798828125, 0.0008544921875, 0.00091552734375, 0.001068115234375, 0.00067138671875, 0.000946044921875, 0.000457763671875) #r = gr.enable_realtime_scheduling () self.chan_filt = dsp.fir_ccf_fm_demod_decim(chan_filt_coeffs_fixed, 14, gr_decim, 0, 0, 0, 0) print "Tap length of chan FIR: ", len(chan_filt_coeffs_fixed) # Set the software LNA gain on the output of the USRP gain = self.rx_gain self.rx_gain = max(0.0, min(gain, 1e7)) if self.DEBUG: print " Rx Path initial software signal gain: %f (max 1e7)" % ( gain) print " Rx Path actual software signal gain : %f (max 1e7)" % ( self.rx_gain) #FM Demodulator fm_demod_gain = self.audio_rate / (2 * math.pi * self.rx_freq_deviation) self.fm_demod = gr.quadrature_demod_cf(fm_demod_gain) #Compute FIR filter taps for audio filter width_of_transition_band = self.rx_base_band_bw * 0.35 audio_coeffs = gr.firdes.low_pass( 1.0, #gain self.gr_rate2, #sampling rate self.rx_base_band_bw, width_of_transition_band, gr.firdes.WIN_HAMMING) self.audio_filter = gr.fir_filter_fff(1, audio_coeffs) passband_cutoff = self.rx_base_band_bw stopband_cutoff = passband_cutoff * 1.35 self.deemph = fm_deemph(self.audio_rate) if self.DEBUG: print "Length Audio FIR ", len(audio_coeffs) # Audio sink audio_sink = audio.sink(int(self.audio_rate), "default") # "", #Audio output pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp # False) # ok_to_block if self.DEBUG: print "Before Connecting Blocks" # Wiring Up #WITH CHANNEL FILTER #self.connect (self.u, self.chan_filt, self.lna, self.fm_demod, self.audio_filter, interpolator, self.deemph, self.volume_control, audio_sink) #self.connect (self.u, self.fm_demod, self.audio_filter, self.deemph, self.volume_control, howto_rx, audio_sink) self.connect(self.u, self.chan_filt, self.audio_filter, self.deemph, audio_sink)
def __init__(self, demod_rate, audio_decimation): """ 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) """ gr.hier_block2.__init__( self, "wfm_rcv_pll", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(2, 2, gr.sizeof_float), ) # Output signature bandwidth = 250e3 audio_rate = demod_rate / audio_decimation # We assign to self so that outsiders can grab the demodulator # if they need to. E.g., to plot its output. # # input: complex; output: float loop_bw = 2 * math.pi / 100.0 max_freq = 2.0 * math.pi * 90e3 / demod_rate self.fm_demod = gr.pll_freqdet_cf(loop_bw, max_freq, -max_freq) # input: float; output: float self.deemph_Left = fm_deemph(audio_rate) self.deemph_Right = fm_deemph(audio_rate) # compute FIR filter taps for audio filter width_of_transition_band = audio_rate / 32 audio_coeffs = gr.firdes.low_pass( 1.0, demod_rate, 15000, width_of_transition_band, gr.firdes.WIN_HAMMING # gain # sampling rate ) # input: float; output: float self.audio_filter = gr.fir_filter_fff(audio_decimation, audio_coeffs) if 1: # Pick off the stereo carrier/2 with this filter. It attenuated 10 dB so apply 10 dB gain # We pick off the negative frequency half because we want to base band by it! ## NOTE THIS WAS HACKED TO OFFSET INSERTION LOSS DUE TO DEEMPHASIS stereo_carrier_filter_coeffs = gr.firdes.complex_band_pass( 10.0, demod_rate, -19020, -18980, width_of_transition_band, gr.firdes.WIN_HAMMING ) # print "len stereo carrier filter = ",len(stereo_carrier_filter_coeffs) # print "stereo carrier filter ", stereo_carrier_filter_coeffs # print "width of transition band = ",width_of_transition_band, " audio rate = ", audio_rate # Pick off the double side band suppressed carrier Left-Right audio. It is attenuated 10 dB so apply 10 dB gain stereo_dsbsc_filter_coeffs = gr.firdes.complex_band_pass( 20.0, demod_rate, 38000 - 15000 / 2, 38000 + 15000 / 2, width_of_transition_band, gr.firdes.WIN_HAMMING ) # print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs) # print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs # construct overlap add filter system from coefficients for stereo carrier self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs) # carrier is twice the picked off carrier so arrange to do a commplex multiply self.stereo_carrier_generator = gr.multiply_cc() # Pick off the rds signal stereo_rds_filter_coeffs = gr.firdes.complex_band_pass( 30.0, demod_rate, 57000 - 1500, 57000 + 1500, width_of_transition_band, gr.firdes.WIN_HAMMING ) # print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs) # print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs # construct overlap add filter system from coefficients for stereo carrier self.rds_signal_filter = gr.fir_filter_fcc(audio_decimation, stereo_rds_filter_coeffs) self.rds_carrier_generator = gr.multiply_cc() self.rds_signal_generator = gr.multiply_cc() self_rds_signal_processor = gr.null_sink(gr.sizeof_gr_complex) loop_bw = 2 * math.pi / 100.0 max_freq = -2.0 * math.pi * 18990 / audio_rate min_freq = -2.0 * math.pi * 19010 / audio_rate self.stereo_carrier_pll_recovery = gr.pll_refout_cc(loop_bw, max_freq, min_freq) # self.stereo_carrier_pll_recovery.squelch_enable(False) #pll_refout does not have squelch yet, so disabled for now # set up mixer (multiplier) to get the L-R signal at baseband self.stereo_basebander = gr.multiply_cc() # pick off the real component of the basebanded L-R signal. The imaginary SHOULD be zero self.LmR_real = gr.complex_to_real() self.Make_Left = gr.add_ff() self.Make_Right = gr.sub_ff() self.stereo_dsbsc_filter = gr.fir_filter_fcc(audio_decimation, stereo_dsbsc_filter_coeffs) if 1: # send the real signal to complex filter to pick off the carrier and then to one side of a multiplier self.connect( self, self.fm_demod, self.stereo_carrier_filter, self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator, 0), ) # send the already filtered carrier to the otherside of the carrier self.connect(self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator, 1)) # the resulting signal from this multiplier is the carrier with correct phase but at -38000 Hz. # send the new carrier to one side of the mixer (multiplier) self.connect(self.stereo_carrier_generator, (self.stereo_basebander, 0)) # send the demphasized audio to the DSBSC pick off filter, the complex # DSBSC signal at +38000 Hz is sent to the other side of the mixer/multiplier self.connect(self.fm_demod, self.stereo_dsbsc_filter, (self.stereo_basebander, 1)) # the result is BASEBANDED DSBSC with phase zero! # Pick off the real part since the imaginary is theoretically zero and then to one side of a summer self.connect(self.stereo_basebander, self.LmR_real, (self.Make_Left, 0)) # take the same real part of the DSBSC baseband signal and send it to negative side of a subtracter self.connect(self.LmR_real, (self.Make_Right, 1)) # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone self.connect(self.stereo_basebander, (self.rds_carrier_generator, 0)) self.connect(self.stereo_carrier_pll_recovery, (self.rds_carrier_generator, 1)) # take signal, filter off rds, send into mixer 0 channel self.connect(self.fm_demod, self.rds_signal_filter, (self.rds_signal_generator, 0)) # take rds_carrier_generator output and send into mixer 1 channel self.connect(self.rds_carrier_generator, (self.rds_signal_generator, 1)) # send basebanded rds signal and send into "processor" which for now is a null sink self.connect(self.rds_signal_generator, self_rds_signal_processor) if 1: # pick off the audio, L+R that is what we used to have and send it to the summer self.connect(self.fm_demod, self.audio_filter, (self.Make_Left, 1)) # take the picked off L+R audio and send it to the PLUS side of the subtractor self.connect(self.audio_filter, (self.Make_Right, 0)) # The result of Make_Left gets (L+R) + (L-R) and results in 2*L # The result of Make_Right gets (L+R) - (L-R) and results in 2*R self.connect(self.Make_Left, self.deemph_Left, (self, 0)) self.connect(self.Make_Right, self.deemph_Right, (self, 1))
def __init__(self, audio_rate, quad_rate, tau=75e-6, max_dev=5e3): """ Narrow Band FM Receiver. Takes a single complex baseband input stream and produces a single float output stream of audio sample in the range [-1, +1]. @param audio_rate: sample rate of audio stream, >= 16k @type audio_rate: integer @param quad_rate: sample rate of output stream @type quad_rate: integer @param tau: preemphasis time constant (default 75e-6) @type tau: float @param max_dev: maximum deviation in Hz (default 5e3) @type max_dev: float quad_rate must be an integer multiple of audio_rate. Exported sub-blocks (attributes): squelch quad_demod deemph audio_filter """ gr.hier_block2.__init__( self, "nbfm_rx", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature # FIXME audio_rate and quad_rate ought to be exact rationals audio_rate = int(audio_rate) quad_rate = int(quad_rate) if quad_rate % audio_rate != 0: raise ValueError, "quad_rate is not an integer multiple of audio_rate" squelch_threshold = 20 # dB #self.squelch = gr.simple_squelch_cc(squelch_threshold, 0.001) # FM Demodulator input: complex; output: float k = quad_rate / (2 * math.pi * max_dev) self.quad_demod = gr.quadrature_demod_cf(k) # FM Deemphasis IIR filter self.deemph = fm_deemph(quad_rate, tau=tau) # compute FIR taps for audio filter audio_decim = quad_rate // audio_rate audio_taps = gr.firdes.low_pass( 1.0, # gain quad_rate, # sampling rate 2.7e3, # Audio LPF cutoff 0.5e3, # Transition band gr.firdes.WIN_HAMMING) # filter type print "len(audio_taps) =", len(audio_taps) # Decimating audio filter # input: float; output: float; taps: float self.audio_filter = gr.fir_filter_fff(audio_decim, audio_taps) self.connect(self, self.quad_demod, self.deemph, self.audio_filter, self)
def __init__(self,frame,panel,vbox,argv): stdgui2.std_top_block.__init__ (self,frame,panel,vbox,argv) parser=OptionParser(option_class=eng_option) parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=None, help="select USRP Rx side A or B (default=A)") parser.add_option("-f", "--freq", type="eng_float", default=100.1e6, help="set frequency to FREQ", metavar="FREQ") parser.add_option("-g", "--gain", type="eng_float", default=40, help="set gain in dB (default is midpoint)") parser.add_option("-V", "--volume", type="eng_float", default=None, help="set volume (default is midpoint)") parser.add_option("-O", "--audio-output", type="string", default="", help="pcm device name. E.g., hw:0,0 or surround51 or /dev/dsp") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.frame = frame self.panel = panel self.vol = 0 self.state = "FREQ" self.freq = 0 # build graph self.u = usrp.source_c() # usrp is data source adc_rate = self.u.adc_rate() # 64 MS/s usrp_decim = 200 self.u.set_decim_rate(usrp_decim) usrp_rate = adc_rate / usrp_decim # 320 kS/s chanfilt_decim = 1 demod_rate = usrp_rate / chanfilt_decim sca_chanfilt_decim = 5 sca_demod_rate = demod_rate / sca_chanfilt_decim #64 kHz audio_decimation = 2 audio_rate = sca_demod_rate / audio_decimation # 32 kHz if options.rx_subdev_spec is None: options.rx_subdev_spec = pick_subdevice(self.u) self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) print "Using RX d'board %s" % (self.subdev.side_and_name(),) #Create filter to get main FM Channel we want chan_filt_coeffs = optfir.low_pass (1, # gain usrp_rate, # sampling rate 100e3, # passband cutoff 140e3, # stopband cutoff 0.1, # passband ripple 60) # stopband attenuation #print len(chan_filt_coeffs) chan_filt = gr.fir_filter_ccf (chanfilt_decim, chan_filt_coeffs) #Create demodulator block for Main FM Channel max_dev = 75e3 fm_demod_gain = demod_rate/(2*math.pi*max_dev) self.fm_demod = gr.quadrature_demod_cf (fm_demod_gain) # Note - deemphasis is not applied to the Main FM Channel as main audio is not decoded # SCA Devation is 10% of carrier but some references say 20% if mono with one SCA (6 KHz seems typical) max_sca_dev = 6e3 # Create filter to get SCA channel we want sca_chan_coeffs = gr.firdes.low_pass (1.0, # gain demod_rate, # sampling rate max_sca_dev, # low pass cutoff freq max_sca_dev/3, # width of trans. band gr.firdes.WIN_HANN) # filter type self.ddc = gr.freq_xlating_fir_filter_fcf(sca_chanfilt_decim, # decimation rate sca_chan_coeffs, # taps 0, # frequency translation amount (Gets set by the UI) demod_rate) # input sample rate #Create demodulator block for SCA Channel sca_demod_gain = sca_demod_rate/(2*math.pi*max_sca_dev) self.fm_demod_sca = gr.quadrature_demod_cf (sca_demod_gain) # SCA analog audio is bandwidth limited to 5 KHz max_sca_audio_freq = 5.0e3 # SCA analog deephasis is 150 uS (75 uS may be used) sca_tau = 150e-6 # compute FIR filter taps for SCA audio filter audio_coeffs = gr.firdes.low_pass (1.0, # gain sca_demod_rate, # sampling rate max_sca_audio_freq, # low pass cutoff freq max_sca_audio_freq/2.5, # width of trans. band gr.firdes.WIN_HAMMING) # input: float; output: float self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs) # Create deemphasis block that is applied after SCA demodulation self.deemph = fm_deemph (audio_rate, sca_tau) self.volume_control = gr.multiply_const_ff(self.vol) # sound card as final sink audio_sink = audio.sink (int (audio_rate), options.audio_output, False) # ok_to_block # now wire it all together self.connect (self.u, chan_filt, self.fm_demod, self.ddc, self.fm_demod_sca) self.connect (self.fm_demod_sca, self.audio_filter, self.deemph, self.volume_control, audio_sink) self._build_gui(vbox, usrp_rate, demod_rate, sca_demod_rate, audio_rate) if options.gain is None: # if no gain was specified, use the mid-point in dB g = self.subdev.gain_range() options.gain = float(g[0]+g[1])/2 if options.volume is None: g = self.volume_range() options.volume = float(g[0]+g[1])/2 if abs(options.freq) < 1e6: options.freq *= 1e6 # set initial values self.set_gain(options.gain) self.set_vol(options.volume) if not(self.set_freq(options.freq)): self._set_status_msg("Failed to set initial frequency") self.set_sca_freq(67000) # A common SCA Frequency