def __init__(self, fft_length): gr.hier_block2.__init__(self, "timing_metric", gr.io_signature(1,1,gr.sizeof_gr_complex), gr.io_signature(1,1,gr.sizeof_float)) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self,self.input) # P(d) nominator = schmidl_nominator(fft_length) # R(d) denominator = schmidl_denominator(fft_length) # |P(d)| ** 2 / (R(d)) ** 2 p_mag_sqrd = gr.complex_to_mag_squared() r_sqrd = gr.multiply_ff() self.timing_metric = gr.divide_ff() self.connect(self.input, nominator, p_mag_sqrd, (self.timing_metric,0)) self.connect(self.input, denominator, (r_sqrd,0)) self.connect(denominator, (r_sqrd,1)) self.connect(r_sqrd, (self.timing_metric,1)) self.connect(self.timing_metric, self) # calculate epsilon from P(d), epsilon is normalized fractional frequency offset #angle = gr.complex_to_arg() #self.epsilon = gr.multiply_const_ff(1.0/math.pi) #self.connect(nominator, angle, self.epsilon) try: gr.hier_block.update_var_names(self, "schmidl", vars()) gr.hier_block.update_var_names(self, "schmidl", vars(self)) except: pass
def __init__(self): gr.top_block.__init__(self) parser = OptionParser(option_class=eng_option) parser.add_option("-I", "--audio-input", type="string", default="", help="pcm input device name. E.g., hw:0,0 or /dev/dsp") parser.add_option("-O", "--audio-output", type="string", default="", help="pcm output device name. E.g., hw:0,0 or /dev/dsp") parser.add_option("-r", "--sample-rate", type="eng_float", default=8000, help="set sample rate to RATE (8000)") (options, args) = parser.parse_args () if len(args) != 0: parser.print_help() raise SystemExit, 1 sample_rate = int(options.sample_rate) src = audio.source (sample_rate, options.audio_input) dst = audio.sink (sample_rate, options.audio_output) vec1 = [1, -1] vsource = gr.vector_source_f(vec1, True) multiply = gr.multiply_ff() self.connect(src, (multiply, 0)) self.connect(vsource, (multiply, 1)) self.connect(multiply, dst)
def __init__(self): gr.top_block.__init__(self) parser = OptionParser(option_class=eng_option) parser.add_option("-O", "--audio-output", type="string", default="", help="pcm output device name") parser.add_option("-r", "--sample-rate", type="eng_float", default=192000, help="set sample rate to RATE (192000)") parser.add_option("-f", "--frequency", type="eng_float", default=1000) parser.add_option("-a", "--amplitude", type="eng_float", default=0.5) (options, args) = parser.parse_args () if len(args) != 0: parser.print_help() raise SystemExit, 1 sample_rate = int(options.sample_rate) ampl = float(options.amplitude) pulse_freq = 1.0 # 1Hz pulse_dc = 0.1 # Low DC value to give high/low value rather than on/off if ampl > 1.0: ampl = 1.0 osc = gr.sig_source_f (sample_rate, gr.GR_SIN_WAVE, options.frequency, ampl) pulse = gr.sig_source_f (sample_rate, gr.GR_SQR_WAVE, pulse_freq, .8, pulse_dc) mixer = gr.multiply_ff () self.connect (osc, (mixer, 0)) self.connect (pulse, (mixer, 1)) dst = audio.sink (sample_rate, options.audio_output, True) self.connect (mixer, dst)
def __init__(self, fft_length, pn_weights): gr.hier_block2.__init__(self, "modified_timing_metric", gr.io_signature(1,1,gr.sizeof_gr_complex), gr.io_signature(1,1,gr.sizeof_float)) assert(len(pn_weights) == fft_length) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self,self.input) # P(d) = sum(0 to L-1, conj(delayed(r)) * r) conj = gr.conjugate_cc() mixer = gr.multiply_cc() nominator = gr.fir_filter_ccf(1,[pn_weights[fft_length-i-1]*pn_weights[fft_length/2-i-1] for i in range(fft_length/2)]) self.connect(self.input, delay(gr.sizeof_gr_complex,fft_length/2), conj, (mixer,0)) self.connect(self.input, (mixer,1)) self.connect(mixer, nominator) # moving_avg = P(d) # R(d) denominator = schmidl_denominator(fft_length) # |P(d)| ** 2 / (R(d)) ** 2 p_mag_sqrd = gr.complex_to_mag_squared() r_sqrd = gr.multiply_ff() self.timing_metric = gr.divide_ff() self.connect(nominator, p_mag_sqrd, (self.timing_metric,0)) self.connect(self.input, denominator, (r_sqrd,0)) self.connect(denominator, (r_sqrd,1)) self.connect(r_sqrd, (self.timing_metric,1)) self.connect(self.timing_metric, self)
def test_mult_ff (self): src1_data = (1, 2, 3, 4, 5) src2_data = (8, -3, 4, 8, 2) expected_result = (8, -6, 12, 32, 10) op = gr.multiply_ff () self.help_ff ((src1_data, src2_data), expected_result, op)
def test_mult_ff (self): # Note: the GNUHawk multiply_ff only outputs data in multiples of 8, # so this test has been modified accordingly src1_data = (1, 2, 3, 4, 5, 6, 7, 8) src2_data = (8, -3, 4, 8, 2, -0.5, -1, 1.5) expected_result = (8, -6, 12, 32, 10, -3, -7, 12) op = gr.multiply_ff () self.help_ff ((src1_data, src2_data), expected_result, op)
def __init__(self, audio_rate): gr.hier_block2.__init__(self, "standard_squelch", gr.io_signature(1, 1, gr.sizeof_float), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature self.input_node = gr.add_const_ff(0) # FIXME kludge self.low_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.9524,-0.9615)) self.low_square = gr.multiply_ff() self.low_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) # 100ms time constant self.hi_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.3597,-0.9615)) self.hi_square = gr.multiply_ff() self.hi_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) self.sub = gr.sub_ff(); self.add = gr.add_ff(); self.gate = gr.threshold_ff(0.3,0.43,0) self.squelch_lpf = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) self.div = gr.divide_ff() self.squelch_mult = gr.multiply_ff() self.connect (self, self.input_node) self.connect (self.input_node, (self.squelch_mult, 0)) self.connect (self.input_node,self.low_iir) self.connect (self.low_iir,(self.low_square,0)) self.connect (self.low_iir,(self.low_square,1)) self.connect (self.low_square,self.low_smooth,(self.sub,0)) self.connect (self.low_smooth, (self.add,0)) self.connect (self.input_node,self.hi_iir) self.connect (self.hi_iir,(self.hi_square,0)) self.connect (self.hi_iir,(self.hi_square,1)) self.connect (self.hi_square,self.hi_smooth,(self.sub,1)) self.connect (self.hi_smooth, (self.add,1)) self.connect (self.sub, (self.div, 0)) self.connect (self.add, (self.div, 1)) self.connect (self.div, self.gate, self.squelch_lpf, (self.squelch_mult,1)) self.connect (self.squelch_mult, self)
def __init__(self): gr.top_block.__init__(self, "FSK Demod Demo") # Variables self.symbol_rate = symbol_rate = 125e3 self.samp_rate = samp_rate = symbol_rate self.f_center = f_center = 868e6 self.sps = sps = 2 self.sensitivity = sensitivity = (pi / 2) / sps self.alpha = alpha = 0.0512/sps self.bandwidth = bandwidth = 100e3 # Blocks self.uhd_usrp_source_0 = uhd.usrp_source( device_addr="", stream_args=uhd.stream_args( cpu_format="fc32", channels=range(1), ), ) self.uhd_usrp_source_0.set_samp_rate(samp_rate) self.uhd_usrp_source_0.set_center_freq(f_center, 0) self.uhd_usrp_source_0.set_gain(0, 0) self.uhd_usrp_source_0.set_bandwidth(bandwidth, 0) self.fm_demod = gr.quadrature_demod_cf(1 / sensitivity) self.freq_offset = gr.single_pole_iir_filter_ff(alpha) self.sub = gr.sub_ff() self.add = gr.add_ff() self.multiply = gr.multiply_ff() self.invert = gr.multiply_const_vff((-1, )) # recover the clock omega = sps gain_mu = 0.03 mu = 0.5 omega_relative_limit = 0.0002 freq_error = 0.0 gain_omega = .25 * gain_mu * gain_mu # critically damped self.clock_recovery = digital.clock_recovery_mm_ff(omega, gain_omega, mu, gain_mu, omega_relative_limit) self.slice = digital.binary_slicer_fb() self.sink = gr.vector_sink_b(1) self.file_sink = gr.file_sink(gr.sizeof_char, 'fsk_dump.log') # Connections self.connect(self.fm_demod, (self.add, 0)) self.connect(self.fm_demod, self.freq_offset, (self.add, 1)) self.connect(self.uhd_usrp_source_0, self.fm_demod) self.connect(self.add, self.clock_recovery, self.invert, self.slice, self.file_sink) self.connect(self.slice, self.sink)
def __init__(self, fg, audio_rate): self.input_node = gr.add_const_ff(0) # FIXME kludge self.low_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.9524,-0.9615)) self.low_square = gr.multiply_ff() self.low_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) # 100ms time constant self.hi_iir = gr.iir_filter_ffd((0.0193,0,-0.0193),(1,1.3597,-0.9615)) self.hi_square = gr.multiply_ff() self.hi_smooth = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) self.sub = gr.sub_ff(); self.add = gr.add_ff(); self.gate = gr.threshold_ff(0.3,0.43,0) self.squelch_lpf = gr.single_pole_iir_filter_ff(1/(0.01*audio_rate)) self.div = gr.divide_ff() self.squelch_mult = gr.multiply_ff() fg.connect (self.input_node, (self.squelch_mult, 0)) fg.connect (self.input_node,self.low_iir) fg.connect (self.low_iir,(self.low_square,0)) fg.connect (self.low_iir,(self.low_square,1)) fg.connect (self.low_square,self.low_smooth,(self.sub,0)) fg.connect (self.low_smooth, (self.add,0)) fg.connect (self.input_node,self.hi_iir) fg.connect (self.hi_iir,(self.hi_square,0)) fg.connect (self.hi_iir,(self.hi_square,1)) fg.connect (self.hi_square,self.hi_smooth,(self.sub,1)) fg.connect (self.hi_smooth, (self.add,1)) fg.connect (self.sub, (self.div, 0)) fg.connect (self.add, (self.div, 1)) fg.connect (self.div, self.gate, self.squelch_lpf, (self.squelch_mult,1)) gr.hier_block.__init__(self, fg, self.input_node, self.squelch_mult)
def __init__(self,vlen): gr.hier_block2.__init__(self,"snr_estimator", gr.io_signature(2,2,gr.sizeof_gr_complex*vlen), gr.io_signature(1,1,gr.sizeof_float)) reference = gr.kludge_copy(gr.sizeof_gr_complex*vlen) received = gr.kludge_copy(gr.sizeof_gr_complex*vlen) self.connect((self,0),reference) self.connect((self,1),received) received_conjugated = gr.conjugate_cc(vlen) self.connect(received,received_conjugated) R_innerproduct = gr.multiply_vcc(vlen) self.connect(reference,R_innerproduct) self.connect(received_conjugated,(R_innerproduct,1)) R_sum = vector_sum_vcc(vlen) self.connect(R_innerproduct,R_sum) R = gr.complex_to_mag_squared() self.connect(R_sum,R) received_magsqrd = gr.complex_to_mag_squared(vlen) reference_magsqrd = gr.complex_to_mag_squared(vlen) self.connect(received,received_magsqrd) self.connect(reference,reference_magsqrd) received_sum = vector_sum_vff(vlen) reference_sum = vector_sum_vff(vlen) self.connect(received_magsqrd,received_sum) self.connect(reference_magsqrd,reference_sum) P = gr.multiply_ff() self.connect(received_sum,(P,0)) self.connect(reference_sum,(P,1)) denominator = gr.sub_ff() self.connect(P,denominator) self.connect(R,(denominator,1)) rho_hat = gr.divide_ff() self.connect(R,rho_hat) self.connect(denominator,(rho_hat,1)) self.connect(rho_hat,self)
def __init__(self, fg, lo_freq, usrp_rate): cl = dcf77.clock(07, # hour 35-2, # min 6, # day in month 7, # day of week 2, # month 8, # year 0) # loop tx = dcf77.mod(usrp_rate) f2c = gr.float_to_complex() mixer = gr.multiply_ff() fg.connect (cl, tx, f2c) gr.hier_block.__init__(self, fg, cl, f2c)
def __init__(self, fg): self.split = gr.multiply_const_ff(1) self.sqr = gr.multiply_ff() self.int0 = gr.iir_filter_ffd([0.004, 0], [0, 0.999]) self.offs = gr.add_const_ff(-30) self.gain = gr.multiply_const_ff(70) self.log = gr.nlog10_ff(10, 1) self.agc = gr.divide_ff() fg.connect(self.split, (self.agc, 0)) fg.connect(self.split, (self.sqr, 0)) fg.connect(self.split, (self.sqr, 1)) fg.connect(self.sqr, self.int0) fg.connect(self.int0, self.log) fg.connect(self.log, self.offs) fg.connect(self.offs, self.gain) fg.connect(self.gain, (self.agc, 1)) gr.hier_block.__init__(self, fg, self.split, self.agc)
def __init__(self): gr.hier_block2.__init__(self,"BER Estimator", gr.io_signature(1,1,gr.sizeof_float), gr.io_signature(1,1,gr.sizeof_float)) #TODO Implement a polynomial block in C++ and approximate with polynomials #of arbitrary order self.add = gr.add_const_vff((-1, )) self.square = gr.multiply_ff() self.mult_lin = gr.multiply_const_ff(-0.20473967) self.mult_sq = gr.multiply_const_ff(1.5228658) self.sum = gr.add_ff() self.connect(self,self.add) self.connect(self.add,(self.square,0)) self.connect(self.add,(self.square,1)) self.connect(self.square,self.mult_sq,(self.sum,0)) self.connect(self.add,self.mult_lin,(self.sum,1)) self.connect(self.sum,self)
def __init__ ( self, fft_length ): gr.hier_block2.__init__(self, "recursive_timing_metric", gr.io_signature(1,1,gr.sizeof_gr_complex), gr.io_signature(1,1,gr.sizeof_float)) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) # P(d) = sum(0 to L-1, conj(delayed(r)) * r) conj = gr.conjugate_cc() mixer = gr.multiply_cc() mix_delay = delay(gr.sizeof_gr_complex,fft_length/2+1) mix_diff = gr.sub_cc() nominator = accumulator_cc() inpdelay = delay(gr.sizeof_gr_complex,fft_length/2) self.connect(self.input, inpdelay, conj, (mixer,0)) self.connect(self.input, (mixer,1)) self.connect(mixer,(mix_diff,0)) self.connect(mixer, mix_delay, (mix_diff,1)) self.connect(mix_diff,nominator) rmagsqrd = gr.complex_to_mag_squared() rm_delay = delay(gr.sizeof_float,fft_length+1) rm_diff = gr.sub_ff() denom = accumulator_ff() self.connect(self.input,rmagsqrd,rm_diff,gr.multiply_const_ff(0.5),denom) self.connect(rmagsqrd,rm_delay,(rm_diff,1)) ps = gr.complex_to_mag_squared() rs = gr.multiply_ff() self.connect(nominator,ps) self.connect(denom,rs) self.connect(denom,(rs,1)) div = gr.divide_ff() self.connect(ps,div) self.connect(rs,(div,1)) self.connect(div,self)
def __init__(self): """ Hierarchical block for FSK demodulation. The input is the complex modulated signal at baseband and the output is a stream of floats. """ # Initialize base class gr.hier_block2.__init__( self, "fsk_demod", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_float) ) # Variables self.sps = sps = 2 self.sensitivity = sensitivity = (pi / 2) / sps self.alpha = alpha = 0.0512 / sps self.fm_demod = gr.quadrature_demod_cf(1 / sensitivity) self.freq_offset = gr.single_pole_iir_filter_ff(alpha) self.sub = gr.sub_ff() self.add = gr.add_ff() self.multiply = gr.multiply_ff() self.invert = gr.multiply_const_vff((-1,)) # recover the clock omega = sps gain_mu = 0.03 mu = 0.5 omega_relative_limit = 0.0002 freq_error = 0.0 gain_omega = 0.25 * gain_mu * gain_mu # critically damped self.clock_recovery = digital.clock_recovery_mm_ff(omega, gain_omega, mu, gain_mu, omega_relative_limit) self.slice = digital.binary_slicer_fb() # Connections self.connect(self.fm_demod, (self.add, 0)) self.connect(self.fm_demod, self.freq_offset, (self.add, 1)) self.connect(self, self.fm_demod) self.connect(self.add, self.clock_recovery, self.invert, self)
def __init__( self ): gr.hier_block2.__init__(self, "agc", gr.io_signature(1,1,gr.sizeof_float), gr.io_signature(1,1,gr.sizeof_float)) self.split = gr.multiply_const_ff( 1 ) self.sqr = gr.multiply_ff( ) self.int0 = gr.iir_filter_ffd( [.004, 0], [0, .999] ) self.offs = gr.add_const_ff( -30 ) self.gain = gr.multiply_const_ff( 70 ) self.log = gr.nlog10_ff( 10, 1 ) self.agc = gr.divide_ff( ) self.connect(self, self.split) self.connect(self.split, (self.agc, 0)) self.connect(self.split, (self.sqr, 0)) self.connect(self.split, (self.sqr, 1)) self.connect(self.sqr, self.int0) self.connect(self.int0, self.log) self.connect(self.log, self.offs) self.connect(self.offs, self.gain) self.connect(self.gain, (self.agc, 1)) self.connect(self.agc, self)
def __init__(self): gr.hier_block2.__init__(self, "agc", gr.io_signature(1, 1, gr.sizeof_float), gr.io_signature(1, 1, gr.sizeof_float)) self.split = gr.multiply_const_ff(1) self.sqr = gr.multiply_ff() self.int0 = gr.iir_filter_ffd([.004, 0], [0, .999]) self.offs = gr.add_const_ff(-30) self.gain = gr.multiply_const_ff(70) self.log = gr.nlog10_ff(10, 1) self.agc = gr.divide_ff() self.connect(self, self.split) self.connect(self.split, (self.agc, 0)) self.connect(self.split, (self.sqr, 0)) self.connect(self.split, (self.sqr, 1)) self.connect(self.sqr, self.int0) self.connect(self.int0, self.log) self.connect(self.log, self.offs) self.connect(self.offs, self.gain) self.connect(self.gain, (self.agc, 1)) self.connect(self.agc, self)
def __init__(self, fft_length, pn_weights): gr.hier_block2.__init__(self, "modified_timing_metric", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_float)) assert (len(pn_weights) == fft_length) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) # P(d) = sum(0 to L-1, conj(delayed(r)) * r) conj = gr.conjugate_cc() mixer = gr.multiply_cc() nominator = gr.fir_filter_ccf(1, [ pn_weights[fft_length - i - 1] * pn_weights[fft_length / 2 - i - 1] for i in range(fft_length / 2) ]) self.connect(self.input, delay(gr.sizeof_gr_complex, fft_length / 2), conj, (mixer, 0)) self.connect(self.input, (mixer, 1)) self.connect(mixer, nominator) # moving_avg = P(d) # R(d) denominator = schmidl_denominator(fft_length) # |P(d)| ** 2 / (R(d)) ** 2 p_mag_sqrd = gr.complex_to_mag_squared() r_sqrd = gr.multiply_ff() self.timing_metric = gr.divide_ff() self.connect(nominator, p_mag_sqrd, (self.timing_metric, 0)) self.connect(self.input, denominator, (r_sqrd, 0)) self.connect(denominator, (r_sqrd, 1)) self.connect(r_sqrd, (self.timing_metric, 1)) self.connect(self.timing_metric, self)
def __init__(self): gr.top_block.__init__(self) parser = OptionParser(option_class=eng_option) parser.add_option( "-I", "--audio-input", type="string", default="", help="pcm input device name. E.g., hw:0,0 or /dev/dsp") parser.add_option( "-O", "--audio-output", type="string", default="", help="pcm output device name. E.g., hw:0,0 or /dev/dsp") parser.add_option("-r", "--sample-rate", type="eng_float", default=8000, help="set sample rate to RATE (8000)") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() raise SystemExit, 1 sample_rate = int(options.sample_rate) src = audio.source(sample_rate, options.audio_input) dst = audio.sink(sample_rate, options.audio_output) vec1 = [1, -1] vsource = gr.vector_source_f(vec1, True) multiply = gr.multiply_ff() self.connect(src, (multiply, 0)) self.connect(vsource, (multiply, 1)) self.connect(multiply, dst)
def __init__(self, fft_length): gr.hier_block2.__init__(self, "timing_metric", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_float)) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) # P(d) nominator = schmidl_nominator(fft_length) # R(d) denominator = schmidl_denominator(fft_length) # |P(d)| ** 2 / (R(d)) ** 2 p_mag_sqrd = gr.complex_to_mag_squared() r_sqrd = gr.multiply_ff() self.timing_metric = gr.divide_ff() self.connect(self.input, nominator, p_mag_sqrd, (self.timing_metric, 0)) self.connect(self.input, denominator, (r_sqrd, 0)) self.connect(denominator, (r_sqrd, 1)) self.connect(r_sqrd, (self.timing_metric, 1)) self.connect(self.timing_metric, self) # calculate epsilon from P(d), epsilon is normalized fractional frequency offset #angle = gr.complex_to_arg() #self.epsilon = gr.multiply_const_ff(1.0/math.pi) #self.connect(nominator, angle, self.epsilon) try: gr.hier_block.update_var_names(self, "schmidl", vars()) gr.hier_block.update_var_names(self, "schmidl", vars(self)) except: pass
def __init__(self, *args, **kwds): # begin wxGlade: MyFrame.__init__ kwds["style"] = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) # Menu Bar self.frame_1_menubar = wx.MenuBar() self.SetMenuBar(self.frame_1_menubar) wxglade_tmp_menu = wx.Menu() self.Exit = wx.MenuItem(wxglade_tmp_menu, ID_EXIT, "Exit", "Exit", wx.ITEM_NORMAL) wxglade_tmp_menu.AppendItem(self.Exit) self.frame_1_menubar.Append(wxglade_tmp_menu, "File") # Menu Bar end self.panel_1 = wx.Panel(self, -1) self.button_1 = wx.Button(self, ID_BUTTON_1, "LSB") self.button_2 = wx.Button(self, ID_BUTTON_2, "USB") self.button_3 = wx.Button(self, ID_BUTTON_3, "AM") self.button_4 = wx.Button(self, ID_BUTTON_4, "CW") self.button_5 = wx.ToggleButton(self, ID_BUTTON_5, "Upper") self.slider_1 = wx.Slider(self, ID_SLIDER_1, 0, -15799, 15799, style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.button_6 = wx.ToggleButton(self, ID_BUTTON_6, "Lower") self.slider_2 = wx.Slider(self, ID_SLIDER_2, 0, -15799, 15799, style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.panel_5 = wx.Panel(self, -1) self.label_1 = wx.StaticText(self, -1, " Band\nCenter") self.text_ctrl_1 = wx.TextCtrl(self, ID_TEXT_1, "") self.panel_6 = wx.Panel(self, -1) self.panel_7 = wx.Panel(self, -1) self.panel_2 = wx.Panel(self, -1) self.button_7 = wx.ToggleButton(self, ID_BUTTON_7, "Freq") self.slider_3 = wx.Slider(self, ID_SLIDER_3, 3000, 0, 6000) self.spin_ctrl_1 = wx.SpinCtrl(self, ID_SPIN_1, "", min=0, max=100) self.button_8 = wx.ToggleButton(self, ID_BUTTON_8, "Vol") self.slider_4 = wx.Slider(self, ID_SLIDER_4, 0, 0, 500) self.slider_5 = wx.Slider(self, ID_SLIDER_5, 0, 0, 20) self.button_9 = wx.ToggleButton(self, ID_BUTTON_9, "Time") self.button_11 = wx.Button(self, ID_BUTTON_11, "Rew") self.button_10 = wx.Button(self, ID_BUTTON_10, "Fwd") self.panel_3 = wx.Panel(self, -1) self.label_2 = wx.StaticText(self, -1, "PGA ") self.panel_4 = wx.Panel(self, -1) self.panel_8 = wx.Panel(self, -1) self.panel_9 = wx.Panel(self, -1) self.label_3 = wx.StaticText(self, -1, "AM Sync\nCarrier") self.slider_6 = wx.Slider(self, ID_SLIDER_6, 50, 0, 200, style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.label_4 = wx.StaticText(self, -1, "Antenna Tune") self.slider_7 = wx.Slider(self, ID_SLIDER_7, 1575, 950, 2200, style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.panel_10 = wx.Panel(self, -1) self.button_12 = wx.ToggleButton(self, ID_BUTTON_12, "Auto Tune") self.button_13 = wx.Button(self, ID_BUTTON_13, "Calibrate") self.button_14 = wx.Button(self, ID_BUTTON_14, "Reset") self.panel_11 = wx.Panel(self, -1) self.panel_12 = wx.Panel(self, -1) self.__set_properties() self.__do_layout() # end wxGlade parser = OptionParser(option_class=eng_option) parser.add_option( "-c", "--ddc-freq", type="eng_float", default=3.9e6, help="set Rx DDC frequency to FREQ", metavar="FREQ" ) parser.add_option("-a", "--audio_file", default="", help="audio output file", metavar="FILE") parser.add_option("-r", "--radio_file", default="", help="radio output file", metavar="FILE") parser.add_option("-i", "--input_file", default="", help="radio input file", metavar="FILE") parser.add_option("-d", "--decim", type="int", default=250, help="USRP decimation") parser.add_option( "-R", "--rx-subdev-spec", type="subdev", default=None, help="select USRP Rx side A or B (default=first one with a daughterboard)", ) (options, args) = parser.parse_args() self.usrp_center = options.ddc_freq usb_rate = 64e6 / options.decim self.slider_range = usb_rate * 0.9375 self.f_lo = self.usrp_center - (self.slider_range / 2) self.f_hi = self.usrp_center + (self.slider_range / 2) self.af_sample_rate = 32000 fir_decim = long(usb_rate / self.af_sample_rate) # data point arrays for antenna tuner self.xdata = [] self.ydata = [] self.tb = gr.top_block() # radio variables, initial conditions self.frequency = self.usrp_center # these map the frequency slider (0-6000) to the actual range self.f_slider_offset = self.f_lo self.f_slider_scale = 10000 / options.decim self.spin_ctrl_1.SetRange(self.f_lo, self.f_hi) self.text_ctrl_1.SetValue(str(int(self.usrp_center))) self.slider_5.SetValue(0) self.AM_mode = False self.slider_3.SetValue((self.frequency - self.f_slider_offset) / self.f_slider_scale) self.spin_ctrl_1.SetValue(int(self.frequency)) POWERMATE = True try: self.pm = powermate.powermate(self) except: sys.stderr.write("Unable to find PowerMate or Contour Shuttle\n") POWERMATE = False if POWERMATE: powermate.EVT_POWERMATE_ROTATE(self, self.on_rotate) powermate.EVT_POWERMATE_BUTTON(self, self.on_pmButton) self.active_button = 7 # command line options if options.audio_file == "": SAVE_AUDIO_TO_FILE = False else: SAVE_AUDIO_TO_FILE = True if options.radio_file == "": SAVE_RADIO_TO_FILE = False else: SAVE_RADIO_TO_FILE = True if options.input_file == "": self.PLAY_FROM_USRP = True else: self.PLAY_FROM_USRP = False if self.PLAY_FROM_USRP: self.src = usrp.source_s(decim_rate=options.decim) if options.rx_subdev_spec is None: options.rx_subdev_spec = pick_subdevice(self.src) self.src.set_mux(usrp.determine_rx_mux_value(self.src, options.rx_subdev_spec)) self.subdev = usrp.selected_subdev(self.src, options.rx_subdev_spec) self.src.tune(0, self.subdev, self.usrp_center) self.tune_offset = 0 # -self.usrp_center - self.src.rx_freq(0) else: self.src = gr.file_source(gr.sizeof_short, options.input_file) self.tune_offset = 2200 # 2200 works for 3.5-4Mhz band # save radio data to a file if SAVE_RADIO_TO_FILE: file = gr.file_sink(gr.sizeof_short, options.radio_file) self.tb.connect(self.src, file) # 2nd DDC xlate_taps = gr.firdes.low_pass(1.0, usb_rate, 16e3, 4e3, gr.firdes.WIN_HAMMING) self.xlate = gr.freq_xlating_fir_filter_ccf(fir_decim, xlate_taps, self.tune_offset, usb_rate) # convert rf data in interleaved short int form to complex s2ss = gr.stream_to_streams(gr.sizeof_short, 2) s2f1 = gr.short_to_float() s2f2 = gr.short_to_float() src_f2c = gr.float_to_complex() self.tb.connect(self.src, s2ss) self.tb.connect((s2ss, 0), s2f1) self.tb.connect((s2ss, 1), s2f2) self.tb.connect(s2f1, (src_f2c, 0)) self.tb.connect(s2f2, (src_f2c, 1)) # Complex Audio filter audio_coeffs = gr.firdes.complex_band_pass( 1.0, # gain self.af_sample_rate, # sample rate -3000, # low cutoff 0, # high cutoff 100, # transition gr.firdes.WIN_HAMMING, ) # window self.slider_1.SetValue(0) self.slider_2.SetValue(-3000) self.audio_filter = gr.fir_filter_ccc(1, audio_coeffs) # Main +/- 16Khz spectrum display self.fft = fftsink2.fft_sink_c( self.panel_2, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640, 240) ) # AM Sync carrier if AM_SYNC_DISPLAY: self.fft2 = fftsink.fft_sink_c( self.tb, self.panel_9, y_per_div=20, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640, 240), ) c2f = gr.complex_to_float() # AM branch self.sel_am = gr.multiply_const_cc(0) # the following frequencies turn out to be in radians/sample # gr.pll_refout_cc(alpha,beta,min_freq,max_freq) # suggested alpha = X, beta = .25 * X * X pll = gr.pll_refout_cc( 0.5, 0.0625, (2.0 * math.pi * 7.5e3 / self.af_sample_rate), (2.0 * math.pi * 6.5e3 / self.af_sample_rate) ) self.pll_carrier_scale = gr.multiply_const_cc(complex(10, 0)) am_det = gr.multiply_cc() # these are for converting +7.5kHz to -7.5kHz # for some reason gr.conjugate_cc() adds noise ?? c2f2 = gr.complex_to_float() c2f3 = gr.complex_to_float() f2c = gr.float_to_complex() phaser1 = gr.multiply_const_ff(1) phaser2 = gr.multiply_const_ff(-1) # filter for pll generated carrier pll_carrier_coeffs = gr.firdes.complex_band_pass( 2.0, # gain self.af_sample_rate, # sample rate 7400, # low cutoff 7600, # high cutoff 100, # transition gr.firdes.WIN_HAMMING, ) # window self.pll_carrier_filter = gr.fir_filter_ccc(1, pll_carrier_coeffs) self.sel_sb = gr.multiply_const_ff(1) combine = gr.add_ff() # AGC sqr1 = gr.multiply_ff() intr = gr.iir_filter_ffd([0.004, 0], [0, 0.999]) offset = gr.add_const_ff(1) agc = gr.divide_ff() self.scale = gr.multiply_const_ff(0.00001) dst = audio.sink(long(self.af_sample_rate)) self.tb.connect(src_f2c, self.xlate, self.fft) self.tb.connect(self.xlate, self.audio_filter, self.sel_am, (am_det, 0)) self.tb.connect(self.sel_am, pll, self.pll_carrier_scale, self.pll_carrier_filter, c2f3) self.tb.connect((c2f3, 0), phaser1, (f2c, 0)) self.tb.connect((c2f3, 1), phaser2, (f2c, 1)) self.tb.connect(f2c, (am_det, 1)) self.tb.connect(am_det, c2f2, (combine, 0)) self.tb.connect(self.audio_filter, c2f, self.sel_sb, (combine, 1)) if AM_SYNC_DISPLAY: self.tb.connect(self.pll_carrier_filter, self.fft2) self.tb.connect(combine, self.scale) self.tb.connect(self.scale, (sqr1, 0)) self.tb.connect(self.scale, (sqr1, 1)) self.tb.connect(sqr1, intr, offset, (agc, 1)) self.tb.connect(self.scale, (agc, 0)) self.tb.connect(agc, dst) if SAVE_AUDIO_TO_FILE: f_out = gr.file_sink(gr.sizeof_short, options.audio_file) sc1 = gr.multiply_const_ff(64000) f2s1 = gr.float_to_short() self.tb.connect(agc, sc1, f2s1, f_out) self.tb.start() # for mouse position reporting on fft display em.eventManager.Register(self.Mouse, wx.EVT_MOTION, self.fft.win) # and left click to re-tune em.eventManager.Register(self.Click, wx.EVT_LEFT_DOWN, self.fft.win) # start a timer to check for web commands if WEB_CONTROL: self.timer = UpdateTimer(self, 1000) # every 1000 mSec, 1 Sec wx.EVT_BUTTON(self, ID_BUTTON_1, self.set_lsb) wx.EVT_BUTTON(self, ID_BUTTON_2, self.set_usb) wx.EVT_BUTTON(self, ID_BUTTON_3, self.set_am) wx.EVT_BUTTON(self, ID_BUTTON_4, self.set_cw) wx.EVT_BUTTON(self, ID_BUTTON_10, self.fwd) wx.EVT_BUTTON(self, ID_BUTTON_11, self.rew) wx.EVT_BUTTON(self, ID_BUTTON_13, self.AT_calibrate) wx.EVT_BUTTON(self, ID_BUTTON_14, self.AT_reset) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_5, self.on_button) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_6, self.on_button) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_7, self.on_button) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_8, self.on_button) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_9, self.on_button) wx.EVT_SLIDER(self, ID_SLIDER_1, self.set_filter) wx.EVT_SLIDER(self, ID_SLIDER_2, self.set_filter) wx.EVT_SLIDER(self, ID_SLIDER_3, self.slide_tune) wx.EVT_SLIDER(self, ID_SLIDER_4, self.set_volume) wx.EVT_SLIDER(self, ID_SLIDER_5, self.set_pga) wx.EVT_SLIDER(self, ID_SLIDER_6, self.am_carrier) wx.EVT_SLIDER(self, ID_SLIDER_7, self.antenna_tune) wx.EVT_SPINCTRL(self, ID_SPIN_1, self.spin_tune) wx.EVT_MENU(self, ID_EXIT, self.TimeToQuit)
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("-T", "--tx-subdev-spec", type="subdev", default=None, help="select USRP Tx side A or B") parser.add_option("-f", "--freq", type="eng_float", default=107.2e6, help="set Tx frequency to FREQ [required]", metavar="FREQ") parser.add_option("--wavfile", type="string", default="", help="read input from FILE") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.usrp_interp = 200 self.u = usrp.sink_c(0, self.usrp_interp) print "USRP Serial: ", self.u.serial_number() self.dac_rate = self.u.dac_rate() # 128 MS/s self.usrp_rate = self.dac_rate / self.usrp_interp # 640 kS/s self.sw_interp = 5 self.audio_rate = self.usrp_rate / self.sw_interp # 128 kS/s # determine the daughterboard subdevice we're using if options.tx_subdev_spec is None: options.tx_subdev_spec = usrp.pick_tx_subdevice(self.u) self.u.set_mux( usrp.determine_tx_mux_value(self.u, options.tx_subdev_spec)) self.subdev = usrp.selected_subdev(self.u, options.tx_subdev_spec) print "Using d'board: ", self.subdev.side_and_name() # set max Tx gain, tune frequency and enable transmitter self.subdev.set_gain(self.subdev.gain_range()[1]) if self.u.tune(self.subdev.which(), self.subdev, options.freq): print "Tuned to", options.freq / 1e6, "MHz" else: sys.exit(1) self.subdev.set_enable(True) # open wav file containing floats in the [-1, 1] range, repeat if options.wavfile is None: print "Please provide a wavfile to transmit! Exiting\n" sys.exit(1) self.src = gr.wavfile_source(options.wavfile, True) nchans = self.src.channels() sample_rate = self.src.sample_rate() bits_per_sample = self.src.bits_per_sample() print nchans, "channels,", sample_rate, "kS/s,", bits_per_sample, "bits/sample" # resample to 128kS/s if sample_rate == 44100: self.resample_left = blks2.rational_resampler_fff(32, 11) self.resample_right = blks2.rational_resampler_fff(32, 11) elif sample_rate == 48000: self.resample_left == blks2.rational_resampler_fff(8, 3) self.resample_right == blks2.rational_resampler_fff(8, 3) elif sample_rate == 8000: self.resample_left == blks2.rational_resampler_fff(16, 1) self.resample_right == blks2.rational_resampler_fff(16, 1) else: print sample_rate, "is an unsupported sample rate" sys.exit(1) self.connect((self.src, 0), self.resample_left) self.connect((self.src, 1), self.resample_right) # create L+R (mono) and L-R (stereo) self.audio_lpr = gr.add_ff() self.audio_lmr = gr.sub_ff() self.connect(self.resample_left, (self.audio_lpr, 0)) self.connect(self.resample_left, (self.audio_lmr, 0)) self.connect(self.resample_right, (self.audio_lpr, 1)) self.connect(self.resample_right, (self.audio_lmr, 1)) # low-pass filter for L+R audio_lpr_taps = gr.firdes.low_pass( 0.3, # gain self.audio_rate, # sampling rate 15e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HANN) self.audio_lpr_filter = gr.fir_filter_fff(1, audio_lpr_taps) self.connect(self.audio_lpr, self.audio_lpr_filter) # create pilot tone at 19 kHz self.pilot = gr.sig_source_f( self.audio_rate, # sampling freq gr.GR_SIN_WAVE, # waveform 19e3, # frequency 3e-2) # amplitude # create the L-R signal carrier at 38 kHz, high-pass to remove 0Hz tone self.stereo_carrier = gr.multiply_ff() self.connect(self.pilot, (self.stereo_carrier, 0)) self.connect(self.pilot, (self.stereo_carrier, 1)) stereo_carrier_taps = gr.firdes.high_pass( 1, # gain self.audio_rate, # sampling rate 1e4, # cutoff freq 2e3, # transition width gr.firdes.WIN_HANN) self.stereo_carrier_filter = gr.fir_filter_fff(1, stereo_carrier_taps) self.connect(self.stereo_carrier, self.stereo_carrier_filter) # upconvert L-R to 23-53 kHz and band-pass self.mix_stereo = gr.multiply_ff() audio_lmr_taps = gr.firdes.band_pass( 3e3, # gain self.audio_rate, # sampling rate 23e3, # low cutoff 53e3, # high cuttof 2e3, # transition width gr.firdes.WIN_HANN) self.audio_lmr_filter = gr.fir_filter_fff(1, audio_lmr_taps) self.connect(self.audio_lmr, (self.mix_stereo, 0)) self.connect(self.stereo_carrier_filter, (self.mix_stereo, 1)) self.connect(self.mix_stereo, self.audio_lmr_filter) # mix L+R, pilot and L-R self.mixer = gr.add_ff() self.connect(self.audio_lpr_filter, (self.mixer, 0)) self.connect(self.pilot, (self.mixer, 1)) self.connect(self.audio_lmr_filter, (self.mixer, 2)) # interpolation & pre-emphasis interp_taps = gr.firdes.low_pass( self.sw_interp, # gain self.audio_rate, # Fs 60e3, # cutoff freq 5e3, # transition width gr.firdes.WIN_HAMMING) self.interpolator = gr.interp_fir_filter_fff(self.sw_interp, interp_taps) self.pre_emph = blks2.fm_preemph(self.usrp_rate, tau=50e-6) self.connect(self.mixer, self.interpolator, self.pre_emph) # fm modulation, gain & TX max_dev = 100e3 k = 2 * math.pi * max_dev / self.usrp_rate # modulator sensitivity self.modulator = gr.frequency_modulator_fc(k) self.gain = gr.multiply_const_cc(1e3) self.connect(self.pre_emph, self.modulator, self.gain, self.u) # plot an FFT to verify we are sending what we want pre_mod = fftsink2.fft_sink_f(panel, title="Before Interpolation", fft_size=512, sample_rate=self.audio_rate, y_per_div=20, ref_level=20) self.connect(self.mixer, pre_mod) vbox.Add(pre_mod.win, 1, wx.EXPAND)
def __init__(self, dab_params, rx_params, verbose=False, debug=False): """ Hierarchical block for OFDM demodulation @param dab_params DAB parameter object (dab.parameters.dab_parameters) @param rx_params RX parameter object (dab.parameters.receiver_parameters) @param debug enables debug output to files @param verbose whether to produce verbose messages """ self.dp = dp = dab_params self.rp = rp = rx_params self.verbose = verbose if self.rp.softbits: gr.hier_block2.__init__(self,"ofdm_demod", gr.io_signature (1, 1, gr.sizeof_gr_complex), # input signature gr.io_signature2(2, 2, gr.sizeof_float*self.dp.num_carriers*2, gr.sizeof_char)) # output signature else: gr.hier_block2.__init__(self,"ofdm_demod", gr.io_signature (1, 1, gr.sizeof_gr_complex), # input signature gr.io_signature2(2, 2, gr.sizeof_char*self.dp.num_carriers/4, gr.sizeof_char)) # output signature # workaround for a problem that prevents connecting more than one block directly (see trac ticket #161) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) # input filtering if self.rp.input_fft_filter: if verbose: print "--> RX filter enabled" lowpass_taps = gr.firdes_low_pass(1.0, # gain dp.sample_rate, # sampling rate rp.filt_bw, # cutoff frequency rp.filt_tb, # width of transition band gr.firdes.WIN_HAMMING) # Hamming window self.fft_filter = gr.fft_filter_ccc(1, lowpass_taps) # correct sample rate offset, if enabled if self.rp.autocorrect_sample_rate: if verbose: print "--> dynamic sample rate correction enabled" self.rate_detect_ns = detect_null.detect_null(dp.ns_length, False) self.rate_estimator = dab_swig.estimate_sample_rate_bf(dp.sample_rate, dp.frame_length) self.rate_prober = gr.probe_signal_f() self.connect(self.input, self.rate_detect_ns, self.rate_estimator, self.rate_prober) # self.resample = gr.fractional_interpolator_cc(0, 1) self.resample = dab_swig.fractional_interpolator_triggered_update_cc(0,1) self.connect(self.rate_detect_ns, (self.resample,1)) self.updater = Timer(0.1,self.update_correction) # self.updater = threading.Thread(target=self.update_correction) self.run_interpolater_update_thread = True self.updater.setDaemon(True) self.updater.start() else: self.run_interpolater_update_thread = False if self.rp.sample_rate_correction_factor != 1: if verbose: print "--> static sample rate correction enabled" self.resample = gr.fractional_interpolator_cc(0, self.rp.sample_rate_correction_factor) # timing and fine frequency synchronisation self.sync = ofdm_sync_dab2.ofdm_sync_dab(self.dp, self.rp, debug) # ofdm symbol sampler self.sampler = dab_swig.ofdm_sampler(dp.fft_length, dp.cp_length, dp.symbols_per_frame, rp.cp_gap) # fft for symbol vectors self.fft = gr.fft_vcc(dp.fft_length, True, [], True) # coarse frequency synchronisation self.cfs = dab_swig.ofdm_coarse_frequency_correct(dp.fft_length, dp.num_carriers, dp.cp_length) # diff phasor self.phase_diff = dab_swig.diff_phasor_vcc(dp.num_carriers) # remove pilot symbol self.remove_pilot = dab_swig.ofdm_remove_first_symbol_vcc(dp.num_carriers) # magnitude equalisation if self.rp.equalize_magnitude: if verbose: print "--> magnitude equalization enabled" self.equalizer = dab_swig.magnitude_equalizer_vcc(dp.num_carriers, rp.symbols_for_magnitude_equalization) # frequency deinterleaving self.deinterleave = dab_swig.frequency_interleaver_vcc(dp.frequency_deinterleaving_sequence_array) # symbol demapping self.demapper = dab_swig.qpsk_demapper_vcb(dp.num_carriers) # # connect everything # if self.rp.autocorrect_sample_rate or self.rp.sample_rate_correction_factor != 1: self.connect(self.input, self.resample) self.input2 = self.resample else: self.input2 = self.input if self.rp.input_fft_filter: self.connect(self.input2, self.fft_filter, self.sync) else: self.connect(self.input2, self.sync) # data stream self.connect((self.sync, 0), (self.sampler, 0), self.fft, (self.cfs, 0), self.phase_diff, (self.remove_pilot,0)) if self.rp.equalize_magnitude: self.connect((self.remove_pilot,0), (self.equalizer,0), self.deinterleave) else: self.connect((self.remove_pilot,0), self.deinterleave) if self.rp.softbits: if verbose: print "--> using soft bits" self.softbit_interleaver = dab_swig.complex_to_interleaved_float_vcf(self.dp.num_carriers) self.connect(self.deinterleave, self.softbit_interleaver, (self,0)) else: self.connect(self.deinterleave, self.demapper, (self,0)) # control stream self.connect((self.sync, 1), (self.sampler, 1), (self.cfs, 1), (self.remove_pilot,1)) if self.rp.equalize_magnitude: self.connect((self.remove_pilot,1), (self.equalizer,1), (self,1)) else: self.connect((self.remove_pilot,1), (self,1)) # calculate an estimate of the SNR self.phase_var_decim = gr.keep_one_in_n(gr.sizeof_gr_complex*self.dp.num_carriers, self.rp.phase_var_estimate_downsample) self.phase_var_arg = gr.complex_to_arg(dp.num_carriers) self.phase_var_v2s = gr.vector_to_stream(gr.sizeof_float, dp.num_carriers) self.phase_var_mod = dab_swig.modulo_ff(pi/2) self.phase_var_avg_mod = gr.iir_filter_ffd([rp.phase_var_estimate_alpha], [0,1-rp.phase_var_estimate_alpha]) self.phase_var_sub_avg = gr.sub_ff() self.phase_var_sqr = gr.multiply_ff() self.phase_var_avg = gr.iir_filter_ffd([rp.phase_var_estimate_alpha], [0,1-rp.phase_var_estimate_alpha]) self.probe_phase_var = gr.probe_signal_f() self.connect((self.remove_pilot,0), self.phase_var_decim, self.phase_var_arg, self.phase_var_v2s, self.phase_var_mod, (self.phase_var_sub_avg,0), (self.phase_var_sqr,0)) self.connect(self.phase_var_mod, self.phase_var_avg_mod, (self.phase_var_sub_avg,1)) self.connect(self.phase_var_sub_avg, (self.phase_var_sqr,1)) self.connect(self.phase_var_sqr, self.phase_var_avg, self.probe_phase_var) # measure processing rate self.measure_rate = dab_swig.measure_processing_rate(gr.sizeof_gr_complex, 2000000) self.connect(self.input, self.measure_rate) # debugging if debug: self.connect(self.fft, gr.file_sink(gr.sizeof_gr_complex*dp.fft_length, "debug/ofdm_after_fft.dat")) self.connect((self.cfs,0), gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_after_cfs.dat")) self.connect(self.phase_diff, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_diff_phasor.dat")) self.connect((self.remove_pilot,0), gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_pilot_removed.dat")) self.connect((self.remove_pilot,1), gr.file_sink(gr.sizeof_char, "debug/ofdm_after_cfs_trigger.dat")) self.connect(self.deinterleave, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_deinterleaved.dat")) if self.rp.equalize_magnitude: self.connect(self.equalizer, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_equalizer.dat")) if self.rp.softbits: self.connect(self.softbit_interleaver, gr.file_sink(gr.sizeof_float*dp.num_carriers*2, "debug/softbits.dat"))
def test_mult_ff(self): src1_data = (1, 2, 3, 4, 5) src2_data = (8, -3, 4, 8, 2) expected_result = (8, -6, 12, 32, 10) op = gr.multiply_ff() self.help_ff((src1_data, src2_data), expected_result, op)
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("-V", "--volume", type="eng_float", default=None, help="set volume (default is midpoint)") parser.add_option("-O", "--audio-output", type="string", default="plughw:0,0", help="pcm device name (default is plughw:0,0)") # print help when called with wrong arguments (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.file_source = gr.file_source(gr.sizeof_gr_complex * 1, offline_samples, True) chan_filter_coeffs = gr.firdes.low_pass( 1.0, # gain usrp_rate, # sampling rate 80e3, # passband cutoff 35e3, # transition width gr.firdes.WIN_HAMMING) self.chan_filter = gr.fir_filter_ccf(1, chan_filter_coeffs) print "# channel filter:", len(chan_filter_coeffs), "taps" # PLL-based WFM demod fm_alpha = 0.25 * 250e3 * math.pi / usrp_rate # 0.767 fm_beta = fm_alpha * fm_alpha / 4.0 # 0.147 fm_max_freq = 2.0 * math.pi * 90e3 / usrp_rate # 2.209 self.fm_demod = gr.pll_freqdet_cf( fm_alpha, # phase gain fm_beta, # freq gain fm_max_freq, # in radians/sample -fm_max_freq) self.connect(self.file_source, self.chan_filter, self.fm_demod) # L+R, pilot, L-R, RDS filters lpr_filter_coeffs = gr.firdes.low_pass( 1.0, # gain usrp_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.lpr_filter = gr.fir_filter_fff(audio_decim, lpr_filter_coeffs) pilot_filter_coeffs = gr.firdes.band_pass( 1.0, # gain usrp_rate, # sampling rate 19e3 - 500, # low cutoff 19e3 + 500, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) dsbsc_filter_coeffs = gr.firdes.band_pass( 1.0, # gain usrp_rate, # sampling rate 38e3 - 15e3 / 2, # low cutoff 38e3 + 15e3 / 2, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.dsbsc_filter = gr.fir_filter_fff(1, dsbsc_filter_coeffs) rds_filter_coeffs = gr.firdes.band_pass( 1.0, # gain usrp_rate, # sampling rate 57e3 - 3e3, # low cutoff 57e3 + 3e3, # high cutoff 3e3, # transition width gr.firdes.WIN_HAMMING) self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) print "# lpr filter:", len(lpr_filter_coeffs), "taps" print "# pilot filter:", len(pilot_filter_coeffs), "taps" print "# dsbsc filter:", len(dsbsc_filter_coeffs), "taps" print "# rds filter:", len(rds_filter_coeffs), "taps" self.connect(self.fm_demod, self.lpr_filter) self.connect(self.fm_demod, self.pilot_filter) self.connect(self.fm_demod, self.dsbsc_filter) self.connect(self.fm_demod, self.rds_filter) # down-convert L-R, RDS self.stereo_baseband = gr.multiply_ff() self.connect(self.pilot_filter, (self.stereo_baseband, 0)) self.connect(self.pilot_filter, (self.stereo_baseband, 1)) self.connect(self.dsbsc_filter, (self.stereo_baseband, 2)) self.rds_baseband = gr.multiply_ff() self.connect(self.pilot_filter, (self.rds_baseband, 0)) self.connect(self.pilot_filter, (self.rds_baseband, 1)) self.connect(self.pilot_filter, (self.rds_baseband, 2)) self.connect(self.rds_filter, (self.rds_baseband, 3)) # low-pass and downsample L-R lmr_filter_coeffs = gr.firdes.low_pass( 1.0, # gain usrp_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.lmr_filter = gr.fir_filter_fff(audio_decim, lmr_filter_coeffs) self.connect(self.stereo_baseband, self.lmr_filter) # create L, R from L-R, L+R self.left = gr.add_ff() self.right = gr.sub_ff() self.connect(self.lpr_filter, (self.left, 0)) self.connect(self.lmr_filter, (self.left, 1)) self.connect(self.lpr_filter, (self.right, 0)) self.connect(self.lmr_filter, (self.right, 1)) # send audio to null sink self.null0 = gr.null_sink(gr.sizeof_float) self.null1 = gr.null_sink(gr.sizeof_float) self.connect(self.left, self.null0) self.connect(self.right, self.null1) # low-pass the baseband RDS signal at 1.5kHz rds_bb_filter_coeffs = gr.firdes.low_pass( 1, # gain usrp_rate, # sampling rate 1.5e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HAMMING) self.rds_bb_filter = gr.fir_filter_fff(audio_decim, rds_bb_filter_coeffs) print "# rds bb filter:", len(rds_bb_filter_coeffs), "taps" self.connect(self.rds_baseband, self.rds_bb_filter) # 1187.5bps = 19kHz/16 self.clock_divider = rds.freq_divider(16) rds_clock_taps = gr.firdes.low_pass( 1, # gain usrp_rate, # sampling rate 1.2e3, # passband cutoff 1.5e3, # transition width gr.firdes.WIN_HAMMING) self.rds_clock = gr.fir_filter_fff(audio_decim, rds_clock_taps) print "# rds clock filter:", len(rds_clock_taps), "taps" self.connect(self.pilot_filter, self.clock_divider, self.rds_clock) # bpsk_demod, diff_decoder, rds_decoder self.bpsk_demod = rds.bpsk_demod(audio_rate) self.differential_decoder = gr.diff_decoder_bb(2) self.msgq = gr.msg_queue() self.rds_decoder = rds.data_decoder(self.msgq) self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) self.connect(self.rds_clock, (self.bpsk_demod, 1)) self.connect(self.bpsk_demod, self.differential_decoder) self.connect(self.differential_decoder, self.rds_decoder) self.frame = frame self.panel = panel self._build_gui(vbox, usrp_rate, audio_rate)
def __init__(self, fft_length, cp_length, half_sync, kstime, ks1time, threshold, logging=False): gr.hier_block2.__init__( self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3( 3, 3, # Output signature gr.sizeof_gr_complex, # delayed input gr.sizeof_float, # fine frequency offset gr.sizeof_char, # timing indicator ), ) if half_sync: period = fft_length / 2 window = fft_length / 2 else: # full symbol period = fft_length + cp_length window = fft_length # makes the plateau cp_length long # Calculate the frequency offset from the correlation of the preamble x_corr = gr.multiply_cc() self.connect(self, gr.conjugate_cc(), (x_corr, 0)) self.connect(self, gr.delay(gr.sizeof_gr_complex, period), (x_corr, 1)) P_d = gr.moving_average_cc(window, 1.0) self.connect(x_corr, P_d) # offset by -1 phi = gr.sample_and_hold_ff() self.corrmag = gr.complex_to_mag_squared() P_d_angle = gr.complex_to_arg() self.connect(P_d, P_d_angle, (phi, 0)) cross_correlate = 1 if cross_correlate == 1: # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) """ self.f2b = gr.float_to_char() self.slice = gr.threshold_ff(threshold, threshold, 0, fft_length) #self.connect(self, self.crosscorr_filter, self.corrmag, self.slice, self.f2b) self.connect(self.f2b, (phi,1)) self.connect(self.f2b, (self,2)) self.connect(self.f2b, gr.file_sink(gr.sizeof_char, "ofdm_f2b.dat")) """ # new method starts here - only crosscorrelate and use peak_detect block # peak_detect = gr.peak_detector_fb(100, 100, 30, 0.001) self.corrmag1 = gr.complex_to_mag_squared() self.connect(self, self.crosscorr_filter, self.corrmag, peak_detect) self.connect(peak_detect, (phi, 1)) self.connect(peak_detect, (self, 2)) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks_b.dat")) self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_corrmag.dat")) self.connect(self, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self, 0)) else: # Get the power of the input signal to normalize the output of the correlation R_d = gr.moving_average_ff(window, 1.0) self.connect(self, gr.complex_to_mag_squared(), R_d) R_d_squared = gr.multiply_ff() # this is retarded self.connect(R_d, (R_d_squared, 0)) self.connect(R_d, (R_d_squared, 1)) M_d = gr.divide_ff() self.connect(P_d, gr.complex_to_mag_squared(), (M_d, 0)) self.connect(R_d_squared, (M_d, 1)) # Now we need to detect peak of M_d matched_filter = gr.moving_average_ff(cp_length, 1.0 / cp_length) peak_detect = gr.peak_detector_fb(0.25, 0.25, 30, 0.001) self.connect(M_d, matched_filter, gr.add_const_ff(-1), peak_detect) offset = cp_length / 2 # cp_length/2 self.connect(peak_detect, (phi, 1)) self.connect(peak_detect, (self, 2)) self.connect(P_d_angle, gr.delay(gr.sizeof_float, offset), (phi, 0)) self.connect( self, gr.delay(gr.sizeof_gr_complex, (fft_length + offset)), (self, 0) ) # delay the input to follow the freq offset self.connect(peak_detect, gr.delay(gr.sizeof_char, (fft_length + offset)), (self, 2)) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks_b.dat")) self.connect(matched_filter, gr.file_sink(gr.sizeof_float, "sync-mf.dat")) self.connect(phi, (self, 1)) if logging: self.connect(matched_filter, gr.file_sink(gr.sizeof_float, "sync-mf.dat")) self.connect(M_d, gr.file_sink(gr.sizeof_float, "sync-M.dat")) self.connect(P_d_angle, gr.file_sink(gr.sizeof_float, "sync-angle.dat")) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks.datb")) self.connect(phi, gr.file_sink(gr.sizeof_float, "sync-phi.dat"))
def __init__(self, options, noutputs=2): """ @param options: parsed raw.ofdm_params """ self.params = ofdm_params(options) params = self.params if noutputs == 2: output_signature = gr.io_signature2( 2, 2, gr.sizeof_gr_complex * params.data_tones, gr.sizeof_char) elif noutputs == 3: output_signature = gr.io_signature3( 3, 3, gr.sizeof_gr_complex * params.data_tones, gr.sizeof_char, gr.sizeof_float) elif noutputs == 4: output_signature = gr.io_signature4( 4, 4, gr.sizeof_gr_complex * params.data_tones, gr.sizeof_char, gr.sizeof_float, gr.sizeof_float) else: # error raise Exception("unsupported number of outputs") gr.hier_block2.__init__(self, "ofdm_demod", gr.io_signature(1, 1, gr.sizeof_gr_complex), output_signature) self.ofdm_recv = ofdm_receiver(params, options.log) # FIXME: magic parameters phgain = 0.4 frgain = phgain * phgain / 4.0 eqgain = 0.05 self.ofdm_demod = raw.ofdm_demapper(params.carriers, phgain, frgain, eqgain) # the studios can't handle the whole ofdm in one thread #ofdm_recv = raw.wrap_sts(self.ofdm_recv) ofdm_recv = self.ofdm_recv self.connect(self, ofdm_recv) self.connect((ofdm_recv, 0), (self.ofdm_demod, 0)) self.connect((ofdm_recv, 1), (self.ofdm_demod, 1)) self.connect(self.ofdm_demod, (self, 0)) self.connect((ofdm_recv, 1), (self, 1)) if noutputs > 2: # average noise power per (pilot) subcarrier self.connect((self.ofdm_demod, 1), (self, 2)) if noutputs > 3: # average signal power per subcarrier self.connect( (ofdm_recv, 0), gr.vector_to_stream(gr.sizeof_float, params.occupied_tones), gr.integrate_ff(params.occupied_tones), gr.multiply_ff(1.0 / params.occupied_tones), (self, 3)) if options.log: self.connect( (self.ofdm_demod, 2), gr.file_sink(gr.sizeof_gr_complex * params.occupied_tones, 'rx-eq.dat')) self.connect((self.ofdm_demod, 1), gr.file_sink(gr.sizeof_float, 'rx-noise.dat')) self.connect((self.ofdm_demod, 0), gr.file_sink(gr.sizeof_gr_complex * params.data_tones, 'rx-demap.dat'))
def __init__(self): gr.top_block.__init__ (self) parser=OptionParser(option_class=eng_option) parser.add_option("-H", "--hostname", type="string", default="localhost", help="set hostname of generic sdr") parser.add_option("-P", "--portnum", type="int", default=None, help="set portnum of generic sdr") parser.add_option("-r", "--sdr_rate", type="eng_float", default=250e3, help="set sample rate of generic sdr") 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="plughw:0,0", help="pcm device name (default is plughw:0,0)") # print help when called with wrong arguments (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.vol = options.volume if self.vol is None: self.vol = 0.1 # connect to generic SDR sdr_rate = options.sdr_rate audio_decim = 8 audio_rate = int(sdr_rate/audio_decim) print "audio_rate = ", audio_rate self.interleaved_short_to_complex = gr.interleaved_short_to_complex() self.char_to_short = gr.char_to_short(1) self.sdr_source = grc_blks2.tcp_source( itemsize=gr.sizeof_char*1, addr=options.hostname, port=options.portnum, server=False ) # self.sdr_source = gr.file_source(1, 'sdrs_baseband.dat') # self.throttle = gr.throttle(1, 500e3) # self.connect(self.sdr_source, self.file_sink) self.logger = gr.file_sink(1, 'log.out') self.connect(self.sdr_source, self.logger) # channel filter chan_filter_coeffs = gr.firdes.low_pass( 1.0, # gain sdr_rate, # sampling rate 80e3, # passband cutoff 35e3, # transition width gr.firdes.WIN_HAMMING) self.chan_filter = gr.fir_filter_ccf(1, chan_filter_coeffs) # print "# channel filter:", len(chan_filter_coeffs), "taps" # PLL-based WFM demod fm_alpha = 0.25 * 250e3 * math.pi / sdr_rate # 0.767 fm_beta = fm_alpha * fm_alpha / 4.0 # 0.147 fm_max_freq = 2.0 * math.pi * 90e3 / sdr_rate # 2.209 self.fm_demod = gr.pll_freqdet_cf( 1.0, # Loop BW fm_max_freq, # in radians/sample -fm_max_freq) self.fm_demod.set_alpha(fm_alpha) self.fm_demod.set_beta(fm_beta) self.connect(self.sdr_source, self.char_to_short) self.connect(self.char_to_short, self.interleaved_short_to_complex) self.connect(self.interleaved_short_to_complex, self.chan_filter, self.fm_demod) # L+R, pilot, L-R, RDS filters lpr_filter_coeffs = gr.firdes.low_pass( 1.0, # gain sdr_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.lpr_filter = gr.fir_filter_fff(audio_decim, lpr_filter_coeffs) pilot_filter_coeffs = gr.firdes.band_pass( 1.0, # gain sdr_rate, # sampling rate 19e3-500, # low cutoff 19e3+500, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) dsbsc_filter_coeffs = gr.firdes.band_pass( 1.0, # gain sdr_rate, # sampling rate 38e3-15e3/2, # low cutoff 38e3+15e3/2, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.dsbsc_filter = gr.fir_filter_fff(1, dsbsc_filter_coeffs) rds_filter_coeffs = gr.firdes.band_pass( 1.0, # gain sdr_rate, # sampling rate 57e3-3e3, # low cutoff 57e3+3e3, # high cutoff 3e3, # transition width gr.firdes.WIN_HAMMING) self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) # print "# lpr filter:", len(lpr_filter_coeffs), "taps" # print "# pilot filter:", len(pilot_filter_coeffs), "taps" # print "# dsbsc filter:", len(dsbsc_filter_coeffs), "taps" # print "# rds filter:", len(rds_filter_coeffs), "taps" self.connect(self.fm_demod, self.lpr_filter) self.connect(self.fm_demod, self.pilot_filter) self.connect(self.fm_demod, self.dsbsc_filter) self.connect(self.fm_demod, self.rds_filter) # down-convert L-R, RDS self.stereo_baseband = gr.multiply_ff() self.connect(self.pilot_filter, (self.stereo_baseband, 0)) self.connect(self.pilot_filter, (self.stereo_baseband, 1)) self.connect(self.dsbsc_filter, (self.stereo_baseband, 2)) self.rds_baseband = gr.multiply_ff() self.connect(self.pilot_filter, (self.rds_baseband, 0)) self.connect(self.pilot_filter, (self.rds_baseband, 1)) self.connect(self.pilot_filter, (self.rds_baseband, 2)) self.connect(self.rds_filter, (self.rds_baseband, 3)) # low-pass and downsample L-R lmr_filter_coeffs = gr.firdes.low_pass( 1.0, # gain sdr_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.lmr_filter = gr.fir_filter_fff(audio_decim, lmr_filter_coeffs) self.connect(self.stereo_baseband, self.lmr_filter) # create L, R from L-R, L+R self.left = gr.add_ff() self.right = gr.sub_ff() self.connect(self.lpr_filter, (self.left, 0)) self.connect(self.lmr_filter, (self.left, 1)) self.connect(self.lpr_filter, (self.right, 0)) self.connect(self.lmr_filter, (self.right, 1)) # volume control, complex2flot, audio sink self.volume_control_l = gr.multiply_const_ff(self.vol) self.volume_control_r = gr.multiply_const_ff(self.vol) output_audio_rate = 48000 self.resamp_L = blks2.rational_resampler_fff(interpolation=output_audio_rate,decimation=audio_rate,taps=None,fractional_bw=None,) self.resamp_R = blks2.rational_resampler_fff(interpolation=output_audio_rate,decimation=audio_rate,taps=None,fractional_bw=None,) self.connect(self.left, self.volume_control_l, self.resamp_L) self.connect(self.right, self.volume_control_r, self.resamp_R) # self.audio_sink = audio.sink(int(output_audio_rate), # options.audio_output, False) # self.connect(self.resamp_L, (self.audio_sink, 0)) # self.connect(self.resamp_R, (self.audio_sink, 1)) self.file_sink1 = gr.file_sink(gr.sizeof_float, 'audioL.dat') self.file_sink2 = gr.file_sink(gr.sizeof_float, 'audioR.dat') self.file_sink3 = gr.file_sink(gr.sizeof_float, 'fmDemod.dat') self.connect(self.resamp_L, self.file_sink1) self.connect(self.resamp_R, self.file_sink2) self.connect(self.fm_demod, self.file_sink3) # low-pass the baseband RDS signal at 1.5kHz rds_bb_filter_coeffs = gr.firdes.low_pass( 1, # gain sdr_rate, # sampling rate 1.5e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HAMMING) self.rds_bb_filter = gr.fir_filter_fff(audio_decim, rds_bb_filter_coeffs) # print "# rds bb filter:", len(rds_bb_filter_coeffs), "taps" self.connect(self.rds_baseband, self.rds_bb_filter) # 1187.5bps = 19kHz/16 self.clock_divider = rds.freq_divider(16) rds_clock_taps = gr.firdes.low_pass( 1, # gain sdr_rate, # sampling rate 1.2e3, # passband cutoff 1.5e3, # transition width gr.firdes.WIN_HAMMING) self.rds_clock = gr.fir_filter_fff(audio_decim, rds_clock_taps) # print "# rds clock filter:", len(rds_clock_taps), "taps" self.connect(self.pilot_filter, self.clock_divider, self.rds_clock) # bpsk_demod, diff_decoder, rds_decoder self.bpsk_demod = rds.bpsk_demod(audio_rate) self.differential_decoder = gr.diff_decoder_bb(2) self.msgq = gr.msg_queue() self.rds_decoder = rds.data_decoder(self.msgq) self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) self.connect(self.rds_clock, (self.bpsk_demod, 1)) self.connect(self.bpsk_demod, self.differential_decoder) self.connect(self.differential_decoder, self.rds_decoder)
def __init__(self): gr.top_block.__init__ (self) parser=OptionParser(option_class=eng_option) parser.add_option("-H", "--hostname", type="string", default="localhost", help="set hostname of generic sdr") parser.add_option("-P", "--portnum", type="int", default=None, help="set portnum of generic sdr") parser.add_option("-W", "--wsportnum", type="int", default=None, help="set portnum of audio websocket server") parser.add_option("-r", "--sdr_rate", type="eng_float", default=250e3, help="set sample rate of generic sdr") 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="plughw:0,0", help="pcm device name (default is plughw:0,0)") # print help when called with wrong arguments (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.vol = options.volume if self.vol is None: self.vol = 0.1 # connect to generic SDR sdr_rate = options.sdr_rate audio_decim = 8 audio_rate = int(sdr_rate/audio_decim) print "audio_rate = ", audio_rate self.interleaved_short_to_complex = gr.interleaved_short_to_complex() self.char_to_short = gr.char_to_short(1) self.sdr_source = grc_blks2.tcp_source( itemsize=gr.sizeof_char*1, addr=options.hostname, port=options.portnum, server=False ) # self.sdr_source = gr.file_source(1, 'sdrs_baseband.dat') # self.throttle = gr.throttle(1, 500e3) # self.connect(self.sdr_source, self.file_sink) self.logger = gr.file_sink(1, 'log.out') self.connect(self.sdr_source, self.logger) # channel filter chan_filter_coeffs = gr.firdes.low_pass( 1.0, # gain sdr_rate, # sampling rate 80e3, # passband cutoff 35e3, # transition width gr.firdes.WIN_HAMMING) self.chan_filter = gr.fir_filter_ccf(1, chan_filter_coeffs) # print "# channel filter:", len(chan_filter_coeffs), "taps" # PLL-based WFM demod fm_alpha = 0.25 * 250e3 * math.pi / sdr_rate # 0.767 fm_beta = fm_alpha * fm_alpha / 4.0 # 0.147 fm_max_freq = 2.0 * math.pi * 90e3 / sdr_rate # 2.209 self.fm_demod = gr.pll_freqdet_cf( 1.0, # Loop BW fm_max_freq, # in radians/sample -fm_max_freq) self.fm_demod.set_alpha(fm_alpha) self.fm_demod.set_beta(fm_beta) self.connect(self.sdr_source, self.char_to_short) self.connect(self.char_to_short, self.interleaved_short_to_complex) self.connect(self.interleaved_short_to_complex, self.chan_filter, self.fm_demod) # L+R, pilot, L-R, RDS filters lpr_filter_coeffs = gr.firdes.low_pass( 1.0, # gain sdr_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.lpr_filter = gr.fir_filter_fff(audio_decim, lpr_filter_coeffs) pilot_filter_coeffs = gr.firdes.band_pass( 1.0, # gain sdr_rate, # sampling rate 19e3-500, # low cutoff 19e3+500, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) dsbsc_filter_coeffs = gr.firdes.band_pass( 1.0, # gain sdr_rate, # sampling rate 38e3-15e3/2, # low cutoff 38e3+15e3/2, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.dsbsc_filter = gr.fir_filter_fff(1, dsbsc_filter_coeffs) rds_filter_coeffs = gr.firdes.band_pass( 1.0, # gain sdr_rate, # sampling rate 57e3-3e3, # low cutoff 57e3+3e3, # high cutoff 3e3, # transition width gr.firdes.WIN_HAMMING) self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) # print "# lpr filter:", len(lpr_filter_coeffs), "taps" # print "# pilot filter:", len(pilot_filter_coeffs), "taps" # print "# dsbsc filter:", len(dsbsc_filter_coeffs), "taps" # print "# rds filter:", len(rds_filter_coeffs), "taps" self.connect(self.fm_demod, self.lpr_filter) self.connect(self.fm_demod, self.pilot_filter) self.connect(self.fm_demod, self.dsbsc_filter) self.connect(self.fm_demod, self.rds_filter) # down-convert L-R, RDS self.stereo_baseband = gr.multiply_ff() self.connect(self.pilot_filter, (self.stereo_baseband, 0)) self.connect(self.pilot_filter, (self.stereo_baseband, 1)) self.connect(self.dsbsc_filter, (self.stereo_baseband, 2)) self.rds_baseband = gr.multiply_ff() self.connect(self.pilot_filter, (self.rds_baseband, 0)) self.connect(self.pilot_filter, (self.rds_baseband, 1)) self.connect(self.pilot_filter, (self.rds_baseband, 2)) self.connect(self.rds_filter, (self.rds_baseband, 3)) # low-pass and downsample L-R lmr_filter_coeffs = gr.firdes.low_pass( 1.0, # gain sdr_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.lmr_filter = gr.fir_filter_fff(audio_decim, lmr_filter_coeffs) self.connect(self.stereo_baseband, self.lmr_filter) # create L, R from L-R, L+R self.left = gr.add_ff() self.right = gr.sub_ff() self.connect(self.lpr_filter, (self.left, 0)) self.connect(self.lmr_filter, (self.left, 1)) self.connect(self.lpr_filter, (self.right, 0)) self.connect(self.lmr_filter, (self.right, 1)) # volume control, complex2flot, audio sink self.volume_control_l = gr.multiply_const_ff(self.vol) self.volume_control_r = gr.multiply_const_ff(self.vol) output_audio_rate = 48000 self.resamp_L = blks2.rational_resampler_fff(interpolation=output_audio_rate,decimation=audio_rate,taps=None,fractional_bw=None,) self.resamp_R = blks2.rational_resampler_fff(interpolation=output_audio_rate,decimation=audio_rate,taps=None,fractional_bw=None,) self.connect(self.left, self.volume_control_l, self.resamp_L) self.connect(self.right, self.volume_control_r, self.resamp_R) # self.audio_sink = audio.sink(int(output_audio_rate), # options.audio_output, False) # self.connect(self.resamp_L, (self.audio_sink, 0)) # self.connect(self.resamp_R, (self.audio_sink, 1)) # self.file_sink1 = gr.file_sink(gr.sizeof_float, 'audioL.dat') # self.file_sink2 = gr.file_sink(gr.sizeof_float, 'audioR.dat') # self.file_sink3 = gr.file_sink(gr.sizeof_float, 'fmDemod.dat') # self.connect(self.resamp_L, self.file_sink1) # self.connect(self.resamp_R, self.file_sink2) # self.connect(self.fm_demod, self.file_sink3) # Interleave both channels so that we can push over one websocket connection self.audio_interleaver = gr.float_to_complex(1) self.connect(self.resamp_L, (self.audio_interleaver, 0)) self.connect(self.resamp_R, (self.audio_interleaver, 1)) self.ws_audio_server = sdrportal.ws_sink_c(True, options.wsportnum, "FLOAT") self.connect(self.audio_interleaver, self.ws_audio_server) # low-pass the baseband RDS signal at 1.5kHz rds_bb_filter_coeffs = gr.firdes.low_pass( 1, # gain sdr_rate, # sampling rate 1.5e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HAMMING) self.rds_bb_filter = gr.fir_filter_fff(audio_decim, rds_bb_filter_coeffs) # print "# rds bb filter:", len(rds_bb_filter_coeffs), "taps" self.connect(self.rds_baseband, self.rds_bb_filter) # 1187.5bps = 19kHz/16 self.clock_divider = rds.freq_divider(16) rds_clock_taps = gr.firdes.low_pass( 1, # gain sdr_rate, # sampling rate 1.2e3, # passband cutoff 1.5e3, # transition width gr.firdes.WIN_HAMMING) self.rds_clock = gr.fir_filter_fff(audio_decim, rds_clock_taps) # print "# rds clock filter:", len(rds_clock_taps), "taps" self.connect(self.pilot_filter, self.clock_divider, self.rds_clock) # bpsk_demod, diff_decoder, rds_decoder self.bpsk_demod = rds.bpsk_demod(audio_rate) self.differential_decoder = gr.diff_decoder_bb(2) self.msgq = gr.msg_queue() self.rds_decoder = rds.data_decoder(self.msgq) self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) self.connect(self.rds_clock, (self.bpsk_demod, 1)) self.connect(self.bpsk_demod, self.differential_decoder) self.connect(self.differential_decoder, self.rds_decoder)
def __init__(self, fft_length, cp_length, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) else: moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)] self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() movingsum2_taps = [1.0 for i in range(fft_length//2)] if 1: self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect #self.sub1 = gr.add_const_ff(-1) self.sub1 = gr.add_const_ff(0) self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) #self.pk_detect = gr.peak_detector2_fb(9) self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # Get the power of the input signal to normalize the output of the correlation self.connect(self.input, self.inputmag2, self.inputmovingsum) self.connect(self.inputmovingsum, (self.square,0)) self.connect(self.inputmovingsum, (self.square,1)) self.connect(self.square, (self.normalize,1)) self.connect(self.c2mag, (self.normalize,0)) # Create a moving sum filter for the corr output matched_filter_taps = [1.0/cp_length for i in range(cp_length)] self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps) self.connect(self.normalize, self.matched_filter) self.connect(self.matched_filter, self.sub1, self.pk_detect) #self.connect(self.matched_filter, self.pk_detect) self.connect(self.pk_detect, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.pk_detect, (self,1)) if logging: self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
def __init__(self, subc, vlen, ss): gr.hier_block2.__init__(self, "new_snr_estimator", gr.io_signature(1,1,gr.sizeof_gr_complex*vlen), gr.io_signature(1,1,gr.sizeof_float)) print "Created Milan's SNR estimator" trigger = [0]*vlen trigger[0] = 1 u = range (vlen/ss*(ss-1)) zeros_ind= map(lambda z: z+1+z/(ss-1),u) skip1 = skip(gr.sizeof_gr_complex,vlen) for x in zeros_ind: skip1.skip(x) #print "skipped zeros",zeros_ind v = range (vlen/ss) ones_ind= map(lambda z: z*ss,v) skip2 = skip(gr.sizeof_gr_complex,vlen) for x in ones_ind: skip2.skip(x) #print "skipped ones",ones_ind v2s = gr.vector_to_stream(gr.sizeof_gr_complex,vlen) s2v1 = gr.stream_to_vector(gr.sizeof_gr_complex,vlen/ss) trigger_src_1 = gr.vector_source_b(trigger,True) s2v2 = gr.stream_to_vector(gr.sizeof_gr_complex,vlen/ss*(ss-1)) trigger_src_2 = gr.vector_source_b(trigger,True) mag_sq_ones = gr.complex_to_mag_squared(vlen/ss) mag_sq_zeros = gr.complex_to_mag_squared(vlen/ss*(ss-1)) filt_ones = gr.single_pole_iir_filter_ff(0.1,vlen/ss) filt_zeros = gr.single_pole_iir_filter_ff(0.1,vlen/ss*(ss-1)) sum_ones = vector_sum_vff(vlen/ss) sum_zeros = vector_sum_vff(vlen/ss*(ss-1)) D = gr.divide_ff() P = gr.multiply_ff() mult1 = gr.multiply_const_ff(ss-1.0) add1 = gr.add_const_ff(-1.0) mult2 = gr.multiply_const_ff(1./ss) scsnrdb = gr.nlog10_ff(10,1,0) filt_end = gr.single_pole_iir_filter_ff(0.1) self.connect(self,v2s,skip1,s2v1,mag_sq_ones,filt_ones,sum_ones) self.connect(trigger_src_1,(skip1,1)) self.connect(v2s,skip2,s2v2,mag_sq_zeros,filt_zeros,sum_zeros) self.connect(trigger_src_2,(skip2,1)) self.connect(sum_ones,D) self.connect(sum_zeros,(D,1)) self.connect(D,mult1,add1,mult2) self.connect(mult2,scsnrdb,filt_end,self)
def __init__(self, fft_length, cp_length, kstime, threshold, threshold_type, threshold_gap, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__( self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length / 2) # Correlation from ML Sync self.conjg = gr.conjugate_cc() self.corr = gr.multiply_cc() # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length // 2)] self.moving_sum_filter = gr.fir_filter_ccf(1, moving_sum_taps) else: moving_sum_taps = [ complex(1.0, 0.0) for i in range(fft_length // 2) ] self.moving_sum_filter = gr.fft_filter_ccc(1, moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() movingsum2_taps = [1.0 for i in range(fft_length // 2)] #movingsum2_taps = [0.5 for i in range(fft_length*4)] #apurv - implementing Veljo's suggestion, when pause b/w packets if 1: self.inputmovingsum = gr.fir_filter_fff(1, movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1, movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.sub1 = gr.add_const_ff(-1) self.pk_detect = gr.peak_detector_fb( 0.20, 0.20, 30, 0.001 ) #apurv - implementing Veljo's suggestion, when pause b/w packets self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr, 0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr, 1)) self.connect(self.corr, self.moving_sum_filter) #self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold, 0)) # apurv-- #self.connect(self.angle, gr.delay(gr.sizeof_float, offset), (self.sample_and_hold, 0)) #apurv++ cross_correlate = 1 if cross_correlate == 1: # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) # get the magnitude # self.corrmag = gr.complex_to_mag_squared() self.f2b = gr.float_to_char() self.threshold_factor = threshold #0.0012 #0.012 #0.0015 if 0: self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0, fft_length) else: #thresholds = [self.threshold_factor, 9e-5] self.slice = gr.threshold_ff(threshold, threshold, 0, fft_length, threshold_type, threshold_gap) self.connect(self.input, self.crosscorr_filter, self.corrmag, self.slice, self.f2b) # some debug dump # self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_corrmag.dat")) #self.connect(self.f2b, gr.file_sink(gr.sizeof_char, "ofdm_f2b.dat")) self.connect(self.f2b, (self.sample_and_hold, 1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self, 0)) #self.connect(self.pk_detect, (self,1)) #removed #self.connect(self.f2b, gr.delay(gr.sizeof_char, 1), (self, 1)) self.connect(self.f2b, (self, 1)) if logging: self.connect( self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect( self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect( self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect( self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect( self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect( self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
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("-T", "--tx-subdev-spec", type="subdev", default=None, help="select USRP Tx side A or B") parser.add_option("-f", "--freq", type="eng_float", default=107.2e6, help="set Tx frequency to FREQ [required]", metavar="FREQ") parser.add_option("--wavfile", type="string", default="", help="read input from FILE") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.usrp_interp = 200 self.u = usrp.sink_c (0, self.usrp_interp) print "USRP Serial: ", self.u.serial_number() self.dac_rate = self.u.dac_rate() # 128 MS/s self.usrp_rate = self.dac_rate / self.usrp_interp # 640 kS/s self.sw_interp = 5 self.audio_rate = self.usrp_rate / self.sw_interp # 128 kS/s # determine the daughterboard subdevice we're using if options.tx_subdev_spec is None: options.tx_subdev_spec = usrp.pick_tx_subdevice(self.u) self.u.set_mux(usrp.determine_tx_mux_value(self.u, options.tx_subdev_spec)) self.subdev = usrp.selected_subdev(self.u, options.tx_subdev_spec) print "Using d'board: ", self.subdev.side_and_name() # set max Tx gain, tune frequency and enable transmitter self.subdev.set_gain(self.subdev.gain_range()[1]) if self.u.tune(self.subdev.which(), self.subdev, options.freq): print "Tuned to", options.freq/1e6, "MHz" else: sys.exit(1) self.subdev.set_enable(True) # open wav file containing floats in the [-1, 1] range, repeat if options.wavfile is None: print "Please provide a wavfile to transmit! Exiting\n" sys.exit(1) self.src = gr.wavfile_source (options.wavfile, True) nchans = self.src.channels() sample_rate = self.src.sample_rate() bits_per_sample = self.src.bits_per_sample() print nchans, "channels,", sample_rate, "kS/s,", bits_per_sample, "bits/sample" # resample to 128kS/s if sample_rate == 44100: self.resample_left = blks2.rational_resampler_fff(32,11) self.resample_right = blks2.rational_resampler_fff(32,11) elif sample_rate == 48000: self.resample_left == blks2.rational_resampler_fff(8,3) self.resample_right == blks2.rational_resampler_fff(8,3) elif sample_rate == 8000: self.resample_left == blks2.rational_resampler_fff(16,1) self.resample_right == blks2.rational_resampler_fff(16,1) else: print sample_rate, "is an unsupported sample rate" sys.exit(1) self.connect ((self.src, 0), self.resample_left) self.connect ((self.src, 1), self.resample_right) # create L+R (mono) and L-R (stereo) self.audio_lpr = gr.add_ff() self.audio_lmr = gr.sub_ff() self.connect (self.resample_left, (self.audio_lpr, 0)) self.connect (self.resample_left, (self.audio_lmr, 0)) self.connect (self.resample_right, (self.audio_lpr, 1)) self.connect (self.resample_right, (self.audio_lmr, 1)) # low-pass filter for L+R audio_lpr_taps = gr.firdes.low_pass (0.3, # gain self.audio_rate, # sampling rate 15e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HANN) self.audio_lpr_filter = gr.fir_filter_fff (1, audio_lpr_taps) self.connect (self.audio_lpr, self.audio_lpr_filter) # create pilot tone at 19 kHz self.pilot = gr.sig_source_f(self.audio_rate, # sampling freq gr.GR_SIN_WAVE, # waveform 19e3, # frequency 3e-2) # amplitude # create the L-R signal carrier at 38 kHz, high-pass to remove 0Hz tone self.stereo_carrier = gr.multiply_ff() self.connect (self.pilot, (self.stereo_carrier, 0)) self.connect (self.pilot, (self.stereo_carrier, 1)) stereo_carrier_taps = gr.firdes.high_pass (1, # gain self.audio_rate, # sampling rate 1e4, # cutoff freq 2e3, # transition width gr.firdes.WIN_HANN) self.stereo_carrier_filter = gr.fir_filter_fff(1, stereo_carrier_taps) self.connect (self.stereo_carrier, self.stereo_carrier_filter) # upconvert L-R to 23-53 kHz and band-pass self.mix_stereo = gr.multiply_ff() audio_lmr_taps = gr.firdes.band_pass (3e3, # gain self.audio_rate, # sampling rate 23e3, # low cutoff 53e3, # high cuttof 2e3, # transition width gr.firdes.WIN_HANN) self.audio_lmr_filter = gr.fir_filter_fff (1, audio_lmr_taps) self.connect (self.audio_lmr, (self.mix_stereo, 0)) self.connect (self.stereo_carrier_filter, (self.mix_stereo, 1)) self.connect (self.mix_stereo, self.audio_lmr_filter) # mix L+R, pilot and L-R self.mixer = gr.add_ff() self.connect (self.audio_lpr_filter, (self.mixer, 0)) self.connect (self.pilot, (self.mixer, 1)) self.connect (self.audio_lmr_filter, (self.mixer, 2)) # interpolation & pre-emphasis interp_taps = gr.firdes.low_pass (self.sw_interp, # gain self.audio_rate, # Fs 60e3, # cutoff freq 5e3, # transition width gr.firdes.WIN_HAMMING) self.interpolator = gr.interp_fir_filter_fff (self.sw_interp, interp_taps) self.pre_emph = blks2.fm_preemph(self.usrp_rate, tau=50e-6) self.connect (self.mixer, self.interpolator, self.pre_emph) # fm modulation, gain & TX max_dev = 100e3 k = 2 * math.pi * max_dev / self.usrp_rate # modulator sensitivity self.modulator = gr.frequency_modulator_fc (k) self.gain = gr.multiply_const_cc (1e3) self.connect (self.pre_emph, self.modulator, self.gain, self.u) # plot an FFT to verify we are sending what we want pre_mod = fftsink2.fft_sink_f(panel, title="Before Interpolation", fft_size=512, sample_rate=self.audio_rate, y_per_div=20, ref_level=20) self.connect (self.mixer, pre_mod) vbox.Add (pre_mod.win, 1, wx.EXPAND)
def __init__(self, fft_length, cp_length, snr, kstime, logging): ''' Maximum Likelihood OFDM synchronizer: J. van de Beek, M. Sandell, and P. O. Borjesson, "ML Estimation of Time and Frequency Offset in OFDM Systems," IEEE Trans. Signal Processing, vol. 45, no. 7, pp. 1800-1805, 1997. ''' gr.hier_block2.__init__(self, "ofdm_sync_ml", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) SNR = 10.0**(snr/10.0) rho = SNR / (SNR + 1.0) symbol_length = fft_length + cp_length # ML Sync # Energy Detection from ML Sync self.connect(self, self.input) # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length) self.connect(self.input, self.delay) # magnitude squared blocks self.magsqrd1 = gr.complex_to_mag_squared() self.magsqrd2 = gr.complex_to_mag_squared() self.adder = gr.add_ff() moving_sum_taps = [rho/2 for i in range(cp_length)] self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps) self.connect(self.input,self.magsqrd1) self.connect(self.delay,self.magsqrd2) self.connect(self.magsqrd1,(self.adder,0)) self.connect(self.magsqrd2,(self.adder,1)) self.connect(self.adder,self.moving_sum_filter) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.mixer = gr.multiply_cc(); movingsum2_taps = [1.0 for i in range(cp_length)] self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps) # Correlator data handler self.c2mag = gr.complex_to_mag() self.angle = gr.complex_to_arg() self.connect(self.input,(self.mixer,1)) self.connect(self.delay,self.conjg,(self.mixer,0)) self.connect(self.mixer,self.movingsum2,self.c2mag) self.connect(self.movingsum2,self.angle) # ML Sync output arg, need to find maximum point of this self.diff = gr.sub_ff() self.connect(self.c2mag,(self.diff,0)) self.connect(self.moving_sum_filter,(self.diff,1)) #ML measurements input to sampler block and detect self.f2c = gr.float_to_complex() self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005) self.sample_and_hold = gr.sample_and_hold_ff() # use the sync loop values to set the sampler and the NCO # self.diff = theta # self.angle = epsilon self.connect(self.diff, self.pk_detect) # The DPLL corrects for timing differences between CP correlations use_dpll = 0 if use_dpll: self.dpll = gr.dpll_bb(float(symbol_length),0.01) self.connect(self.pk_detect, self.dpll) self.connect(self.dpll, (self.sample_and_hold,1)) else: self.connect(self.pk_detect, (self.sample_and_hold,1)) self.connect(self.angle, (self.sample_and_hold,0)) ################################ # correlate against known symbol # This gives us the same timing signal as the PN sync block only on the preamble # we don't use the signal generated from the CP correlation because we don't want # to readjust the timing in the middle of the packet or we ruin the equalizer settings. kstime = [k.conjugate() for k in kstime] kstime.reverse() self.kscorr = gr.fir_filter_ccc(1, kstime) self.corrmag = gr.complex_to_mag_squared() self.div = gr.divide_ff() # The output signature of the correlation has a few spikes because the rest of the # system uses the repeated preamble symbol. It needs to work that generically if # anyone wants to use this against a WiMAX-like signal since it, too, repeats. # The output theta of the correlator above is multiplied with this correlation to # identify the proper peak and remove other products in this cross-correlation self.threshold_factor = 0.1 self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0) self.f2b = gr.float_to_char() self.b2f = gr.char_to_float() self.mul = gr.multiply_ff() # Normalize the power of the corr output by the energy. This is not really needed # and could be removed for performance, but it makes for a cleaner signal. # if this is removed, the threshold value needs adjustment. self.connect(self.input, self.kscorr, self.corrmag, (self.div,0)) self.connect(self.moving_sum_filter, (self.div,1)) self.connect(self.div, (self.mul,0)) self.connect(self.pk_detect, self.b2f, (self.mul,1)) self.connect(self.mul, self.slice) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.slice, self.f2b, (self,1)) if logging: self.connect(self.moving_sum_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-energy_f.dat")) self.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat")) self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-corrmag_f.dat")) self.connect(self.kscorr, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-kscorr_c.dat")) self.connect(self.div, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-div_f.dat")) self.connect(self.mul, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-mul_f.dat")) self.connect(self.slice, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-slice_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat")) if use_dpll: self.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat"))
def __init__(self): gr.top_block.__init__ (self) 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=91.2e6, help="set frequency to FREQ", metavar="FREQ") parser.add_option("-g", "--gain", type="eng_float", default=None, help="set gain in dB") parser.add_option("-s", "--squelch", type="eng_float", default=0, help="set squelch level (default is 0)") 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="plughw:0,0", help="pcm device name (default is plughw:0,0)") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) # connect to USRP usrp_decim = 250 self.u = usrp.source_c(0, usrp_decim) print "USRP Serial: ", self.u.serial_number() demod_rate = self.u.adc_rate() / usrp_decim # 256 kS/s audio_decim = 8 audio_rate = demod_rate / audio_decim # 32 kS/s if options.rx_subdev_spec is None: options.rx_subdev_spec = usrp.pick_subdev(self.u, dblist) 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 d'board", self.subdev.side_and_name() # gain, volume, frequency self.gain = options.gain if options.gain is None: self.gain = self.subdev.gain_range()[1] self.vol = options.volume if self.vol is None: g = self.volume_range() self.vol = float(g[0]+g[1])/2 self.freq = options.freq if abs(self.freq) < 1e6: self.freq *= 1e6 print "Volume:%r, Gain:%r, Freq:%3.1f MHz" % (self.vol, self.gain, self.freq/1e6) # channel filter, wfm_rcv_pll chan_filt_coeffs = optfir.low_pass( 1, # gain demod_rate, # rate 80e3, # passband cutoff 115e3, # stopband cutoff 0.1, # passband ripple 60) # stopband attenuation self.chan_filt = gr.fir_filter_ccf (1, chan_filt_coeffs) self.guts = blks2.wfm_rcv_pll (demod_rate, audio_decim) self.connect(self.u, self.chan_filt, self.guts) # volume control, audio sink self.volume_control_l = gr.multiply_const_ff(self.vol) self.volume_control_r = gr.multiply_const_ff(self.vol) self.audio_sink = audio.sink(int(audio_rate), options.audio_output, False) self.connect ((self.guts, 0), self.volume_control_l, (self.audio_sink, 0)) self.connect ((self.guts, 1), self.volume_control_r, (self.audio_sink, 1)) # pilot channel filter (band-pass, 18.5-19.5kHz) pilot_filter_coeffs = gr.firdes.band_pass( 1, # gain demod_rate, # sampling rate 18.5e3, # low cutoff 19.5e3, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) self.connect(self.guts.fm_demod, self.pilot_filter) # RDS channel filter (band-pass, 54-60kHz) rds_filter_coeffs = gr.firdes.band_pass( 1, # gain demod_rate, # sampling rate 54e3, # low cutoff 60e3, # high cutoff 3e3, # transition width gr.firdes.WIN_HAMMING) self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) self.connect(self.guts.fm_demod, self.rds_filter) # create 57kHz subcarrier from 19kHz pilot, downconvert RDS channel self.mixer = gr.multiply_ff() self.connect(self.pilot_filter, (self.mixer, 0)) self.connect(self.pilot_filter, (self.mixer, 1)) self.connect(self.pilot_filter, (self.mixer, 2)) self.connect(self.rds_filter, (self.mixer, 3)) # low-pass the baseband RDS signal at 1.5kHz rds_bb_filter_coeffs = gr.firdes.low_pass( 1, # gain demod_rate, # sampling rate 1.5e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HAMMING) self.rds_bb_filter = gr.fir_filter_fff(1, rds_bb_filter_coeffs) self.connect(self.mixer, self.rds_bb_filter) # 1187.5bps = 19kHz/16 self.rds_clock = rds.freq_divider(16) clock_taps = gr.firdes.low_pass( 1, # gain demod_rate, # sampling rate 1.2e3, # passband cutoff 1.5e3, # transition width gr.firdes.WIN_HANN) self.clock_filter = gr.fir_filter_fff(1, clock_taps) self.connect(self.pilot_filter, self.rds_clock, self.clock_filter) # bpsk_demod, diff_decoder, rds_decoder self.bpsk_demod = rds.bpsk_demod(demod_rate) self.differential_decoder = gr.diff_decoder_bb(2) self.msgq = gr.msg_queue() self.rds_decoder = rds.data_decoder(self.msgq) self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) self.connect(self.clock_filter, (self.bpsk_demod, 1)) self.connect(self.bpsk_demod, self.differential_decoder) self.connect(self.differential_decoder, self.rds_decoder) # set initial values self.subdev.set_gain(self.gain) self.set_vol(self.vol) self.set_freq(self.freq)
def multiply_ff(N): op = gr.multiply_ff() tb = helper(N, op, gr.sizeof_float, gr.sizeof_float, 2, 1) return tb
def __init__(self, frame, panel, vbox, argv): stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) self.frame = frame self.panel = panel self.offset = 0.0 # Channel frequency offset parser = OptionParser(option_class=eng_option) parser.add_option( "-p", "--protocol", type="int", default=1, help="set protocol: 0 = RDLAP 19.2kbps; 1 = APCO25 (default)") parser.add_option("-g", "--gain", type="eng_float", default=1.0, help="set linear input gain (default: %default)") parser.add_option("-x", "--freq-translation", type="eng_float", default=0.0, help="initial channel frequency translation") parser.add_option( "-n", "--frame-decim", type="int", default=1, help="set oscope frame decimation factor to n [default=1]") parser.add_option( "-v", "--v-scale", type="eng_float", default=5000, help="set oscope initial V/div to SCALE [default=%default]") parser.add_option( "-t", "--t-scale", type="eng_float", default=49e-6, help="set oscope initial s/div to SCALE [default=50us]") parser.add_option( "-I", "--audio-input", type="string", default="", help="pcm input device name. E.g., hw:0,0 or /dev/dsp") parser.add_option("-r", "--sample-rate", type="eng_float", default=48000, help="set sample rate to RATE (default: %default)") parser.add_option( "-d", "--channel-decim", type="int", default=None, help= "set channel decimation factor to n [default depends on protocol]") parser.add_option("-w", "--wav-file", type="string", default=None, help="WAV input path") parser.add_option("-f", "--data-file", type="string", default=None, help="Data input path") parser.add_option("-B", "--base-band", action="store_true", default=False) parser.add_option("-R", "--repeat", action="store_true", default=False) parser.add_option("-o", "--wav-out", type="string", default=None, help="WAV output path") parser.add_option("-G", "--wav-out-gain", type="eng_float", default=0.05, help="set WAV output gain (default: %default)") parser.add_option("-F", "--data-out", type="string", default=None, help="Data output path") parser.add_option("-C", "--carrier-freq", type="eng_float", default=None, help="set data output carrier frequency to FREQ", metavar="FREQ") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.options = options if options.wav_file is not None: #try: self.input_file = gr.wavfile_source(options.wav_file, options.repeat) #except: # print "WAV file not found or not a WAV file" # sys.exit(1) print "WAV input: %i Hz, %i bits, %i channels" % ( self.input_file.sample_rate(), self.input_file.bits_per_sample(), self.input_file.channels()) self.sample_rate = self.input_file.sample_rate() self.input_stream = gr.throttle(gr.sizeof_float, self.sample_rate) self.connect(self.input_file, self.input_stream) self.src = gr.multiply_const_ff(options.gain) elif options.data_file is not None: if options.base_band: sample_size = gr.sizeof_float print "Data file is baseband (float)" self.src = gr.multiply_const_ff(options.gain) else: sample_size = gr.sizeof_gr_complex print "Data file is IF (complex)" self.src = gr.multiply_const_cc(options.gain) self.input_file = gr.file_source(sample_size, options.data_file, options.repeat) self.sample_rate = options.sample_rate # E.g. 250000 print "Data file sampling rate = " + str(self.sample_rate) self.input_stream = gr.throttle(sample_size, self.sample_rate) self.connect(self.input_file, self.input_stream) else: self.sample_rate = options.sample_rate print "Soundcard sampling rate = " + str(self.sample_rate) self.input_stream = audio.source( self.sample_rate, options.audio_input) # float samples self.src = gr.multiply_const_ff(options.gain) print "Fixed input gain = " + str(options.gain) self.connect(self.input_stream, self.src) if options.wav_out is not None: output_rate = int(self.sample_rate) if options.channel_decim is not None: output_rate /= options.channel_decim self.wav_out = gr.wavfile_sink(options.wav_out, 1, output_rate, 16) print "Opened WAV output file: " + options.wav_out + " at rate: " + str( output_rate) else: self.wav_out = None if options.data_out is not None: if options.carrier_freq is None: self.data_out = gr.file_sink(gr.sizeof_float, options.data_out) print "Opened float data output file: " + options.data_out else: self.data_out = gr.file_sink(gr.sizeof_gr_complex, options.data_out) print "Opened complex data output file: " + options.data_out else: self.data_out = None self.num_inputs = 1 input_rate = self.sample_rate #title='IF', #size=(1024,800), if (options.data_file is not None) and (options.base_band == False): self.scope = scopesink2.scope_sink_c( panel, sample_rate=input_rate, frame_decim=options.frame_decim, v_scale=options.v_scale, t_scale=options.t_scale, num_inputs=self.num_inputs) else: self.scope = scopesink2.scope_sink_f( panel, sample_rate=input_rate, frame_decim=options.frame_decim, v_scale=options.v_scale, t_scale=options.t_scale, num_inputs=self.num_inputs) #self.di = gr.deinterleave(gr.sizeof_float) #self.di = gr.complex_to_float(1) #self.di = gr.complex_to_imag() #self.dr = gr.complex_to_real() #self.connect(self.src,self.dr) #self.connect(self.src,self.di) #self.null = gr.null_sink(gr.sizeof_double) #self.connect(self.src,self.di) #self.connect((self.di,0),(self.scope,0)) #self.connect((self.di,1),(self.scope,1)) #self.connect(self.dr,(self.scope,0)) #self.connect(self.di,(self.scope,1)) self.msgq = gr.msg_queue(2) # queue that holds a maximum of 2 messages self.queue_watcher = queue_watcher(self.msgq, self.adjust_freq_norm) #------------------------------------------------------------------------------- if options.protocol == 0: # ---------- RD-LAP 19.2 kbps (9600 ksps), 25kHz channel, print "RD-LAP selected" self.symbol_rate = 9600 # symbol rate; at 2 bits/symbol this corresponds to 19.2kbps if options.channel_decim is None: self.channel_decimation = 10 # decimation (final rate should be at least several symbol rate) else: self.channel_decimation = options.channel_decim self.max_frequency_offset = 12000.0 # coarse carrier tracker leash, ~ half a channel either way self.symbol_deviation = 1200.0 # this is frequency offset from center of channel to +1 / -1 symbols self.input_sample_rate = self.sample_rate self.protocol_processing = fsk4.rdlap_f( self.msgq, 0) # desired protocol processing block selected here self.channel_rate = self.input_sample_rate / self.channel_decimation # channel selection filter characteristics channel_taps = optfir.low_pass( 1.0, # Filter gain self.input_sample_rate, # Sample rate 10000, # One-sided modulation bandwidth 12000, # One-sided channel bandwidth 0.1, # Passband ripple 60) # Stopband attenuation # symbol shaping filter characteristics symbol_coeffs = gr.firdes.root_raised_cosine( 1.0, # gain self.channel_rate, # sampling rate self.symbol_rate, # symbol rate 0.2, # alpha 500) # taps if options.protocol == 1: # ---------- APCO-25 C4FM Test Data print "APCO selected" self.symbol_rate = 4800 # symbol rate if options.channel_decim is None: self.channel_decimation = 20 # decimation else: self.channel_decimation = options.channel_decim self.max_frequency_offset = 6000.0 # coarse carrier tracker leash self.symbol_deviation = 600.0 # this is frequency offset from center of channel to +1 / -1 symbols self.input_sample_rate = self.sample_rate self.protocol_processing = fsk4.apco25_f(self.msgq, 0) self.channel_rate = self.input_sample_rate / self.channel_decimation # channel selection filter if (options.data_file is not None) and (options.base_band == False): channel_taps = optfir.low_pass( 1.0, # Filter gain self.input_sample_rate, # Sample rate 5000, # One-sided modulation bandwidth 6500, # One-sided channel bandwidth 0.2, # Passband ripple (was 0.1) 60) # Stopband attenuation else: channel_taps = None # symbol shaping filter symbol_coeffs = gr.firdes.root_raised_cosine( 1.0, # gain self.channel_rate, # sampling rate self.symbol_rate, # symbol rate 0.2, # alpha 500) # taps # ----------------- End of setup block print "Input rate = " + str(self.input_sample_rate) print "Channel decimation = " + str(self.channel_decimation) print "Channel rate = " + str(self.channel_rate) if channel_taps is not None: self.chan = gr.freq_xlating_fir_filter_ccf( self.channel_decimation, # Decimation rate channel_taps, # Filter taps 0.0, # Offset frequency self.input_sample_rate) # Sample rate if (options.freq_translation != 0): print "Channel center frequency = " + str( options.freq_translation) self.chan.set_center_freq(options.freq_translation) else: self.chan = None if options.carrier_freq is not None: print "Carrier frequency = " + str(options.carrier_freq) self.sig_carrier = gr.sig_source_c(self.channel_rate, gr.GR_COS_WAVE, options.carrier_freq, 1, 0) self.carrier_mul = gr.multiply_vcc(1) # cc(gr.sizeof_gr_complex) self.connect(self.sig_carrier, (self.carrier_mul, 0)) self.connect(self.chan, (self.carrier_mul, 1)) self.sig_i_carrier = gr.sig_source_f(self.channel_rate, gr.GR_COS_WAVE, options.carrier_freq, 1, 0) self.sig_q_carrier = gr.sig_source_f(self.channel_rate, gr.GR_COS_WAVE, options.carrier_freq, 1, (pi / 2.0)) self.carrier_i_mul = gr.multiply_ff(1) self.carrier_q_mul = gr.multiply_ff(1) self.iq_to_float = gr.complex_to_float(1) self.carrier_iq_add = gr.add_ff(1) self.connect(self.carrier_mul, self.iq_to_float) self.connect((self.iq_to_float, 0), (self.carrier_i_mul, 0)) self.connect((self.iq_to_float, 1), (self.carrier_q_mul, 0)) self.connect(self.sig_i_carrier, (self.carrier_i_mul, 1)) self.connect(self.sig_q_carrier, (self.carrier_q_mul, 1)) self.connect(self.carrier_i_mul, (self.carrier_iq_add, 0)) self.connect(self.carrier_q_mul, (self.carrier_iq_add, 1)) else: self.sig_carrier = None self.carrier_mul = None #lf_channel_taps = optfir.low_pass(1.0, # Filter gain # self.input_sample_rate, # Sample rate # 2500, # One-sided modulation bandwidth # 3250, # One-sided channel bandwidth # 0.1, # Passband ripple (0.1) # 60, # Stopband attenuation # 9) # Extra taps (default 2, which doesn't work at 48kHz) #self.lf = gr.fir_filter_fff(self.channel_decimation, lf_channel_taps) self.scope2 = scopesink2.scope_sink_f(panel, sample_rate=self.symbol_rate, frame_decim=1, v_scale=2, t_scale=0.025, num_inputs=self.num_inputs) # also note: this specifies the nominal frequency deviation for the 4-level fsk signal self.fm_demod_gain = self.channel_rate / (2.0 * pi * self.symbol_deviation) self.fm_demod = gr.quadrature_demod_cf(self.fm_demod_gain) symbol_decim = 1 self.symbol_filter = gr.fir_filter_fff(symbol_decim, symbol_coeffs) # eventually specify: sample rate, symbol rate self.demod_fsk4 = fsk4.demod_ff(self.msgq, self.channel_rate, self.symbol_rate) if (self.chan is not None): self.connect(self.src, self.chan, self.fm_demod, self.symbol_filter, self.demod_fsk4, self.protocol_processing) if options.wav_out is not None: print "WAV output gain = " + str(options.wav_out_gain) self.scaled_wav_data = gr.multiply_const_ff( float(options.wav_out_gain)) self.connect(self.scaled_wav_data, self.wav_out) if self.carrier_mul is None: self.connect(self.fm_demod, self.scaled_wav_data) else: self.connect(self.carrier_iq_add, self.scaled_wav_data) if self.data_out is not None: if self.carrier_mul is None: self.connect(self.fm_demod, self.data_out) else: self.connect(self.carrier_mul, self.data_out) # During signal, -4..4 #self.connect(self.fm_demod, self.scope2) else: self.connect(self.src, self.symbol_filter, self.demod_fsk4, self.protocol_processing) self.connect(self.src, self.scope) #self.connect(self.lf, self.scope) self.connect(self.demod_fsk4, self.scope2) #self.connect(self.symbol_filter, self.scope2) # --------------- End of most of the 4L-FSK hack & slash self._build_gui(vbox) # set initial values if options.gain is None: options.gain = 0 self.set_gain(options.gain)
def __init__(self, subc, vlen, ss): gr.hier_block2.__init__( self, "new_snr_estimator", gr.io_signature(1, 1, gr.sizeof_gr_complex * vlen), gr.io_signature(1, 1, gr.sizeof_float)) print "Created Milan's SNR estimator" trigger = [0] * vlen trigger[0] = 1 u = range(vlen / ss * (ss - 1)) zeros_ind = map(lambda z: z + 1 + z / (ss - 1), u) skip1 = skip(gr.sizeof_gr_complex, vlen) for x in zeros_ind: skip1.skip(x) #print "skipped zeros",zeros_ind v = range(vlen / ss) ones_ind = map(lambda z: z * ss, v) skip2 = skip(gr.sizeof_gr_complex, vlen) for x in ones_ind: skip2.skip(x) #print "skipped ones",ones_ind v2s = gr.vector_to_stream(gr.sizeof_gr_complex, vlen) s2v1 = gr.stream_to_vector(gr.sizeof_gr_complex, vlen / ss) trigger_src_1 = gr.vector_source_b(trigger, True) s2v2 = gr.stream_to_vector(gr.sizeof_gr_complex, vlen / ss * (ss - 1)) trigger_src_2 = gr.vector_source_b(trigger, True) mag_sq_ones = gr.complex_to_mag_squared(vlen / ss) mag_sq_zeros = gr.complex_to_mag_squared(vlen / ss * (ss - 1)) filt_ones = gr.single_pole_iir_filter_ff(0.1, vlen / ss) filt_zeros = gr.single_pole_iir_filter_ff(0.1, vlen / ss * (ss - 1)) sum_ones = vector_sum_vff(vlen / ss) sum_zeros = vector_sum_vff(vlen / ss * (ss - 1)) D = gr.divide_ff() P = gr.multiply_ff() mult1 = gr.multiply_const_ff(ss - 1.0) add1 = gr.add_const_ff(-1.0) mult2 = gr.multiply_const_ff(1. / ss) scsnrdb = gr.nlog10_ff(10, 1, 0) filt_end = gr.single_pole_iir_filter_ff(0.1) self.connect(self, v2s, skip1, s2v1, mag_sq_ones, filt_ones, sum_ones) self.connect(trigger_src_1, (skip1, 1)) self.connect(v2s, skip2, s2v2, mag_sq_zeros, filt_zeros, sum_zeros) self.connect(trigger_src_2, (skip2, 1)) self.connect(sum_ones, D) self.connect(sum_zeros, (D, 1)) self.connect(D, mult1, add1, mult2) self.connect(mult2, scsnrdb, filt_end, self)
def __init__(self, fft_length, cp_length, kstime, threshold, threshold_type, threshold_gap, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) else: moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)] self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() movingsum2_taps = [1.0 for i in range(fft_length//2)] #movingsum2_taps = [0.5 for i in range(fft_length*4)] #apurv - implementing Veljo's suggestion, when pause b/w packets if 1: self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.sub1 = gr.add_const_ff(-1) self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) #apurv - implementing Veljo's suggestion, when pause b/w packets self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) #self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # apurv-- #self.connect(self.angle, gr.delay(gr.sizeof_float, offset), (self.sample_and_hold, 0)) #apurv++ cross_correlate = 1 if cross_correlate==1: # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) # get the magnitude # self.corrmag = gr.complex_to_mag_squared() self.f2b = gr.float_to_char() self.threshold_factor = threshold #0.0012 #0.012 #0.0015 if 0: self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0, fft_length) else: #thresholds = [self.threshold_factor, 9e-5] self.slice = gr.threshold_ff(threshold, threshold, 0, fft_length, threshold_type, threshold_gap) self.connect(self.input, self.crosscorr_filter, self.corrmag, self.slice, self.f2b) # some debug dump # self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_corrmag.dat")) #self.connect(self.f2b, gr.file_sink(gr.sizeof_char, "ofdm_f2b.dat")) self.connect(self.f2b, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) #self.connect(self.pk_detect, (self,1)) #removed #self.connect(self.f2b, gr.delay(gr.sizeof_char, 1), (self, 1)) self.connect(self.f2b, (self, 1)) if logging: self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
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=91.2e6, help="set frequency to FREQ", metavar="FREQ") parser.add_option("-g", "--gain", type="eng_float", default=None, help="set gain in dB") # FIXME add squelch parser.add_option("-s", "--squelch", type="eng_float", default=0, help="set squelch level (default is 0)") 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="plughw:0,0", help="pcm device name (default is plughw:0,0)") # print help when called with wrong arguments (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) # connect to USRP usrp_decim = 250 self.u = usrp.source_c(0, usrp_decim) print "USRP Serial:", self.u.serial_number() usrp_rate = self.u.adc_rate() / usrp_decim # 256 kS/s print "usrp_rate =", usrp_rate audio_decim = 8 audio_rate = usrp_rate / audio_decim # 32 kS/s print "audio_rate =", audio_rate #if options.rx_subdev_spec is None: # options.rx_subdev_spec = usrp.pick_subdev(self.u, dblist) 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 d'board", self.subdev.side_and_name() # gain, volume, frequency self.gain = options.gain if options.gain is None: g = self.subdev.gain_range() self.gain = float(g[0]+g[1])/2 self.vol = options.volume if self.vol is None: g = self.volume_range() self.vol = float(g[0]+g[1])/2 self.freq = options.freq print "Volume:%r, Gain:%r, Freq:%3.1f MHz" % (self.vol, self.gain, self.freq/1e6) # channel filter chan_filter_coeffs = gr.firdes.low_pass( 1.0, # gain usrp_rate, # sampling rate 80e3, # passband cutoff 35e3, # transition width gr.firdes.WIN_HAMMING) self.chan_filter = gr.fir_filter_ccf(1, chan_filter_coeffs) print "# channel filter:", len(chan_filter_coeffs), "taps" # PLL-based WFM demod fm_alpha = 0.25 * 250e3 * math.pi / usrp_rate # 0.767 fm_beta = fm_alpha * fm_alpha / 4.0 # 0.147 fm_max_freq = 2.0 * math.pi * 90e3 / usrp_rate # 2.209 self.fm_demod = gr.pll_freqdet_cf( #fm_alpha, # phase gain #fm_beta, # freq gain 1.0, # Loop BW fm_max_freq, # in radians/sample -fm_max_freq) self.fm_demod.set_alpha(fm_alpha) self.fm_demod.set_beta(fm_beta) self.connect(self.u, self.chan_filter, self.fm_demod) # L+R, pilot, L-R, RDS filters lpr_filter_coeffs = gr.firdes.low_pass( 1.0, # gain usrp_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.lpr_filter = gr.fir_filter_fff(audio_decim, lpr_filter_coeffs) pilot_filter_coeffs = gr.firdes.band_pass( 1.0, # gain usrp_rate, # sampling rate 19e3-500, # low cutoff 19e3+500, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) dsbsc_filter_coeffs = gr.firdes.band_pass( 1.0, # gain usrp_rate, # sampling rate 38e3-15e3/2, # low cutoff 38e3+15e3/2, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.dsbsc_filter = gr.fir_filter_fff(1, dsbsc_filter_coeffs) rds_filter_coeffs = gr.firdes.band_pass( 1.0, # gain usrp_rate, # sampling rate 57e3-3e3, # low cutoff 57e3+3e3, # high cutoff 3e3, # transition width gr.firdes.WIN_HAMMING) self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) print "# lpr filter:", len(lpr_filter_coeffs), "taps" print "# pilot filter:", len(pilot_filter_coeffs), "taps" print "# dsbsc filter:", len(dsbsc_filter_coeffs), "taps" print "# rds filter:", len(rds_filter_coeffs), "taps" self.connect(self.fm_demod, self.lpr_filter) self.connect(self.fm_demod, self.pilot_filter) self.connect(self.fm_demod, self.dsbsc_filter) self.connect(self.fm_demod, self.rds_filter) # down-convert L-R, RDS self.stereo_baseband = gr.multiply_ff() self.connect(self.pilot_filter, (self.stereo_baseband, 0)) self.connect(self.pilot_filter, (self.stereo_baseband, 1)) self.connect(self.dsbsc_filter, (self.stereo_baseband, 2)) self.rds_baseband = gr.multiply_ff() self.connect(self.pilot_filter, (self.rds_baseband, 0)) self.connect(self.pilot_filter, (self.rds_baseband, 1)) self.connect(self.pilot_filter, (self.rds_baseband, 2)) self.connect(self.rds_filter, (self.rds_baseband, 3)) # low-pass and downsample L-R lmr_filter_coeffs = gr.firdes.low_pass( 1.0, # gain usrp_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.lmr_filter = gr.fir_filter_fff(audio_decim, lmr_filter_coeffs) self.connect(self.stereo_baseband, self.lmr_filter) # create L, R from L-R, L+R self.left = gr.add_ff() self.right = gr.sub_ff() self.connect(self.lpr_filter, (self.left, 0)) self.connect(self.lmr_filter, (self.left, 1)) self.connect(self.lpr_filter, (self.right, 0)) self.connect(self.lmr_filter, (self.right, 1)) # volume control, complex2flot, audio sink self.volume_control_l = gr.multiply_const_ff(self.vol) self.volume_control_r = gr.multiply_const_ff(self.vol) output_audio_rate = 48000 self.audio_sink = audio.sink(int(output_audio_rate), options.audio_output, False) #self.connect(self.left, self.volume_control_l, (self.audio_sink, 0)) #self.connect(self.right, self.volume_control_r, (self.audio_sink, 1)) self.resamp_L = blks2.rational_resampler_fff(interpolation=output_audio_rate,decimation=audio_rate,taps=None,fractional_bw=None,) self.resamp_R = blks2.rational_resampler_fff(interpolation=output_audio_rate,decimation=audio_rate,taps=None,fractional_bw=None,) self.connect(self.left, self.volume_control_l, self.resamp_L, (self.audio_sink, 0)) self.connect(self.right, self.volume_control_r, self.resamp_R, (self.audio_sink, 1)) # low-pass the baseband RDS signal at 1.5kHz rds_bb_filter_coeffs = gr.firdes.low_pass( 1, # gain usrp_rate, # sampling rate 1.5e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HAMMING) self.rds_bb_filter = gr.fir_filter_fff(audio_decim, rds_bb_filter_coeffs) print "# rds bb filter:", len(rds_bb_filter_coeffs), "taps" self.connect(self.rds_baseband, self.rds_bb_filter) # 1187.5bps = 19kHz/16 self.clock_divider = rds.freq_divider(16) rds_clock_taps = gr.firdes.low_pass( 1, # gain usrp_rate, # sampling rate 1.2e3, # passband cutoff 1.5e3, # transition width gr.firdes.WIN_HAMMING) self.rds_clock = gr.fir_filter_fff(audio_decim, rds_clock_taps) print "# rds clock filter:", len(rds_clock_taps), "taps" self.connect(self.pilot_filter, self.clock_divider, self.rds_clock) # bpsk_demod, diff_decoder, rds_decoder self.bpsk_demod = rds.bpsk_demod(audio_rate) self.differential_decoder = gr.diff_decoder_bb(2) self.msgq = gr.msg_queue() self.rds_decoder = rds.data_decoder(self.msgq) self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) self.connect(self.rds_clock, (self.bpsk_demod, 1)) self.connect(self.bpsk_demod, self.differential_decoder) self.connect(self.differential_decoder, self.rds_decoder) self.frame = frame self.panel = panel self._build_gui(vbox, usrp_rate, audio_rate) self.set_gain(self.gain) self.set_vol(self.vol) if not(self.set_freq(self.freq)): self._set_status_msg("Failed to set initial frequency")
def __init__(self, *args, **kwds): # begin wxGlade: MyFrame.__init__ kwds["style"] = wx.DEFAULT_FRAME_STYLE wx.Frame.__init__(self, *args, **kwds) # Menu Bar self.frame_1_menubar = wx.MenuBar() self.SetMenuBar(self.frame_1_menubar) wxglade_tmp_menu = wx.Menu() self.Exit = wx.MenuItem(wxglade_tmp_menu, ID_EXIT, "Exit", "Exit", wx.ITEM_NORMAL) wxglade_tmp_menu.AppendItem(self.Exit) self.frame_1_menubar.Append(wxglade_tmp_menu, "File") # Menu Bar end self.panel_1 = wx.Panel(self, -1) self.button_1 = wx.Button(self, ID_BUTTON_1, "LSB") self.button_2 = wx.Button(self, ID_BUTTON_2, "USB") self.button_3 = wx.Button(self, ID_BUTTON_3, "AM") self.button_4 = wx.Button(self, ID_BUTTON_4, "CW") self.button_5 = wx.ToggleButton(self, ID_BUTTON_5, "Upper") self.slider_fcutoff_hi = wx.Slider(self, ID_SLIDER_1, 0, -15798, 15799, style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.button_6 = wx.ToggleButton(self, ID_BUTTON_6, "Lower") self.slider_fcutoff_lo = wx.Slider(self, ID_SLIDER_2, 0, -15799, 15798, style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.panel_5 = wx.Panel(self, -1) self.label_1 = wx.StaticText(self, -1, " Band\nCenter") self.text_ctrl_1 = wx.TextCtrl(self, ID_TEXT_1, "") self.panel_6 = wx.Panel(self, -1) self.panel_7 = wx.Panel(self, -1) self.panel_2 = wx.Panel(self, -1) self.button_7 = wx.ToggleButton(self, ID_BUTTON_7, "Freq") self.slider_3 = wx.Slider(self, ID_SLIDER_3, 3000, 0, 6000) self.spin_ctrl_1 = wx.SpinCtrl(self, ID_SPIN_1, "", min=0, max=100) self.button_8 = wx.ToggleButton(self, ID_BUTTON_8, "Vol") self.slider_4 = wx.Slider(self, ID_SLIDER_4, 0, 0, 500) self.slider_5 = wx.Slider(self, ID_SLIDER_5, 0, 0, 20) self.button_9 = wx.ToggleButton(self, ID_BUTTON_9, "Time") self.button_11 = wx.Button(self, ID_BUTTON_11, "Rew") self.button_10 = wx.Button(self, ID_BUTTON_10, "Fwd") self.panel_3 = wx.Panel(self, -1) self.label_2 = wx.StaticText(self, -1, "PGA ") self.panel_4 = wx.Panel(self, -1) self.panel_8 = wx.Panel(self, -1) self.panel_9 = wx.Panel(self, -1) self.label_3 = wx.StaticText(self, -1, "AM Sync\nCarrier") self.slider_6 = wx.Slider(self, ID_SLIDER_6, 50, 0, 200, style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.label_4 = wx.StaticText(self, -1, "Antenna Tune") self.slider_7 = wx.Slider(self, ID_SLIDER_7, 1575, 950, 2200, style=wx.SL_HORIZONTAL | wx.SL_LABELS) self.panel_10 = wx.Panel(self, -1) self.button_12 = wx.ToggleButton(self, ID_BUTTON_12, "Auto Tune") self.button_13 = wx.Button(self, ID_BUTTON_13, "Calibrate") self.button_14 = wx.Button(self, ID_BUTTON_14, "Reset") self.panel_11 = wx.Panel(self, -1) self.panel_12 = wx.Panel(self, -1) self.__set_properties() self.__do_layout() # end wxGlade parser = OptionParser(option_class=eng_option) parser.add_option("", "--address", type="string", default="addr=192.168.10.2", help="Address of UHD device, [default=%default]") parser.add_option("-c", "--ddc-freq", type="eng_float", default=3.9e6, help="set Rx DDC frequency to FREQ", metavar="FREQ") parser.add_option( "-s", "--samp-rate", type="eng_float", default=256e3, help="set sample rate (bandwidth) [default=%default]") parser.add_option("-a", "--audio_file", default="", help="audio output file", metavar="FILE") parser.add_option("-r", "--radio_file", default="", help="radio output file", metavar="FILE") parser.add_option("-i", "--input_file", default="", help="radio input file", metavar="FILE") parser.add_option( "-O", "--audio-output", type="string", default="", help="audio output device name. E.g., hw:0,0, /dev/dsp, or pulse") (options, args) = parser.parse_args() self.usrp_center = options.ddc_freq input_rate = options.samp_rate self.slider_range = input_rate * 0.9375 self.f_lo = self.usrp_center - (self.slider_range / 2) self.f_hi = self.usrp_center + (self.slider_range / 2) self.af_sample_rate = 32000 fir_decim = long(input_rate / self.af_sample_rate) # data point arrays for antenna tuner self.xdata = [] self.ydata = [] self.tb = gr.top_block() # radio variables, initial conditions self.frequency = self.usrp_center # these map the frequency slider (0-6000) to the actual range self.f_slider_offset = self.f_lo self.f_slider_scale = 10000 self.spin_ctrl_1.SetRange(self.f_lo, self.f_hi) self.text_ctrl_1.SetValue(str(int(self.usrp_center))) self.slider_5.SetValue(0) self.AM_mode = False self.slider_3.SetValue( (self.frequency - self.f_slider_offset) / self.f_slider_scale) self.spin_ctrl_1.SetValue(int(self.frequency)) POWERMATE = True try: self.pm = powermate.powermate(self) except: sys.stderr.write("Unable to find PowerMate or Contour Shuttle\n") POWERMATE = False if POWERMATE: powermate.EVT_POWERMATE_ROTATE(self, self.on_rotate) powermate.EVT_POWERMATE_BUTTON(self, self.on_pmButton) self.active_button = 7 # command line options if options.audio_file == "": SAVE_AUDIO_TO_FILE = False else: SAVE_AUDIO_TO_FILE = True if options.radio_file == "": SAVE_RADIO_TO_FILE = False else: SAVE_RADIO_TO_FILE = True if options.input_file == "": self.PLAY_FROM_USRP = True else: self.PLAY_FROM_USRP = False if self.PLAY_FROM_USRP: self.src = uhd.usrp_source(device_addr=options.address, io_type=uhd.io_type.COMPLEX_FLOAT32, num_channels=1) self.src.set_samp_rate(input_rate) input_rate = self.src.get_samp_rate() self.src.set_center_freq(self.usrp_center, 0) self.tune_offset = 0 else: self.src = gr.file_source(gr.sizeof_short, options.input_file) self.tune_offset = 2200 # 2200 works for 3.5-4Mhz band # convert rf data in interleaved short int form to complex s2ss = gr.stream_to_streams(gr.sizeof_short, 2) s2f1 = gr.short_to_float() s2f2 = gr.short_to_float() src_f2c = gr.float_to_complex() self.tb.connect(self.src, s2ss) self.tb.connect((s2ss, 0), s2f1) self.tb.connect((s2ss, 1), s2f2) self.tb.connect(s2f1, (src_f2c, 0)) self.tb.connect(s2f2, (src_f2c, 1)) # save radio data to a file if SAVE_RADIO_TO_FILE: radio_file = gr.file_sink(gr.sizeof_short, options.radio_file) self.tb.connect(self.src, radio_file) # 2nd DDC xlate_taps = gr.firdes.low_pass ( \ 1.0, input_rate, 16e3, 4e3, gr.firdes.WIN_HAMMING ) self.xlate = gr.freq_xlating_fir_filter_ccf ( \ fir_decim, xlate_taps, self.tune_offset, input_rate ) # Complex Audio filter audio_coeffs = gr.firdes.complex_band_pass( 1.0, # gain self.af_sample_rate, # sample rate -3000, # low cutoff 0, # high cutoff 100, # transition gr.firdes.WIN_HAMMING) # window self.slider_fcutoff_hi.SetValue(0) self.slider_fcutoff_lo.SetValue(-3000) self.audio_filter = gr.fir_filter_ccc(1, audio_coeffs) # Main +/- 16Khz spectrum display self.fft = fftsink2.fft_sink_c(self.panel_2, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640, 240)) # AM Sync carrier if AM_SYNC_DISPLAY: self.fft2 = fftsink.fft_sink_c(self.tb, self.panel_9, y_per_div=20, fft_size=512, sample_rate=self.af_sample_rate, average=True, size=(640, 240)) c2f = gr.complex_to_float() # AM branch self.sel_am = gr.multiply_const_cc(0) # the following frequencies turn out to be in radians/sample # gr.pll_refout_cc(alpha,beta,min_freq,max_freq) # suggested alpha = X, beta = .25 * X * X pll = gr.pll_refout_cc(.5, .0625, (2. * math.pi * 7.5e3 / self.af_sample_rate), (2. * math.pi * 6.5e3 / self.af_sample_rate)) self.pll_carrier_scale = gr.multiply_const_cc(complex(10, 0)) am_det = gr.multiply_cc() # these are for converting +7.5kHz to -7.5kHz # for some reason gr.conjugate_cc() adds noise ?? c2f2 = gr.complex_to_float() c2f3 = gr.complex_to_float() f2c = gr.float_to_complex() phaser1 = gr.multiply_const_ff(1) phaser2 = gr.multiply_const_ff(-1) # filter for pll generated carrier pll_carrier_coeffs = gr.firdes.complex_band_pass( 2.0, # gain self.af_sample_rate, # sample rate 7400, # low cutoff 7600, # high cutoff 100, # transition gr.firdes.WIN_HAMMING) # window self.pll_carrier_filter = gr.fir_filter_ccc(1, pll_carrier_coeffs) self.sel_sb = gr.multiply_const_ff(1) combine = gr.add_ff() #AGC sqr1 = gr.multiply_ff() intr = gr.iir_filter_ffd([.004, 0], [0, .999]) offset = gr.add_const_ff(1) agc = gr.divide_ff() self.scale = gr.multiply_const_ff(0.00001) dst = audio.sink(long(self.af_sample_rate), options.audio_output) if self.PLAY_FROM_USRP: self.tb.connect(self.src, self.xlate, self.fft) else: self.tb.connect(src_f2c, self.xlate, self.fft) self.tb.connect(self.xlate, self.audio_filter, self.sel_am, (am_det, 0)) self.tb.connect(self.sel_am, pll, self.pll_carrier_scale, self.pll_carrier_filter, c2f3) self.tb.connect((c2f3, 0), phaser1, (f2c, 0)) self.tb.connect((c2f3, 1), phaser2, (f2c, 1)) self.tb.connect(f2c, (am_det, 1)) self.tb.connect(am_det, c2f2, (combine, 0)) self.tb.connect(self.audio_filter, c2f, self.sel_sb, (combine, 1)) if AM_SYNC_DISPLAY: self.tb.connect(self.pll_carrier_filter, self.fft2) self.tb.connect(combine, self.scale) self.tb.connect(self.scale, (sqr1, 0)) self.tb.connect(self.scale, (sqr1, 1)) self.tb.connect(sqr1, intr, offset, (agc, 1)) self.tb.connect(self.scale, (agc, 0)) self.tb.connect(agc, dst) if SAVE_AUDIO_TO_FILE: f_out = gr.file_sink(gr.sizeof_short, options.audio_file) sc1 = gr.multiply_const_ff(64000) f2s1 = gr.float_to_short() self.tb.connect(agc, sc1, f2s1, f_out) self.tb.start() # for mouse position reporting on fft display self.fft.win.Bind(wx.EVT_LEFT_UP, self.Mouse) # and left click to re-tune self.fft.win.Bind(wx.EVT_LEFT_DOWN, self.Click) # start a timer to check for web commands if WEB_CONTROL: self.timer = UpdateTimer(self, 1000) # every 1000 mSec, 1 Sec wx.EVT_BUTTON(self, ID_BUTTON_1, self.set_lsb) wx.EVT_BUTTON(self, ID_BUTTON_2, self.set_usb) wx.EVT_BUTTON(self, ID_BUTTON_3, self.set_am) wx.EVT_BUTTON(self, ID_BUTTON_4, self.set_cw) wx.EVT_BUTTON(self, ID_BUTTON_10, self.fwd) wx.EVT_BUTTON(self, ID_BUTTON_11, self.rew) wx.EVT_BUTTON(self, ID_BUTTON_13, self.AT_calibrate) wx.EVT_BUTTON(self, ID_BUTTON_14, self.AT_reset) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_5, self.on_button) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_6, self.on_button) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_7, self.on_button) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_8, self.on_button) wx.EVT_TOGGLEBUTTON(self, ID_BUTTON_9, self.on_button) wx.EVT_SLIDER(self, ID_SLIDER_1, self.set_filter) wx.EVT_SLIDER(self, ID_SLIDER_2, self.set_filter) wx.EVT_SLIDER(self, ID_SLIDER_3, self.slide_tune) wx.EVT_SLIDER(self, ID_SLIDER_4, self.set_volume) wx.EVT_SLIDER(self, ID_SLIDER_5, self.set_pga) wx.EVT_SLIDER(self, ID_SLIDER_6, self.am_carrier) wx.EVT_SLIDER(self, ID_SLIDER_7, self.antenna_tune) wx.EVT_SPINCTRL(self, ID_SPIN_1, self.spin_tune) wx.EVT_MENU(self, ID_EXIT, self.TimeToQuit)
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("-T", "--tx-subdev-spec", type="subdev", default=None, help="select USRP Tx side A or B") parser.add_option( "-f", "--freq", type="eng_float", default=107.2e6, help="set Tx frequency to FREQ [required]", metavar="FREQ", ) parser.add_option("--wavfile", type="string", default=None, help="open .wav audio file FILE") parser.add_option("--xml", type="string", default="rds_data.xml", help="open .xml RDS data FILE") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) usrp_interp = 500 self.u = usrp.sink_c(0, usrp_interp) print "USRP Serial: ", self.u.serial_number() usrp_rate = self.u.dac_rate() / usrp_interp # 256 kS/s # determine the daughterboard subdevice we're using if options.tx_subdev_spec is None: options.tx_subdev_spec = usrp.pick_tx_subdevice(self.u) self.u.set_mux(usrp.determine_tx_mux_value(self.u, options.tx_subdev_spec)) self.subdev = usrp.selected_subdev(self.u, options.tx_subdev_spec) print "Using d'board", self.subdev.side_and_name() # set max Tx gain, tune frequency and enable transmitter gain = self.subdev.gain_range()[1] self.subdev.set_gain(gain) print "Gain set to", gain if self.u.tune(self.subdev.which(), self.subdev, options.freq): print "Tuned to", options.freq / 1e6, "MHz" else: sys.exit(1) self.subdev.set_enable(True) # open wav file containing floats in the [-1, 1] range, repeat if options.wavfile is None: print "Please provide a wavfile to transmit! Exiting\n" sys.exit(1) self.src = gr.wavfile_source(options.wavfile, True) nchans = self.src.channels() sample_rate = self.src.sample_rate() bits_per_sample = self.src.bits_per_sample() print nchans, "channels,", sample_rate, "samples/sec,", bits_per_sample, "bits/sample" # resample to usrp rate self.resample_left = blks2.rational_resampler_fff(usrp_rate, sample_rate) self.resample_right = blks2.rational_resampler_fff(usrp_rate, sample_rate) self.connect((self.src, 0), self.resample_left) self.connect((self.src, 1), self.resample_right) # create L+R (mono) and L-R (stereo) self.audio_lpr = gr.add_ff() self.audio_lmr = gr.sub_ff() self.connect(self.resample_left, (self.audio_lpr, 0)) self.connect(self.resample_left, (self.audio_lmr, 0)) self.connect(self.resample_right, (self.audio_lpr, 1)) self.connect(self.resample_right, (self.audio_lmr, 1)) # low-pass filter for L+R audio_lpr_taps = gr.firdes.low_pass( 0.5, # gain usrp_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING, ) self.audio_lpr_filter = gr.fir_filter_fff(1, audio_lpr_taps) self.connect(self.audio_lpr, self.audio_lpr_filter) # create pilot tone at 19 kHz self.pilot = gr.sig_source_f( usrp_rate, gr.GR_SIN_WAVE, 19e3, 5e-2 # sampling rate # waveform # frequency ) # amplitude # upconvert L-R to 38 kHz and band-pass self.mix_stereo = gr.multiply_ff() audio_lmr_taps = gr.firdes.band_pass( 80, # gain usrp_rate, # sampling rate 38e3 - 15e3, # low cutoff 38e3 + 15e3, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING, ) self.audio_lmr_filter = gr.fir_filter_fff(1, audio_lmr_taps) self.connect(self.audio_lmr, (self.mix_stereo, 0)) self.connect(self.pilot, (self.mix_stereo, 1)) self.connect(self.pilot, (self.mix_stereo, 2)) self.connect(self.mix_stereo, self.audio_lmr_filter) # create RDS bitstream # diff-encode, manchester-emcode, NRZ # enforce the 1187.5bps rate # pulse shaping filter (matched with receiver) # mix with 57kHz carrier (equivalent to BPSK) self.rds_enc = rds.data_encoder("rds_data.xml") self.diff_enc = gr.diff_encoder_bb(2) self.manchester1 = gr.map_bb([1, 2]) self.manchester2 = gr.unpack_k_bits_bb(2) self.nrz = gr.map_bb([-1, 1]) self.c2f = gr.char_to_float() self.rate_enforcer = rds.rate_enforcer(usrp_rate) pulse_shaping_taps = gr.firdes.low_pass( 1, # gain usrp_rate, # sampling rate 1.5e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HAMMING, ) self.pulse_shaping = gr.fir_filter_fff(1, pulse_shaping_taps) self.bpsk_mod = gr.multiply_ff() self.connect(self.rds_enc, self.diff_enc, self.manchester1, self.manchester2, self.nrz, self.c2f) self.connect(self.c2f, (self.rate_enforcer, 0)) self.connect(self.pilot, (self.rate_enforcer, 1)) self.connect(self.rate_enforcer, (self.bpsk_mod, 0)) self.connect(self.pilot, (self.bpsk_mod, 1)) self.connect(self.pilot, (self.bpsk_mod, 2)) self.connect(self.pilot, (self.bpsk_mod, 3)) # RDS band-pass filter rds_filter_taps = gr.firdes.band_pass( 50, # gain usrp_rate, # sampling rate 57e3 - 3e3, # low cutoff 57e3 + 3e3, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING, ) self.rds_filter = gr.fir_filter_fff(1, rds_filter_taps) self.connect(self.bpsk_mod, self.rds_filter) # mix L+R, pilot, L-R and RDS self.mixer = gr.add_ff() self.connect(self.audio_lpr_filter, (self.mixer, 0)) self.connect(self.pilot, (self.mixer, 1)) self.connect(self.audio_lmr_filter, (self.mixer, 2)) self.connect(self.rds_filter, (self.mixer, 3)) # fm modulation, gain & TX max_dev = 75e3 k = 2 * math.pi * max_dev / usrp_rate # modulator sensitivity self.modulator = gr.frequency_modulator_fc(k) self.gain = gr.multiply_const_cc(5e3) self.connect(self.mixer, self.modulator, self.gain, self.u) # plot an FFT to verify we are sending what we want if 1: self.fft = fftsink2.fft_sink_f( panel, title="Pre FM modulation", fft_size=512 * 4, sample_rate=usrp_rate, y_per_div=20, ref_level=-20 ) self.connect(self.mixer, self.fft) vbox.Add(self.fft.win, 1, wx.EXPAND) if 0: self.scope = scopesink2.scope_sink_f(panel, title="RDS encoder output", sample_rate=usrp_rate) self.connect(self.rds_enc, self.scope) vbox.Add(self.scope.win, 1, wx.EXPAND)
def __init__(self, fft_length, cp_length, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) else: moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)] self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() # Modified by Yong (12.06.27) #movingsum2_taps = [1.0 for i in range(fft_length//2)] movingsum2_taps = [0.5 for i in range(fft_length)] if 1: self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.sub1 = gr.add_const_ff(-1) self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) #self.pk_detect = gr.peak_detector2_fb(9) self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # Get the power of the input signal to normalize the output of the correlation self.connect(self.input, self.inputmag2, self.inputmovingsum) self.connect(self.inputmovingsum, (self.square,0)) self.connect(self.inputmovingsum, (self.square,1)) self.connect(self.square, (self.normalize,1)) self.connect(self.c2mag, (self.normalize,0)) # Create a moving sum filter for the corr output matched_filter_taps = [1.0/cp_length for i in range(cp_length)] self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps) self.connect(self.normalize, self.matched_filter) self.connect(self.matched_filter, self.sub1, self.pk_detect) #self.connect(self.matched_filter, self.pk_detect) self.connect(self.pk_detect, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.pk_detect, (self,1)) if logging: self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-nominator_f.dat")) self.connect(self.square, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-denominator_f.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
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("-T", "--tx-subdev-spec", type="subdev", default=None, help="select USRP Tx side A or B") parser.add_option("-f", "--freq", type="eng_float", default=107.2e6, help="set Tx frequency to FREQ [required]", metavar="FREQ") parser.add_option("--wavfile", type="string", default=None, help="open .wav audio file FILE") parser.add_option("--xml", type="string", default="rds_data.xml", help="open .xml RDS data FILE") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) usrp_interp = 500 self.u = usrp.sink_c(0, usrp_interp) print "USRP Serial: ", self.u.serial_number() usrp_rate = self.u.dac_rate() / usrp_interp # 256 kS/s # determine the daughterboard subdevice we're using if options.tx_subdev_spec is None: options.tx_subdev_spec = usrp.pick_tx_subdevice(self.u) self.u.set_mux( usrp.determine_tx_mux_value(self.u, options.tx_subdev_spec)) self.subdev = usrp.selected_subdev(self.u, options.tx_subdev_spec) print "Using d'board", self.subdev.side_and_name() # set max Tx gain, tune frequency and enable transmitter gain = self.subdev.gain_range()[1] self.subdev.set_gain(gain) print "Gain set to", gain if self.u.tune(self.subdev.which(), self.subdev, options.freq): print "Tuned to", options.freq / 1e6, "MHz" else: sys.exit(1) self.subdev.set_enable(True) # open wav file containing floats in the [-1, 1] range, repeat if options.wavfile is None: print "Please provide a wavfile to transmit! Exiting\n" sys.exit(1) self.src = gr.wavfile_source(options.wavfile, True) nchans = self.src.channels() sample_rate = self.src.sample_rate() bits_per_sample = self.src.bits_per_sample() print nchans, "channels,", sample_rate, "samples/sec,", \ bits_per_sample, "bits/sample" # resample to usrp rate self.resample_left = blks2.rational_resampler_fff( usrp_rate, sample_rate) self.resample_right = blks2.rational_resampler_fff( usrp_rate, sample_rate) self.connect((self.src, 0), self.resample_left) self.connect((self.src, 1), self.resample_right) # create L+R (mono) and L-R (stereo) self.audio_lpr = gr.add_ff() self.audio_lmr = gr.sub_ff() self.connect(self.resample_left, (self.audio_lpr, 0)) self.connect(self.resample_left, (self.audio_lmr, 0)) self.connect(self.resample_right, (self.audio_lpr, 1)) self.connect(self.resample_right, (self.audio_lmr, 1)) # low-pass filter for L+R audio_lpr_taps = gr.firdes.low_pass( 0.5, # gain usrp_rate, # sampling rate 15e3, # passband cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.audio_lpr_filter = gr.fir_filter_fff(1, audio_lpr_taps) self.connect(self.audio_lpr, self.audio_lpr_filter) # create pilot tone at 19 kHz self.pilot = gr.sig_source_f( usrp_rate, # sampling rate gr.GR_SIN_WAVE, # waveform 19e3, # frequency 5e-2) # amplitude # upconvert L-R to 38 kHz and band-pass self.mix_stereo = gr.multiply_ff() audio_lmr_taps = gr.firdes.band_pass( 80, # gain usrp_rate, # sampling rate 38e3 - 15e3, # low cutoff 38e3 + 15e3, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.audio_lmr_filter = gr.fir_filter_fff(1, audio_lmr_taps) self.connect(self.audio_lmr, (self.mix_stereo, 0)) self.connect(self.pilot, (self.mix_stereo, 1)) self.connect(self.pilot, (self.mix_stereo, 2)) self.connect(self.mix_stereo, self.audio_lmr_filter) # create RDS bitstream # diff-encode, manchester-emcode, NRZ # enforce the 1187.5bps rate # pulse shaping filter (matched with receiver) # mix with 57kHz carrier (equivalent to BPSK) self.rds_enc = rds.data_encoder('rds_data.xml') self.diff_enc = gr.diff_encoder_bb(2) self.manchester1 = gr.map_bb([1, 2]) self.manchester2 = gr.unpack_k_bits_bb(2) self.nrz = gr.map_bb([-1, 1]) self.c2f = gr.char_to_float() self.rate_enforcer = rds.rate_enforcer(usrp_rate) pulse_shaping_taps = gr.firdes.low_pass( 1, # gain usrp_rate, # sampling rate 1.5e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HAMMING) self.pulse_shaping = gr.fir_filter_fff(1, pulse_shaping_taps) self.bpsk_mod = gr.multiply_ff() self.connect (self.rds_enc, self.diff_enc, self.manchester1, \ self.manchester2, self.nrz, self.c2f) self.connect(self.c2f, (self.rate_enforcer, 0)) self.connect(self.pilot, (self.rate_enforcer, 1)) self.connect(self.rate_enforcer, (self.bpsk_mod, 0)) self.connect(self.pilot, (self.bpsk_mod, 1)) self.connect(self.pilot, (self.bpsk_mod, 2)) self.connect(self.pilot, (self.bpsk_mod, 3)) # RDS band-pass filter rds_filter_taps = gr.firdes.band_pass( 50, # gain usrp_rate, # sampling rate 57e3 - 3e3, # low cutoff 57e3 + 3e3, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.rds_filter = gr.fir_filter_fff(1, rds_filter_taps) self.connect(self.bpsk_mod, self.rds_filter) # mix L+R, pilot, L-R and RDS self.mixer = gr.add_ff() self.connect(self.audio_lpr_filter, (self.mixer, 0)) self.connect(self.pilot, (self.mixer, 1)) self.connect(self.audio_lmr_filter, (self.mixer, 2)) self.connect(self.rds_filter, (self.mixer, 3)) # fm modulation, gain & TX max_dev = 75e3 k = 2 * math.pi * max_dev / usrp_rate # modulator sensitivity self.modulator = gr.frequency_modulator_fc(k) self.gain = gr.multiply_const_cc(5e3) self.connect(self.mixer, self.modulator, self.gain, self.u) # plot an FFT to verify we are sending what we want if 1: self.fft = fftsink2.fft_sink_f(panel, title="Pre FM modulation", fft_size=512 * 4, sample_rate=usrp_rate, y_per_div=20, ref_level=-20) self.connect(self.mixer, self.fft) vbox.Add(self.fft.win, 1, wx.EXPAND) if 0: self.scope = scopesink2.scope_sink_f(panel, title="RDS encoder output", sample_rate=usrp_rate) self.connect(self.rds_enc, self.scope) vbox.Add(self.scope.win, 1, wx.EXPAND)
def __init__(self, fft_length, cp_length, half_sync, logging=False): gr.hier_block2.__init__( self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3( 3, 3, # Output signature gr.sizeof_gr_complex, # delayed input gr.sizeof_float, # fine frequency offset gr.sizeof_char # timing indicator )) if half_sync: period = fft_length / 2 window = fft_length / 2 else: # full symbol period = fft_length + cp_length window = fft_length # makes the plateau cp_length long # Calculate the frequency offset from the correlation of the preamble x_corr = gr.multiply_cc() self.connect(self, gr.conjugate_cc(), (x_corr, 0)) self.connect(self, gr.delay(gr.sizeof_gr_complex, period), (x_corr, 1)) P_d = gr.moving_average_cc(window, 1.0) self.connect(x_corr, P_d) P_d_angle = gr.complex_to_arg() self.connect(P_d, P_d_angle) # Get the power of the input signal to normalize the output of the correlation R_d = gr.moving_average_ff(window, 1.0) self.connect(self, gr.complex_to_mag_squared(), R_d) R_d_squared = gr.multiply_ff() # this is retarded self.connect(R_d, (R_d_squared, 0)) self.connect(R_d, (R_d_squared, 1)) M_d = gr.divide_ff() self.connect(P_d, gr.complex_to_mag_squared(), (M_d, 0)) self.connect(R_d_squared, (M_d, 1)) # Now we need to detect peak of M_d # NOTE: replaced fir_filter with moving_average for clarity # the peak is up to cp_length long, but noisy, so average it out #matched_filter_taps = [1.0/cp_length for i in range(cp_length)] #matched_filter = gr.fir_filter_fff(1, matched_filter_taps) matched_filter = gr.moving_average_ff(cp_length, 1.0 / cp_length) # NOTE: the look_ahead parameter doesn't do anything # these parameters are kind of magic, increase 1 and 2 (==) to be more tolerant #peak_detect = raw.peak_detector_fb(0.55, 0.55, 30, 0.001) peak_detect = raw.peak_detector_fb(0.25, 0.25, 30, 0.001) # NOTE: gr.peak_detector_fb is broken! #peak_detect = gr.peak_detector_fb(0.55, 0.55, 30, 0.001) #peak_detect = gr.peak_detector_fb(0.45, 0.45, 30, 0.001) #peak_detect = gr.peak_detector_fb(0.30, 0.30, 30, 0.001) # offset by -1 self.connect(M_d, matched_filter, gr.add_const_ff(-1), peak_detect) # peak_detect indicates the time M_d is highest, which is the end of the symbol. # We should try to sample in the middle of the plateau!! # FIXME until we figure out how to do this, just offset by cp_length/2 offset = 6 #cp_length/2 # nco(t) = P_d_angle(t-offset) sampled at peak_detect(t) # modulate input(t - fft_length) by nco(t) # signal to sample input(t) at t-offset # # We can't delay by < 0 so instead: # input is delayed by fft_length # P_d_angle is delayed by offset # signal to sample is delayed by fft_length - offset # phi = gr.sample_and_hold_ff() self.connect(peak_detect, (phi, 1)) self.connect(P_d_angle, gr.delay(gr.sizeof_float, offset), (phi, 0)) #self.connect(P_d_angle, matched_filter2, (phi,0)) # why isn't this better?!? # FIXME: we add fft_length delay so that the preamble is nco corrected too # BUT is this buffering worth it? consider implementing sync as a proper block # delay the input signal to follow the frequency offset signal self.connect(self, gr.delay(gr.sizeof_gr_complex, (fft_length + offset)), (self, 0)) self.connect(phi, (self, 1)) self.connect(peak_detect, (self, 2)) if logging: self.connect(matched_filter, gr.file_sink(gr.sizeof_float, "sync-mf.dat")) self.connect(M_d, gr.file_sink(gr.sizeof_float, "sync-M.dat")) self.connect(P_d_angle, gr.file_sink(gr.sizeof_float, "sync-angle.dat")) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks.datb")) self.connect(phi, gr.file_sink(gr.sizeof_float, "sync-phi.dat"))
def __init__(self): gr.top_block.__init__(self) 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=91.2e6, help="set frequency to FREQ", metavar="FREQ") parser.add_option("-g", "--gain", type="eng_float", default=None, help="set gain in dB") parser.add_option("-s", "--squelch", type="eng_float", default=0, help="set squelch level (default is 0)") 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="plughw:0,0", help="pcm device name (default is plughw:0,0)") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) # connect to USRP usrp_decim = 250 self.u = usrp.source_c(0, usrp_decim) print "USRP Serial: ", self.u.serial_number() demod_rate = self.u.adc_rate() / usrp_decim # 256 kS/s audio_decim = 8 audio_rate = demod_rate / audio_decim # 32 kS/s if options.rx_subdev_spec is None: options.rx_subdev_spec = usrp.pick_subdev(self.u, dblist) 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 d'board", self.subdev.side_and_name() # gain, volume, frequency self.gain = options.gain if options.gain is None: self.gain = self.subdev.gain_range()[1] self.vol = options.volume if self.vol is None: g = self.volume_range() self.vol = float(g[0] + g[1]) / 2 self.freq = options.freq if abs(self.freq) < 1e6: self.freq *= 1e6 print "Volume:%r, Gain:%r, Freq:%3.1f MHz" % (self.vol, self.gain, self.freq / 1e6) # channel filter, wfm_rcv_pll chan_filt_coeffs = optfir.low_pass( 1, # gain demod_rate, # rate 80e3, # passband cutoff 115e3, # stopband cutoff 0.1, # passband ripple 60) # stopband attenuation self.chan_filt = gr.fir_filter_ccf(1, chan_filt_coeffs) self.guts = blks2.wfm_rcv_pll(demod_rate, audio_decim) self.connect(self.u, self.chan_filt, self.guts) # volume control, audio sink self.volume_control_l = gr.multiply_const_ff(self.vol) self.volume_control_r = gr.multiply_const_ff(self.vol) self.audio_sink = audio.sink(int(audio_rate), options.audio_output, False) self.connect((self.guts, 0), self.volume_control_l, (self.audio_sink, 0)) self.connect((self.guts, 1), self.volume_control_r, (self.audio_sink, 1)) # pilot channel filter (band-pass, 18.5-19.5kHz) pilot_filter_coeffs = gr.firdes.band_pass( 1, # gain demod_rate, # sampling rate 18.5e3, # low cutoff 19.5e3, # high cutoff 1e3, # transition width gr.firdes.WIN_HAMMING) self.pilot_filter = gr.fir_filter_fff(1, pilot_filter_coeffs) self.connect(self.guts.fm_demod, self.pilot_filter) # RDS channel filter (band-pass, 54-60kHz) rds_filter_coeffs = gr.firdes.band_pass( 1, # gain demod_rate, # sampling rate 54e3, # low cutoff 60e3, # high cutoff 3e3, # transition width gr.firdes.WIN_HAMMING) self.rds_filter = gr.fir_filter_fff(1, rds_filter_coeffs) self.connect(self.guts.fm_demod, self.rds_filter) # create 57kHz subcarrier from 19kHz pilot, downconvert RDS channel self.mixer = gr.multiply_ff() self.connect(self.pilot_filter, (self.mixer, 0)) self.connect(self.pilot_filter, (self.mixer, 1)) self.connect(self.pilot_filter, (self.mixer, 2)) self.connect(self.rds_filter, (self.mixer, 3)) # low-pass the baseband RDS signal at 1.5kHz rds_bb_filter_coeffs = gr.firdes.low_pass( 1, # gain demod_rate, # sampling rate 1.5e3, # passband cutoff 2e3, # transition width gr.firdes.WIN_HAMMING) self.rds_bb_filter = gr.fir_filter_fff(1, rds_bb_filter_coeffs) self.connect(self.mixer, self.rds_bb_filter) # 1187.5bps = 19kHz/16 self.rds_clock = rds.freq_divider(16) clock_taps = gr.firdes.low_pass( 1, # gain demod_rate, # sampling rate 1.2e3, # passband cutoff 1.5e3, # transition width gr.firdes.WIN_HANN) self.clock_filter = gr.fir_filter_fff(1, clock_taps) self.connect(self.pilot_filter, self.rds_clock, self.clock_filter) # bpsk_demod, diff_decoder, rds_decoder self.bpsk_demod = rds.bpsk_demod(demod_rate) self.differential_decoder = gr.diff_decoder_bb(2) self.msgq = gr.msg_queue() self.rds_decoder = rds.data_decoder(self.msgq) self.connect(self.rds_bb_filter, (self.bpsk_demod, 0)) self.connect(self.clock_filter, (self.bpsk_demod, 1)) self.connect(self.bpsk_demod, self.differential_decoder) self.connect(self.differential_decoder, self.rds_decoder) # set initial values self.subdev.set_gain(self.gain) self.set_vol(self.vol) self.set_freq(self.freq)
def __init__(self, fft_length, cp_length, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. Improved with averaging over the whole FFT and peak averaging over cp_length. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) self.inputmag2 = gr.complex_to_mag_squared() #movingsum2_taps = [0.125 for i in range(fft_length*4)] movingsum2_taps = [0.5 for i in range(fft_length*4)] self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = flex.sample_and_hold_ff() self.sub1 = gr.add_const_ff(-1) # linklab, change peak detector parameters: use higher threshold to detect rise/fall of peak #self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) self.pk_detect = flex.peak_detector_fb(0.7, 0.7, 30, 0.001) # linklab #self.pk_detect = gr.peak_detector2_fb(9) self.connect(self, self.input) # Lower branch: self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) self.connect(self.c2mag, (self.normalize,0)) # Upper branch self.connect(self.input, self.inputmag2, self.inputmovingsum) self.connect(self.inputmovingsum, (self.square,0)) self.connect(self.inputmovingsum, (self.square,1)) self.connect(self.square, (self.normalize,1)) matched_filter_taps = [1.0/cp_length for i in range(cp_length)] self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps) self.connect(self.normalize, self.matched_filter) # linklab, provide the signal power into peak detector, linklab self.connect(self.square, (self.pk_detect,1)) self.connect(self.matched_filter, self.sub1, (self.pk_detect, 0)) self.connect(self.pk_detect, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.pk_detect, (self,1)) if logging: self.connect(self.square, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-square.dat")) self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-c2mag.dat")) self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.sub1, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sub1.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
def __init__(self, fft_length, cp_length, half_sync, logging=False): gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3(3, 3, # Output signature gr.sizeof_gr_complex, # delayed input gr.sizeof_float, # fine frequency offset gr.sizeof_char # timing indicator )) if half_sync: period = fft_length/2 window = fft_length/2 else: # full symbol period = fft_length + cp_length window = fft_length # makes the plateau cp_length long # Calculate the frequency offset from the correlation of the preamble x_corr = gr.multiply_cc() self.connect(self, gr.conjugate_cc(), (x_corr, 0)) self.connect(self, gr.delay(gr.sizeof_gr_complex, period), (x_corr, 1)) P_d = gr.moving_average_cc(window, 1.0) self.connect(x_corr, P_d) P_d_angle = gr.complex_to_arg() self.connect(P_d, P_d_angle) # Get the power of the input signal to normalize the output of the correlation R_d = gr.moving_average_ff(window, 1.0) self.connect(self, gr.complex_to_mag_squared(), R_d) R_d_squared = gr.multiply_ff() # this is retarded self.connect(R_d, (R_d_squared, 0)) self.connect(R_d, (R_d_squared, 1)) M_d = gr.divide_ff() self.connect(P_d, gr.complex_to_mag_squared(), (M_d, 0)) self.connect(R_d_squared, (M_d, 1)) # Now we need to detect peak of M_d # NOTE: replaced fir_filter with moving_average for clarity # the peak is up to cp_length long, but noisy, so average it out #matched_filter_taps = [1.0/cp_length for i in range(cp_length)] #matched_filter = gr.fir_filter_fff(1, matched_filter_taps) matched_filter = gr.moving_average_ff(cp_length, 1.0/cp_length) # NOTE: the look_ahead parameter doesn't do anything # these parameters are kind of magic, increase 1 and 2 (==) to be more tolerant #peak_detect = raw.peak_detector_fb(0.55, 0.55, 30, 0.001) peak_detect = raw.peak_detector_fb(0.25, 0.25, 30, 0.001) # NOTE: gr.peak_detector_fb is broken! #peak_detect = gr.peak_detector_fb(0.55, 0.55, 30, 0.001) #peak_detect = gr.peak_detector_fb(0.45, 0.45, 30, 0.001) #peak_detect = gr.peak_detector_fb(0.30, 0.30, 30, 0.001) # offset by -1 self.connect(M_d, matched_filter, gr.add_const_ff(-1), peak_detect) # peak_detect indicates the time M_d is highest, which is the end of the symbol. # We should try to sample in the middle of the plateau!! # FIXME until we figure out how to do this, just offset by cp_length/2 offset = 6 #cp_length/2 # nco(t) = P_d_angle(t-offset) sampled at peak_detect(t) # modulate input(t - fft_length) by nco(t) # signal to sample input(t) at t-offset # # We can't delay by < 0 so instead: # input is delayed by fft_length # P_d_angle is delayed by offset # signal to sample is delayed by fft_length - offset # phi = gr.sample_and_hold_ff() self.connect(peak_detect, (phi,1)) self.connect(P_d_angle, gr.delay(gr.sizeof_float, offset), (phi,0)) #self.connect(P_d_angle, matched_filter2, (phi,0)) # why isn't this better?!? # FIXME: we add fft_length delay so that the preamble is nco corrected too # BUT is this buffering worth it? consider implementing sync as a proper block # delay the input signal to follow the frequency offset signal self.connect(self, gr.delay(gr.sizeof_gr_complex, (fft_length+offset)), (self,0)) self.connect(phi, (self,1)) self.connect(peak_detect, (self,2)) if logging: self.connect(matched_filter, gr.file_sink(gr.sizeof_float, "sync-mf.dat")) self.connect(M_d, gr.file_sink(gr.sizeof_float, "sync-M.dat")) self.connect(P_d_angle, gr.file_sink(gr.sizeof_float, "sync-angle.dat")) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks.datb")) self.connect(phi, gr.file_sink(gr.sizeof_float, "sync-phi.dat"))