def setup_radiometer_common(self,n): # The IIR integration filter for post-detection self.integrator = gr.single_pole_iir_filter_ff(1.0) self.integrator.set_taps (1.0/self.bw) if (self.use_notches == True): self.compute_notch_taps(self.notches) if (n == 2): self.notch_filt1 = gr.fft_filter_ccc(1, self.notch_taps) self.notch_filt2 = gr.fft_filter_ccc(1, self.notch_taps) else: self.notch_filt = gr.fft_filter_ccc(1, self.notch_taps) # Signal probe self.probe = gr.probe_signal_f() # # Continuum calibration stuff # x = self.calib_coeff/100.0 self.cal_mult = gr.multiply_const_ff(self.calib_coeff/100.0) self.cal_offs = gr.add_const_ff(self.calib_offset*(x*8000)) # # Mega decimator after IIR filter # if (self.switch_mode == False): self.keepn = gr.keep_one_in_n(gr.sizeof_float, self.bw) else: self.keepn = gr.keep_one_in_n(gr.sizeof_float, int(self.bw/2)) # # For the Dicke-switching scheme # #self.switch = gr.multiply_const_ff(1.0) # if (self.switch_mode == True): self.vector = gr.vector_sink_f() self.swkeep = gr.keep_one_in_n(gr.sizeof_float, int(self.bw/3)) self.mute = gr.keep_one_in_n(gr.sizeof_float, 1) self.cmute = gr.keep_one_in_n(gr.sizeof_float, int(1.0e9)) self.cintegrator = gr.single_pole_iir_filter_ff(1.0/(self.bw/2)) self.cprobe = gr.probe_signal_f() else: self.mute = gr.multiply_const_ff(1.0) self.avg_reference_value = 0.0 self.reference_level = gr.add_const_ff(0.0)
def __init__ ( self, noise_power, coherence_time, taps ): gr.hier_block2.__init__(self, "fading_channel", gr.io_signature( 1, 1, gr.sizeof_gr_complex ), gr.io_signature( 1, 1, gr.sizeof_gr_complex ) ) inp = gr.kludge_copy( gr.sizeof_gr_complex ) self.connect( self, inp ) tap1_delay = gr.delay( gr.sizeof_gr_complex, 1 ) tap2_delay = gr.delay( gr.sizeof_gr_complex, 2 ) self.connect( inp, tap1_delay ) self.connect( inp, tap2_delay ) fd = 100 z = numpy.arange(-fd+0.1,fd,0.1) t = numpy.sqrt(1. / ( pi * fd * numpy.sqrt( 1. - (z/fd)**2 ) ) ) tap1_noise = ofdm.complex_white_noise( 0.0, taps[0] ) tap1_filter = gr.fft_filter_ccc(1, t) self.connect( tap1_noise, tap1_filter ) tap1_mult = gr.multiply_cc() self.connect( tap1_filter, (tap1_mult,0) ) self.connect( tap1_delay, (tap1_mult,1) ) tap2_noise = ofdm.complex_white_noise( 0.0, taps[1] ) tap2_filter = gr.fft_filter_ccc(1, t) self.connect( tap2_noise, tap2_filter ) tap2_mult = gr.multiply_cc() self.connect( tap2_filter, (tap2_mult,0) ) self.connect( tap2_delay, (tap2_mult,1) ) noise_src = ofdm.complex_white_noise( 0.0, sqrt( noise_power ) ) chan = gr.add_cc() self.connect( inp, (chan,0) ) self.connect( tap1_mult, (chan,1) ) self.connect( tap2_mult, (chan,2) ) self.connect( noise_src, (chan,3) ) self.connect( chan, self ) log_to_file( self, tap1_filter, "data/tap1_filter.compl") log_to_file( self, tap1_filter, "data/tap1_filter.float", mag=True) log_to_file( self, tap2_filter, "data/tap2_filter.compl") log_to_file( self, tap2_filter, "data/tap2_filter.float", mag=True)
def __init__(self, noise_power, coherence_time, taps): gr.hier_block2.__init__(self, "fading_channel", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_gr_complex)) inp = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, inp) tap1_delay = gr.delay(gr.sizeof_gr_complex, 1) tap2_delay = gr.delay(gr.sizeof_gr_complex, 2) self.connect(inp, tap1_delay) self.connect(inp, tap2_delay) fd = 100 z = numpy.arange(-fd + 0.1, fd, 0.1) t = numpy.sqrt(1. / (pi * fd * numpy.sqrt(1. - (z / fd)**2))) tap1_noise = ofdm.complex_white_noise(0.0, taps[0]) tap1_filter = gr.fft_filter_ccc(1, t) self.connect(tap1_noise, tap1_filter) tap1_mult = gr.multiply_cc() self.connect(tap1_filter, (tap1_mult, 0)) self.connect(tap1_delay, (tap1_mult, 1)) tap2_noise = ofdm.complex_white_noise(0.0, taps[1]) tap2_filter = gr.fft_filter_ccc(1, t) self.connect(tap2_noise, tap2_filter) tap2_mult = gr.multiply_cc() self.connect(tap2_filter, (tap2_mult, 0)) self.connect(tap2_delay, (tap2_mult, 1)) noise_src = ofdm.complex_white_noise(0.0, sqrt(noise_power)) chan = gr.add_cc() self.connect(inp, (chan, 0)) self.connect(tap1_mult, (chan, 1)) self.connect(tap2_mult, (chan, 2)) self.connect(noise_src, (chan, 3)) self.connect(chan, self) log_to_file(self, tap1_filter, "data/tap1_filter.compl") log_to_file(self, tap1_filter, "data/tap1_filter.float", mag=True) log_to_file(self, tap2_filter, "data/tap2_filter.compl") log_to_file(self, tap2_filter, "data/tap2_filter.float", mag=True)
def __init__(self, demod_class, rx_callback, options): gr.hier_block2.__init__(self, "receive_path", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(0, 0, 0)) options = copy.copy(options) # make a copy so we can destructively modify self._verbose = options.verbose self._bitrate = options.bitrate # desired bit rate self._rx_callback = rx_callback # this callback is fired when a packet arrives self._demod_class = demod_class # the demodulator_class we're using self._chbw_factor = options.chbw_factor # channel filter bandwidth factor # Get demod_kwargs demod_kwargs = self._demod_class.extract_kwargs_from_options(options) # Build the demodulator self.demodulator = self._demod_class(**demod_kwargs) # Make sure the channel BW factor is between 1 and sps/2 # or the filter won't work. if(self._chbw_factor < 1.0 or self._chbw_factor > self.samples_per_symbol()/2): sys.stderr.write("Channel bandwidth factor ({0}) must be within the range [1.0, {1}].\n".format(self._chbw_factor, self.samples_per_symbol()/2)) sys.exit(1) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass (1.0, # gain sw_decim * self.samples_per_symbol(), # sampling rate self._chbw_factor, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # receiver self.packet_receiver = \ digital.demod_pkts(self.demodulator, access_code=None, callback=self._rx_callback, threshold=-1) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) # Display some information about the setup if self._verbose: self._print_verbage() # connect block input to channel filter self.connect(self, self.channel_filter) # connect the channel input filter to the carrier power detector self.connect(self.channel_filter, self.probe) # connect channel filter to the packet receiver self.connect(self.channel_filter, self.packet_receiver)
def __init__(self, options, rx_callback): phy_base.__init__(self, options, rx_callback) # Receive chain demod_class = phy_psk.DEMODS[options.psk_modulation] demod_kwargs = demod_class.extract_kwargs_from_options(options) self._rx_demod = blks2.demod_pkts( demod_class(**demod_kwargs), callback=self._rx_callback) sw_decim = 1 self._rx_chan_filt = gr.fft_filter_ccc( sw_decim, gr.firdes.low_pass ( 1.0, # gain sw_decim * self._rx_demod._demodulator.samples_per_symbol(), # sampling rate 1.0, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN)) # filter type self.connect(self, self._rx_chan_filt, self._rx_demod) # Trasmit chain mod_class = phy_psk.MODS[options.psk_modulation] mod_kwargs = mod_class.extract_kwargs_from_options(options) self._tx_mod = blks2.mod_pkts( mod_class(**mod_kwargs), pad_for_usrp=True) self.connect(self._tx_mod, gr.kludge_copy(gr.sizeof_gr_complex), self)
def reset_filter(self): bw = (float(self._occupied_tones) / float(self._fft_length)) / 2.0 tb = bw*0.06 chan_coeffs = gr.firdes.low_pass (1.0, # gain 1.0, # sampling rate bw+tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs)
def test_ccc_get0(self): random.seed(0) for i in xrange(25): ntaps = int(random.uniform(2, 100)) taps = make_random_complex_tuple(ntaps) op = gr.fft_filter_ccc(1, taps) result_data = op.taps() #print result_data self.assertComplexTuplesAlmostEqual(taps, result_data, 4)
def __init__(self, demod_class, rx_callback, options): gr.hier_block2.__init__( self, "receive_path", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(0, 0, 0)) # Output signature options = copy.copy( options) # make a copy so we can destructively modify self._verbose = options.verbose self._bitrate = options.bitrate # desired bit rate self._samples_per_symbol = options.samples_per_symbol # desired samples/symbol self._rx_callback = rx_callback # this callback is fired when there's a packet available self._demod_class = demod_class # the demodulator_class we're using # Get demod_kwargs demod_kwargs = self._demod_class.extract_kwargs_from_options(options) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass( 1.0, # gain sw_decim * self._samples_per_symbol, # sampling rate 1.0, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # receiver self.packet_receiver = \ blks2.demod_pkts(self._demod_class(**demod_kwargs), access_code=None, callback=self._rx_callback, threshold=-1) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.probe = gr.probe_avg_mag_sqrd_c(thresh, alpha) # Display some information about the setup if self._verbose: self._print_verbage() # connect block input to channel filter self.connect(self, self.channel_filter) # connect the channel input filter to the carrier power detector self.connect(self.channel_filter, self.probe) # connect channel filter to the packet receiver self.connect(self.channel_filter, self.packet_receiver)
def __init__(self, principal_gui, rx_callback, options): gr.hier_block2.__init__(self, "receive_path", gr.io_signature(0,0,0), # the 1,1 indicate the minimum, maximum stream in input gr.io_signature(0,0,0)) # the 0,0 indicate the minimum, maximum stream in output #local setup usrp source creat self.usrp source self._setup_usrp_source(options) options = copy.copy(options) # Make copy of the received options self._verbose = options.verbose self._bitrate = options.rate # The bit rate transmission self._samples_per_symbol= options.samples_per_symbol # samples/sample self._rx_callback = rx_callback #This callback is fired (declanche) when there's packet is available self.demodulator = bpsk_demodulator(principal_gui, options) #The demodulator used #Designe filter to get actual channel we want sw_decim = 1 #Create the low pass filter chan_coeffs = gr.firdes.low_pass (1.0, #gain sw_decim * self._samples_per_symbol, #sampling rate 1.0, #midpoint of trans band 0.5, #width of trans band gr.firdes.WIN_HAMMING) #filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) self.packet_receiver = ieee_pkt_receiver.ieee_pkt_receiver_868_915(self, gui = principal_gui, demodulator = self.demodulator, callback = rx_callback, sps = self._samples_per_symbol, symbol_rate = self._bitrate, threshold = -1) #Carrier Sensing Block (ecoute du canal) alpha = 0.001 # Carrier Sensing with dB, will have to adjust thresh = 30 #construct analyser of carrier (c'est un sink) self.probe = gr.probe_avg_mag_sqrd_c(thresh, alpha) #display information about the setup if self._verbose: self._print_verbage() #self.squelch = gr.pwr_squelch_cc(50, 1, 0, True) #connect the input block to channel filter #connect the blocks with usrp, the self.usrp is created in _setup_usrp_source self.squelch = gr.pwr_squelch_cc(50, 1, 0, True) self.connect(self._usrp, self.packet_receiver)
def __init__(self, demod_class, rx_callback, options): gr.hier_block2.__init__( self, "receive_path", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(0, 0, 0), ) # Output signature options = copy.copy(options) # make a copy so we can destructively modify self._verbose = options.verbose self._bitrate = options.bitrate # desired bit rate self._samples_per_symbol = options.samples_per_symbol # desired samples/symbol self._rx_callback = rx_callback # this callback is fired when there's a packet available self._demod_class = demod_class # the demodulator_class we're using # Get demod_kwargs demod_kwargs = self._demod_class.extract_kwargs_from_options(options) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass( 1.0, # gain sw_decim * self._samples_per_symbol, # sampling rate 1.0, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN, ) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # receiver self.packet_receiver = blks22.demod_pkts( self._demod_class(**demod_kwargs), access_code=None, callback=self._rx_callback, threshold=-1 ) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.probe = gr.probe_avg_mag_sqrd_c(thresh, alpha) # Display some information about the setup if self._verbose: self._print_verbage() # connect block input to channel filter self.connect(self, self.channel_filter) # connect the channel input filter to the carrier power detector self.connect(self.channel_filter, self.probe) # connect channel filter to the packet receiver self.connect(self.channel_filter, self.packet_receiver)
def __init__(self, fft_length, occupied_tones, carrier_map_bin): """ Hierarchical block for receiving OFDM symbols. """ gr.hier_block2.__init__(self, "ncofdm_filt", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_gr_complex)) # Input signature # fft length, e.g. 256 self._fft_length = fft_length # the number of used subcarriers, e.g. 240 self._occupied_tones = occupied_tones # a binary array indicates the used subcarriers self._carrier_map_bin = carrier_map_bin # setup filter banks self.chan_filt_low = gr.fft_filter_ccc(1,[1]) self.chan_filt_high1 = gr.fft_filter_ccc(1,[1]) self.chan_filt_high2 = gr.fft_filter_ccc(1,[1]) self.chan_filt_high3 = gr.fft_filter_ccc(1,[1]) self.chan_filt_high4 = gr.fft_filter_ccc(1,[1]) self.chan_filt_high5 = gr.fft_filter_ccc(1,[1]) # calculate the filter taps filt_num = self.calc_filter_taps(2, 0) # signals run into a serial of filters, one lowpass filter and 5 highpass filters self.connect(self, self.chan_filt_high1, self.chan_filt_high2, self.chan_filt_high3, self.chan_filt_high4, self.chan_filt_high5, self.chan_filt_low, self)
def test_ccc_001(self): src_data = (0,1,2,3,4,5,6,7) taps = (1,) expected_result = tuple([complex(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_c(src_data) op = gr.fft_filter_ccc(1, taps) dst = gr.vector_sink_c() self.fg.connect(src, op, dst) self.fg.run() result_data = dst.data() #print 'expected:', expected_result #print 'results: ', result_data self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
def test_ccc_001(self): tb = gr.top_block() src_data = (0,1,2,3,4,5,6,7) taps = (1,) expected_result = tuple([complex(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_c(src_data) op = gr.fft_filter_ccc(1, taps) dst = gr.vector_sink_c() tb.connect(src, op, dst) tb.run() result_data = dst.data() #print 'expected:', expected_result #print 'results: ', result_data self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
def test_ccc_002(self): # Test nthreads tb = gr.top_block() src_data = (0,1,2,3,4,5,6,7) taps = (2,) nthreads = 2 expected_result = tuple([2 * complex(x) for x in (0,1,2,3,4,5,6,7)]) src = gr.vector_source_c(src_data) op = gr.fft_filter_ccc(1, taps, nthreads) dst = gr.vector_sink_c() tb.connect(src, op, dst) tb.run() result_data = dst.data() #print 'expected:', expected_result #print 'results: ', result_data self.assertComplexTuplesAlmostEqual (expected_result, result_data, 5)
def __init__(self, mpoints, taps=None): """ Takes 1 complex stream in, produces M complex streams out that runs at 1/M times the input sample rate @param mpoints: number of freq bins/interpolation factor/subbands @param taps: filter taps for subband filter Same channel to frequency mapping as described above. """ item_size = gr.sizeof_gr_complex gr.hier_block2.__init__( self, "analysis_filterbank", gr.io_signature(1, 1, item_size), # Input signature gr.io_signature(mpoints, mpoints, item_size), ) # Output signature if taps is None: taps = _generate_synthesis_taps(mpoints) # pad taps to multiple of mpoints r = len(taps) % mpoints if r != 0: taps = taps + (mpoints - r) * (0,) # split in mpoints separate set of taps sub_taps = _split_taps(taps, mpoints) # print >> sys.stderr, "mpoints =", mpoints, "len(sub_taps) =", len(sub_taps) self.s2ss = gr.stream_to_streams(item_size, mpoints) # filters here self.ss2v = gr.streams_to_vector(item_size, mpoints) self.fft = gr.fft_vcc(mpoints, True, []) self.v2ss = gr.vector_to_streams(item_size, mpoints) self.connect(self, self.s2ss) # build mpoints fir filters... for i in range(mpoints): f = gr.fft_filter_ccc(1, sub_taps[mpoints - i - 1]) self.connect((self.s2ss, i), f) self.connect(f, (self.ss2v, i)) self.connect((self.v2ss, i), (self, i)) self.connect(self.ss2v, self.fft, self.v2ss)
def __init__(self, mpoints, taps=None): """ Takes 1 complex stream in, produces M complex streams out that runs at 1/M times the input sample rate @param mpoints: number of freq bins/interpolation factor/subbands @param taps: filter taps for subband filter Same channel to frequency mapping as described above. """ item_size = gr.sizeof_gr_complex gr.hier_block2.__init__( self, "analysis_filterbank", gr.io_signature(1, 1, item_size), # Input signature gr.io_signature(mpoints, mpoints, item_size)) # Output signature if taps is None: taps = _generate_synthesis_taps(mpoints) # pad taps to multiple of mpoints r = len(taps) % mpoints if r != 0: taps = taps + (mpoints - r) * (0, ) # split in mpoints separate set of taps sub_taps = _split_taps(taps, mpoints) # print >> sys.stderr, "mpoints =", mpoints, "len(sub_taps) =", len(sub_taps) self.s2ss = gr.stream_to_streams(item_size, mpoints) # filters here self.ss2v = gr.streams_to_vector(item_size, mpoints) self.fft = gr.fft_vcc(mpoints, True, []) self.v2ss = gr.vector_to_streams(item_size, mpoints) self.connect(self, self.s2ss) # build mpoints fir filters... for i in range(mpoints): f = gr.fft_filter_ccc(1, sub_taps[mpoints - i - 1]) self.connect((self.s2ss, i), f) self.connect(f, (self.ss2v, i)) self.connect((self.v2ss, i), (self, i)) self.connect(self.ss2v, self.fft, self.v2ss)
def test_ccc_004(self): random.seed(0) for i in xrange(25): # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) src_len = 4*1024 src_data = make_random_complex_tuple(src_len) ntaps = int(random.uniform(2, 1000)) taps = make_random_complex_tuple(ntaps) expected_result = reference_filter_ccc(1, taps, src_data) src = gr.vector_source_c(src_data) op = gr.fft_filter_ccc(1, taps) dst = gr.vector_sink_c() self.fg.connect(src, op, dst) self.fg.run() result_data = dst.data() self.assert_fft_ok2(expected_result, result_data)
def __init__(self, center_freq, bandwidth, samp_rate): gr.hier_block2.__init__(self, "linker", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_gr_complex)) self.coswave = gr.sig_source_c(samp_rate, gr.GR_COS_WAVE, -center_freq, 1, 0) self.multiply = gr.multiply_vcc(1) midpoint = bandwidth width = 20 stopband_attenuation = 10 decim = int(samp_rate / bandwidth / 4) taps = gr.firdes.low_pass_2(decim, samp_rate, midpoint, width, stopband_attenuation) self.lpf = gr.fft_filter_ccc(decim, taps) self.connect(self, (self.multiply, 0)) self.connect(self.coswave, (self.multiply, 1)) self.connect(self.multiply, self.lpf, self) self.samp_rate = 1.0*samp_rate/decim
def test_ccc_004(self): random.seed(0) for i in xrange(25): # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) src_len = 4*1024 src_data = make_random_complex_tuple(src_len) ntaps = int(random.uniform(2, 1000)) taps = make_random_complex_tuple(ntaps) expected_result = reference_filter_ccc(1, taps, src_data) src = gr.vector_source_c(src_data) op = gr.fft_filter_ccc(1, taps) dst = gr.vector_sink_c() tb = gr.top_block() tb.connect(src, op, dst) tb.run() result_data = dst.data() del tb self.assert_fft_ok2(expected_result, result_data)
def test_ccc_006(self): # Test decimating with nthreads=2 random.seed(0) nthreads = 2 for i in xrange(25): # sys.stderr.write("\n>>> Loop = %d\n" % (i,)) dec = i + 1 src_len = 4*1024 src_data = make_random_complex_tuple(src_len) ntaps = int(random.uniform(2, 100)) taps = make_random_complex_tuple(ntaps) expected_result = reference_filter_ccc(dec, taps, src_data) src = gr.vector_source_c(src_data) op = gr.fft_filter_ccc(dec, taps, nthreads) dst = gr.vector_sink_c() tb = gr.top_block() tb.connect(src, op, dst) tb.run() del tb result_data = dst.data() self.assert_fft_ok2(expected_result, result_data)
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, bandwidth, fft_length, cp_length, occupied_tones, snr, ks, logging=False, mode='benchmark'): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param fft_length: total number of subcarriers @type fft_length: int @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) @type cp_length: int @param occupied_tones: number of subcarriers used for data @type occupied_tones: int @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer @type snr: float @param ks: known symbols used as preambles to each packet @type ks: list of lists @param logging: turn file logging on or off @type logging: bool """ gr.hier_block2.__init__(self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char)) # Output signature bw = (float(occupied_tones) / float(fft_length)) / 2.0 tb = bw*0.08 chan_coeffs = gr.firdes.low_pass (1.0, # gain 1.0, # sampling rate bw+tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) win = [1 for i in range(fft_length)] zeros_on_left = int(math.ceil((fft_length - occupied_tones)/2.0)) ks0 = fft_length*[0,] ks0[zeros_on_left : zeros_on_left + occupied_tones] = ks[0] ks0 = fft.ifftshift(ks0) ks0time = fft.ifft(ks0) # ADD SCALING FACTOR ks0time = ks0time.tolist() SYNC = "pn" if SYNC == "ml": nco_sensitivity = -1.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_ml(fft_length, cp_length, snr, ks0time, logging) elif SYNC == "pn": nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pn(fft_length, cp_length, logging, mode) elif SYNC == "pnac": nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pnac(fft_length, cp_length, ks0time, logging) # for testing only; do not user over the air # remove filter and filter delay for this elif SYNC == "fixed": self.chan_filt = gr.multiply_const_cc(1.0) nsymbols = 18 # enter the number of symbols per packet freq_offset = 0.0 # if you use a frequency offset, enter it here nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_fixed(fft_length, cp_length, nsymbols, freq_offset, logging) # Set up blocks self.nco = gr.frequency_modulator_fc(nco_sensitivity) # generate a signal proportional to frequency error of sync block self.sigmix = gr.multiply_cc() self.sampler = digital_swig.ofdm_sampler(fft_length, fft_length+cp_length, long(bandwidth)) self.fft_demod = gr.fft_vcc(fft_length, True, win, True) self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition(occupied_tones, fft_length, cp_length, ks[0]) self.connect(self, self.chan_filt) # filter the input channel self.connect(self.chan_filt, self.ofdm_sync) # into the synchronization alg. self.connect((self.ofdm_sync,0), self.nco, (self.sigmix,1)) # use sync freq. offset output to derotate input signal self.connect(self.chan_filt, (self.sigmix,0)) # signal to be derotated self.connect(self.sigmix, (self.sampler,0)) # sample off timing signal detected in sync alg self.connect((self.ofdm_sync,1), (self.sampler,1)) # timing signal to sample at self.connect((self.sampler,0), self.fft_demod) # send derotated sampled signal to FFT self.connect(self.fft_demod, (self.ofdm_frame_acq,0)) # find frame start and equalize signal self.connect((self.sampler,1), (self.ofdm_frame_acq,1)) # send timing signal to signal frame start self.connect((self.ofdm_frame_acq,0), (self,0)) # finished with fine/coarse freq correction, self.connect((self.ofdm_frame_acq,1), (self,1)) # frame and symbol timing, and equalization # for debugging self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) if logging: self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-fft_out_c.dat")) self.connect(self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_receiver-frame_acq_c.dat")) self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat")) self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat")) self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat"))
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, options): gr.top_block.__init__(self) options = copy.copy(options) symbol_rate = 2500000 print "%f" % (options.tx_freq-1.25e6) #print "==========================================\n" #print "Samp per sym = %d\n" % options.samples_per_symbol #print "==========================================\n" self.source = uhd_receiver(options.args, symbol_rate, 1, #options.samples_per_symbol, options.tx_freq-1.25e6, 30, options.spec, options.antenna, options.verbose) low_pass_taps = [ -0.0401, 0.0663, 0.0468, -0.0235, -0.0222, 0.0572, 0.0299, -0.1001, -0.0294, 0.3166, 0.5302, 0.3166, -0.0294, -0.1001, 0.0299, 0.0572, -0.0222, -0.0235, 0.0468, 0.0663, -0.0401] high_pass_taps = [ -0.0389, -0.0026, 0.0302, 0.0181, -0.0357, -0.0394, 0.0450, 0.0923, -0.0472, -0.3119, 0.5512, -0.3119, -0.0472, 0.0923, 0.0450, -0.0394, -0.0357, 0.0181, 0.0302, -0.0026, -0.0389] # Carrier Sensing Blocks alpha = 0.5 thresh = 30 # in dB, will have to adjust self.probe_lp = gr.probe_avg_mag_sqrd_c(thresh,alpha) self.probe_hp = gr.probe_avg_mag_sqrd_c(thresh,alpha) self.lp = gr.fft_filter_ccc(32, low_pass_taps) self.hp = gr.fft_filter_ccc(32, high_pass_taps) self.connect(self.source, self.lp) self.connect(self.lp, self.probe_lp) self.connect(self.source, self.hp) self.connect(self.hp, self.probe_hp)
def __init__(self, frame, panel, vbox, argv): stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) self.frame = frame self.panel = panel parser = OptionParser(option_class=eng_option) parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), help="select USRP Rx side A or B (default=A)") parser.add_option( "-d", "--decim", type="int", default=16, help="set fgpa decimation rate to DECIM [default=%default]") parser.add_option("-f", "--freq", type="eng_float", default=None, help="set frequency to FREQ", metavar="FREQ") parser.add_option("-Q", "--observing", type="eng_float", default=0.0, help="set observing frequency to FREQ") parser.add_option("-a", "--avg", type="eng_float", default=1.0, help="set spectral averaging alpha") parser.add_option("-V", "--favg", type="eng_float", default=2.0, help="set folder averaging alpha") parser.add_option("-g", "--gain", type="eng_float", default=None, help="set gain in dB (default is midpoint)") parser.add_option("-l", "--reflevel", type="eng_float", default=30.0, help="Set pulse display reference level") parser.add_option("-L", "--lowest", type="eng_float", default=1.5, help="Lowest valid frequency bin") parser.add_option("-e", "--longitude", type="eng_float", default=-76.02, help="Set Observer Longitude") parser.add_option("-c", "--latitude", type="eng_float", default=44.85, help="Set Observer Latitude") parser.add_option("-F", "--fft_size", type="eng_float", default=1024, help="Size of FFT") parser.add_option("-t", "--threshold", type="eng_float", default=2.5, help="pulsar threshold") parser.add_option("-p", "--lowpass", type="eng_float", default=100, help="Pulse spectra cutoff freq") parser.add_option("-P", "--prefix", default="./", help="File prefix") parser.add_option("-u", "--pulsefreq", type="eng_float", default=0.748, help="Observation pulse rate") parser.add_option("-D", "--dm", type="eng_float", default=1.0e-5, help="Dispersion Measure") parser.add_option("-O", "--doppler", type="eng_float", default=1.0, help="Doppler ratio") parser.add_option("-B", "--divbase", type="eng_float", default=20, help="Y/Div menu base") parser.add_option("-I", "--division", type="eng_float", default=100, help="Y/Div") parser.add_option("-A", "--audio_source", default="plughw:0,0", help="Audio input device spec") parser.add_option("-N", "--num_pulses", default=1, type="eng_float", help="Number of display pulses") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.show_debug_info = True self.reflevel = options.reflevel self.divbase = options.divbase self.division = options.division self.audiodev = options.audio_source self.mult = int(options.num_pulses) # Low-pass cutoff for post-detector filter # Set to 100Hz usually, since lots of pulsars fit in this # range self.lowpass = options.lowpass # What is lowest valid frequency bin in post-detector FFT? # There's some pollution very close to DC self.lowest_freq = options.lowest # What (dB) threshold to use in determining spectral candidates self.threshold = options.threshold # Filename prefix for recording file self.prefix = options.prefix # Dispersion Measure (DM) self.dm = options.dm # Doppler shift, as a ratio # 1.0 == no doppler shift # 1.005 == a little negative shift # 0.995 == a little positive shift self.doppler = options.doppler # # Input frequency and observing frequency--not necessarily the # same thing, if we're looking at the IF of some downconverter # that's ahead of the USRP and daughtercard. This distinction # is important in computing the correct de-dispersion filter. # self.frequency = options.freq if options.observing <= 0: self.observing_freq = options.freq else: self.observing_freq = options.observing # build the graph self.u = usrp.source_c(decim_rate=options.decim) self.u.set_mux( usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) # # Recording file, in case we ever need to record baseband data # self.recording = gr.file_sink(gr.sizeof_char, "/dev/null") self.recording_state = False self.pulse_recording = gr.file_sink(gr.sizeof_short, "/dev/null") self.pulse_recording_state = False # # We come up with recording turned off, but the user may # request recording later on self.recording.close() self.pulse_recording.close() # # Need these two for converting 12-bit baseband signals to 8-bit # self.tofloat = gr.complex_to_float() self.tochar = gr.float_to_char() # Need this for recording pulses (post-detector) self.toshort = gr.float_to_short() # # The spectral measurer sets this when it has a valid # average spectral peak-to-peak distance # We can then use this to program the parameters for the epoch folder # # We set a sentimental value here self.pulse_freq = options.pulsefreq # Folder runs at this raw sample rate self.folder_input_rate = 20000 # Each pulse in the epoch folder is sampled at 128 times the nominal # pulse rate self.folding = 128 # # Try to find candidate parameters for rational resampler # save_i = 0 candidates = [] for i in range(20, 300): input_rate = self.folder_input_rate output_rate = int(self.pulse_freq * i) interp = gru.lcm(input_rate, output_rate) / input_rate decim = gru.lcm(input_rate, output_rate) / output_rate if (interp < 500 and decim < 250000): candidates.append(i) # We didn't find anything, bail! if (len(candidates) < 1): print "Couldn't converge on resampler parameters" sys.exit(1) # # Now try to find candidate with the least sampling error # mindiff = 999.999 for i in candidates: diff = self.pulse_freq * i diff = diff - int(diff) if (diff < mindiff): mindiff = diff save_i = i # Recompute rates input_rate = self.folder_input_rate output_rate = int(self.pulse_freq * save_i) # Compute new interp and decim, based on best candidate interp = gru.lcm(input_rate, output_rate) / input_rate decim = gru.lcm(input_rate, output_rate) / output_rate # Save optimized folding parameters, used later self.folding = save_i self.interp = int(interp) self.decim = int(decim) # So that we can view N pulses in the pulse viewer window FOLD_MULT = self.mult # determine the daughterboard subdevice we're using self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) self.cardtype = self.u.daughterboard_id(0) # Compute raw input rate input_rate = self.u.adc_freq() / self.u.decim_rate() # BW==input_rate for complex data self.bw = input_rate # # Set baseband filter bandwidth if DBS_RX: # if self.cardtype == usrp_dbid.DBS_RX: lbw = input_rate / 2 if lbw < 1.0e6: lbw = 1.0e6 self.subdev.set_bw(lbw) # # We use this as a crude volume control for the audio output # #self.volume = gr.multiply_const_ff(10**(-1)) # # Create location data for ephem package # self.locality = ephem.Observer() self.locality.long = str(options.longitude) self.locality.lat = str(options.latitude) # # What is the post-detector LPF cutoff for the FFT? # PULSAR_MAX_FREQ = int(options.lowpass) # First low-pass filters down to input_rate/FIRST_FACTOR # and decimates appropriately FIRST_FACTOR = int(input_rate / (self.folder_input_rate / 2)) first_filter = gr.firdes.low_pass(1.0, input_rate, input_rate / FIRST_FACTOR, input_rate / (FIRST_FACTOR * 20), gr.firdes.WIN_HAMMING) # Second filter runs at the output rate of the first filter, # And low-pass filters down to PULSAR_MAX_FREQ*10 # second_input_rate = int(input_rate / (FIRST_FACTOR / 2)) second_filter = gr.firdes.band_pass(1.0, second_input_rate, 0.10, PULSAR_MAX_FREQ * 10, PULSAR_MAX_FREQ * 1.5, gr.firdes.WIN_HAMMING) # Third filter runs at PULSAR_MAX_FREQ*20 # and filters down to PULSAR_MAX_FREQ # third_input_rate = PULSAR_MAX_FREQ * 20 third_filter = gr.firdes_band_pass(1.0, third_input_rate, 0.10, PULSAR_MAX_FREQ, PULSAR_MAX_FREQ / 10.0, gr.firdes.WIN_HAMMING) # # Create the appropriate FFT scope # self.scope = ra_fftsink.ra_fft_sink_f(panel, fft_size=int(options.fft_size), sample_rate=PULSAR_MAX_FREQ * 2, title="Post-detector spectrum", ofunc=self.pulsarfunc, xydfunc=self.xydfunc, fft_rate=200) # # Tell scope we're looking from DC to PULSAR_MAX_FREQ # self.scope.set_baseband_freq(0.0) # # Setup stripchart for showing pulse profiles # hz = "%5.3fHz " % self.pulse_freq per = "(%5.3f sec)" % (1.0 / self.pulse_freq) sr = "%d sps" % (int(self.pulse_freq * self.folding)) times = " %d Pulse Intervals" % self.mult self.chart = ra_stripchartsink.stripchart_sink_f( panel, sample_rate=1, stripsize=self.folding * FOLD_MULT, parallel=True, title="Pulse Profiles: " + hz + per + times, xlabel="Seconds @ " + sr, ylabel="Level", autoscale=True, divbase=self.divbase, scaling=1.0 / (self.folding * self.pulse_freq)) self.chart.set_ref_level(self.reflevel) self.chart.set_y_per_div(self.division) # De-dispersion filter setup # # Do this here, just before creating the filter # that will use the taps. # ntaps = self.compute_disp_ntaps(self.dm, self.bw, self.observing_freq) # Taps for the de-dispersion filter self.disp_taps = Numeric.zeros(ntaps, Numeric.Complex64) # Compute the de-dispersion filter now self.compute_dispfilter(self.dm, self.doppler, self.bw, self.observing_freq) # # Call constructors for receive chains # # # Now create the FFT filter using the computed taps self.dispfilt = gr.fft_filter_ccc(1, self.disp_taps) # # Audio sink # #print "input_rate ", second_input_rate, "audiodev ", self.audiodev #self.audio = audio.sink(second_input_rate, self.audiodev) # # The three post-detector filters # Done this way to allow an audio path (up to 10Khz) # ...and also because going from xMhz down to ~100Hz # In a single filter doesn't seem to work. # self.first = gr.fir_filter_fff(FIRST_FACTOR / 2, first_filter) p = second_input_rate / (PULSAR_MAX_FREQ * 20) self.second = gr.fir_filter_fff(int(p), second_filter) self.third = gr.fir_filter_fff(10, third_filter) # Detector self.detector = gr.complex_to_mag_squared() self.enable_comb_filter = False # Epoch folder comb filter if self.enable_comb_filter == True: bogtaps = Numeric.zeros(512, Numeric.Float64) self.folder_comb = gr.fft_filter_ccc(1, bogtaps) # Rational resampler self.folder_rr = blks2.rational_resampler_fff(self.interp, self.decim) # Epoch folder bandpass bogtaps = Numeric.zeros(1, Numeric.Float64) self.folder_bandpass = gr.fir_filter_fff(1, bogtaps) # Epoch folder F2C/C2F self.folder_f2c = gr.float_to_complex() self.folder_c2f = gr.complex_to_float() # Epoch folder S2P self.folder_s2p = gr.serial_to_parallel(gr.sizeof_float, self.folding * FOLD_MULT) # Epoch folder IIR Filter (produces average pulse profiles) self.folder_iir = gr.single_pole_iir_filter_ff( 1.0 / options.favg, self.folding * FOLD_MULT) # # Set all the epoch-folder goop up # self.set_folding_params() # # Start connecting configured modules in the receive chain # # Connect raw USRP to de-dispersion filter, detector self.connect(self.u, self.dispfilt, self.detector) # Connect detector output to FIR LPF # in two stages, followed by the FFT scope self.connect(self.detector, self.first, self.second, self.third, self.scope) # Connect audio output #self.connect(self.first, self.volume) #self.connect(self.volume, (self.audio, 0)) #self.connect(self.volume, (self.audio, 1)) # Connect epoch folder if self.enable_comb_filter == True: self.connect(self.first, self.folder_bandpass, self.folder_rr, self.folder_f2c, self.folder_comb, self.folder_c2f, self.folder_s2p, self.folder_iir, self.chart) else: self.connect(self.first, self.folder_bandpass, self.folder_rr, self.folder_s2p, self.folder_iir, self.chart) # Connect baseband recording file (initially /dev/null) self.connect(self.u, self.tofloat, self.tochar, self.recording) # Connect pulse recording file (initially /dev/null) self.connect(self.first, self.toshort, self.pulse_recording) # # Build the GUI elements # self._build_gui(vbox) # Make GUI agree with command-line self.myform['average'].set_value(int(options.avg)) self.myform['foldavg'].set_value(int(options.favg)) # Make spectral averager agree with command line if options.avg != 1.0: self.scope.set_avg_alpha(float(1.0 / options.avg)) self.scope.set_average(True) # set initial values if options.gain is None: # if no gain was specified, use the mid-point in dB g = self.subdev.gain_range() options.gain = float(g[0] + g[1]) / 2 if options.freq is None: # if no freq was specified, use the mid-point r = self.subdev.freq_range() options.freq = float(r[0] + r[1]) / 2 self.set_gain(options.gain) #self.set_volume(-10.0) if not (self.set_freq(options.freq)): self._set_status_msg("Failed to set initial frequency") self.myform['decim'].set_value(self.u.decim_rate()) self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) self.myform['dbname'].set_value(self.subdev.name()) self.myform['DM'].set_value(self.dm) self.myform['Doppler'].set_value(self.doppler) # # Start the timer that shows current LMST on the GUI # self.lmst_timer.Start(1000)
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, ntxchans, nrxchans, fft_length, cp_length, occupied_tones, snr, ks, logging=False): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param ntxchans: Number of transmitter MIMO channels (antennas) @type ntxchans: int @param nrxchans: Number of reciever MIMO channels (antennas) @type nrxchans: int @param fft_length: total number of subcarriers @type fft_length: int @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) @type cp_length: int @param occupied_tones: number of subcarriers used for data @type occupied_tones: int @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer @type snr: float @param ks: known symbols used as preambles to each packet @type ks: list of lists @param logging: turn file logging on or off @type logging: bool """ gr.hier_block2.__init__(self, "ofdm_mimo_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char)) # Output signature bw = (float(occupied_tones) / float(fft_length)) / 2.0 tb = bw*0.08 chan_coeffs = gr.firdes.low_pass (1.0, # gain 1.0, # sampling rate bw+tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type # For starters, run Sync on channel 0 and use it to clock and retime all channels win = [1 for i in range(fft_length)] zeros_on_left = int(math.ceil((fft_length - occupied_tones)/2.0)) ks0 = fft_length*[0,] ks0[zeros_on_left : zeros_on_left + occupied_tones] = ks[0][0:] ks0 = fft.ifftshift(ks0) ks0time = fft.ifft(ks0) # ADD SCALING FACTOR ks0time = ks0time.tolist() SYNC = "pn" if SYNC == "ml": nco_sensitivity = -1.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_ml(fft_length, cp_length, snr, ks0time, logging) elif SYNC == "pn": nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pn(fft_length, cp_length, logging) elif SYNC == "pnac": nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pnac(fft_length, cp_length, ks0time, logging) elif SYNC == "fixed": # for testing only; do not user over the air self.chan_filt = gr.multiply_const_cc(1.0) # remove filter and filter delay for this nsymbols = 18 # enter the number of symbols per packet freq_offset = 0.0 # if you use a frequency offset, enter it here nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_fixed(fft_length, cp_length, nsymbols, freq_offset, logging) # Set up blocks self.chan_filt = list() self.sigmix = list() self.sampler = list() self.fft_demod = list() # Deinterleave the incoming stream into separate channels self.deint = gr.deinterleave(gr.sizeof_gr_complex) # generate a signal proportional to frequency error of sync block self.nco = gr.frequency_modulator_fc(nco_sensitivity) # Manage and combine all channels if 0: self.ofdm_frame_acq = gr.ofdm_mrc_frame_acquisition(nrxchans, occupied_tones, fft_length, cp_length, ks[0]) else: print "Transmitter using ", ntxchans print "Receiver using ", nrxchans if(ntxchans == 1): self.ofdm_frame_acq = gr.ofdm_alamouti_frame_acquisition(ntxchans, occupied_tones, fft_length, cp_length, ks[0], occupied_tones*[0,]) else: self.ofdm_frame_acq = gr.ofdm_alamouti_frame_acquisition(ntxchans, occupied_tones, fft_length, cp_length, ks[0], ks[1]) self.connect(self, self.deint) # deinterleave channels self.connect((self.ofdm_sync,0), self.nco) # use sync freq. offset to derotate signal for i in xrange(nrxchans): self.chan_filt.append(gr.fft_filter_ccc(1, chan_coeffs)) self.sigmix.append(gr.multiply_cc()) self.sampler.append(gr.ofdm_sampler(fft_length, fft_length+cp_length)) self.fft_demod.append(gr.fft_vcc(fft_length, True, win, True)) self.connect((self.deint, i), self.chan_filt[i]) # filter the input channel self.connect(self.nco, (self.sigmix[i],1)) # use sync freq. offset to derotate signal self.connect(self.chan_filt[i], (self.sigmix[i],0)) # signal to be derotated self.connect(self.sigmix[i], (self.sampler[i],0)) # sample off timing signal detected in sync alg self.connect((self.ofdm_sync,1), (self.sampler[i],1)) # timing signal to sample at self.connect((self.sampler[i],0), self.fft_demod[i]) # send derotated sampled signal to FFT self.connect(self.fft_demod[i], (self.ofdm_frame_acq,1+i)) # find frame start and equalize signal if logging: self.connect(self.chan_filt[i], gr.file_sink(gr.sizeof_gr_complex, ("ofdm_mimo-receiver-chan%02d-chan_filt_c.dat" % i))) self.connect(self.fft_demod[i], gr.file_sink(gr.sizeof_gr_complex*fft_length, ("ofdm_mimo-receiver-chan%02d-fft_out_c.dat" % i))) self.connect(self.sampler[i], gr.file_sink(gr.sizeof_gr_complex*fft_length, ("ofdm_mimo-receiver-chan%02d-sampler_c.dat" % i))) self.connect(self.sigmix[i], gr.file_sink(gr.sizeof_gr_complex, ("ofdm_mimo-receiver-chan%02d-sigmix_c.dat" % i))) if logging: self.connect((self.ofdm_frame_acq,0), gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_mimo-receiver-frame_acq_c.dat")) self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "ofdm_mimo-receiver-found_corr_b.dat")) self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_mimo-receiver-nco_c.dat")) self.connect(self.chan_filt[0], self.ofdm_sync) # into the synchronization alg. self.connect((self.sampler[0],1), (self.ofdm_frame_acq,0)) # send timing signal to signal frame start self.connect((self.sampler[i],1), gr.null_sink(fft_length*gr.sizeof_char)) self.connect((self.ofdm_frame_acq,0), (self,0)) # finished with fine/coarse freq correction, self.connect((self.ofdm_frame_acq,1), (self,1)) # frame and symbol timing, and equalization
======= ######################################### # Blocks ######################################### >>>>>>> 8d49faa84f59bbacfa1e96f9d30a844fba01f641 # Build the demodulator self.demodulator = self._demod_class(**demod_kwargs) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass (1.0, # gain sw_decim * self.samples_per_symbol(), # sampling rate 1.0, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # receiver self.packet_receiver = \ digital.demod_pkts(self.demodulator, access_code=None, callback=self._rx_callback, threshold=-1) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) # Display some information about the setup if self._verbose:
def __init__(self, demod_class, rx_callback, options): gr.hier_block2.__init__( self, "receive_path", gr.io_signature(0, 0, 0), # Input signature gr.io_signature(0, 0, 0)) # Output signature options = copy.copy( options) # make a copy so we can destructively modify self._which = options.which # the USRP board attached self._verbose = options.verbose self._rx_freq = options.rx_freq # receiver's center frequency self._rx_gain = options.rx_gain # receiver's gain self._rx_subdev_spec = options.rx_subdev_spec # daughterboard to use self._bitrate = options.bitrate # desired bit rate self._decim = options.decim # Decimating rate for the USRP (prelim) self._samples_per_symbol = options.samples_per_symbol # desired samples/symbol self._fusb_block_size = options.fusb_block_size # usb info for USRP self._fusb_nblocks = options.fusb_nblocks # usb info for USRP self._rx_callback = rx_callback # this callback is fired when there's a packet available self._demod_class = demod_class # the demodulator_class we're using if self._rx_freq is None: sys.stderr.write( "-f FREQ or --freq FREQ or --rx-freq FREQ must be specified\n") raise SystemExit # Set up USRP source; also adjusts decim, samples_per_symbol, and bitrate self._setup_usrp_source() g = self.subdev.gain_range() if options.show_rx_gain_range: print "Rx Gain Range: minimum = %g, maximum = %g, step size = %g" \ % (g[0], g[1], g[2]) self.set_gain(options.rx_gain) self.set_auto_tr(True) # enable Auto Transmit/Receive switching # Set RF frequency ok = self.set_freq(self._rx_freq) if not ok: print "Failed to set Rx frequency to %s" % ( eng_notation.num_to_str(self._rx_freq)) raise ValueError, eng_notation.num_to_str(self._rx_freq) # copy the final answers back into options for use by demodulator options.samples_per_symbol = self._samples_per_symbol options.bitrate = self._bitrate options.decim = self._decim # Get demod_kwargs demod_kwargs = self._demod_class.extract_kwargs_from_options(options) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass( 1.0, # gain sw_decim * self._samples_per_symbol, # sampling rate 1.0, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type # Decimating channel filter # complex in and out, float taps self.chan_filt = gr.fft_filter_ccc(sw_decim, chan_coeffs) #self.chan_filt = gr.fir_filter_ccf(sw_decim, chan_coeffs) # receiver self.packet_receiver = \ blks2.demod_pkts(self._demod_class(**demod_kwargs), access_code=None, callback=self._rx_callback, threshold=-1) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust if options.log_rx_power == True: self.probe = gr.probe_avg_mag_sqrd_cf(thresh, alpha) self.power_sink = gr.file_sink(gr.sizeof_float, "rxpower.dat") self.connect(self.chan_filt, self.probe, self.power_sink) else: self.probe = gr.probe_avg_mag_sqrd_c(thresh, alpha) self.connect(self.chan_filt, self.probe) # Display some information about the setup if self._verbose: self._print_verbage() self.connect(self.u, self.chan_filt, self.packet_receiver)
def __init__(self, options): gr.top_block.__init__(self) # Source block symbol_rate = 500000 self.source = uhd_receiver(options.args, symbol_rate, 2, options.rx_freq, 30, #options.spec, "RX2", options.spec, "TX/RX", 1) #options.verbose) options.samples_per_symbol = self.source._sps RX_output_file = "RX_output_test_file" _RX_output_file = 'RX_output_test_file' #RX_output_file = "Ding_Dong2.wma" #_RX_output_file = 'Ding_Dong2.wma' RX_file_sink = gr.file_sink(gr.sizeof_char, RX_output_file) RX_output_filesize = os.stat(_RX_output_file).st_size #print RX_output_filesize self.filesink = gr.file_sink(1, "output_file") # Correlator block self.correlator = correlator_cc.correlator_cc() # Despreader block self.despreader = spreader.despreader_cb() # RS Decoder block self.decoder = rscoding_bb.decode_bb() # CRC RX Block self.crcrx = crc.crcrx() # Print block self.printer = print_bb.print_bb() # NULL sink block self.nullsink = gr.null_sink(1) # RRC filter #nfilts = 32 #ntaps = nfilts * 11 * int(options.samples_per_symbol) #self.rrc_taps = gr.firdes.root_raised_cosine( #1.0, # gain #1000000, # sampling rate based on 32 fliters in resampler #symbol_rate, # symbol rate #1.0, # excess bandwidth or roll-off factor #ntaps) #self.rrc_filter = gr.pfb_arb_resampler_ccf(options.samples_per_symbol, # self.rrc_taps) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass (1.0, # gain #sw_decim * self.samples_per_symbol(), # sampling rate #1000000, # sampling rate 2, #self._chbw_factor, # midpoint of trans. band 1, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # Connect the blocks #self.connect(self.source, self.correlator) #self.connect(self.source, self.rrc_filter) #self.connect(self.rrc_filter, self.correlator) # connect block input to channel filter self.connect(self.source, self.channel_filter) self.connect(self.channel_filter, self.correlator) #self.connect(self.source, self.correlator) self.connect(self.correlator, self.despreader) self.connect(self.despreader, self.decoder) self.connect(self.decoder, self.crcrx) #self.connect(self.decoder, self.printer) #self.connect(self.printer, self.crcrx) #self.connect(self.despreader, self.printer) #self.connect(self.printer, self.nullsink) #self.connect(self.printer, self.filesink) #self.connect(self.crcrx, self.filesink) self.connect(self.crcrx, RX_file_sink)
def __init__(self, demod_class, rx_callback, options, source_block): gr.hier_block2.__init__(self, "receive_path", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(0, 0, 0)) options = copy.copy( options) # make a copy so we can destructively modify self._verbose = options.verbose self._bitrate = options.bitrate # desired bit rate self._rx_callback = rx_callback # this callback is fired when a packet arrives self._demod_class = demod_class # the demodulator_class we're using self._chbw_factor = options.chbw_factor # channel filter bandwidth factor # Get demod_kwargs demod_kwargs = self._demod_class.extract_kwargs_from_options(options) #Give hooks of usrp to blocks downstream self.source_block = source_block ######################################### # Build Blocks ######################################### # Build the demodulator self.demodulator = self._demod_class(**demod_kwargs) # Make sure the channel BW factor is between 1 and sps/2 # or the filter won't work. if (self._chbw_factor < 1.0 or self._chbw_factor > self.samples_per_symbol() / 2): sys.stderr.write( "Channel bandwidth factor ({0}) must be within the range [1.0, {1}].\n" .format(self._chbw_factor, self.samples_per_symbol() / 2)) sys.exit(1) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass( 1.0, # gain sw_decim * self.samples_per_symbol(), # sampling rate self._chbw_factor, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # receiver self.packet_receiver = \ digital.demod_pkts(self.demodulator, access_code=None, callback=self._rx_callback, threshold=-1) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.probe = gr.probe_avg_mag_sqrd_c(thresh, alpha) # Display some information about the setup if self._verbose: self._print_verbage() # More Carrier Sensing with FFT #self.gr_vector_sink = gr.vector_sink_c(1024) #self.gr_stream_to_vector = gr.stream_to_vector(gr.sizeof_gr_complex*1, 1024) #self.gr_head = gr.head(gr.sizeof_gr_complex*1024, 1024) #self.fft = fft.fft_vcc(1024, True, (window.blackmanharris(1024)), True, 1) # Parameters usrp_rate = options.bitrate self.fft_size = 1024 self.min_freq = 2.4e9 - 0.75e6 self.max_freq = 2.4e9 + 0.75e6 self.tune_delay = 0.001 self.dwell_delay = 0.01 s2v = gr.stream_to_vector(gr.sizeof_gr_complex, self.fft_size) mywindow = window.blackmanharris(self.fft_size) fft = gr.fft_vcc(self.fft_size, True, mywindow) power = 0 for tap in mywindow: power += tap * tap c2mag = gr.complex_to_mag_squared(self.fft_size) # FIXME the log10 primitive is dog slow log = gr.nlog10_ff( 10, self.fft_size, -20 * math.log10(self.fft_size) - 10 * math.log10(power / self.fft_size)) # Set the freq_step to 75% of the actual data throughput. # This allows us to discard the bins on both ends of the spectrum. #self.freq_step = 0.75 * usrp_rate #self.min_center_freq = self.min_freq + self.freq_step/2 #nsteps = math.ceil((self.max_freq - self.min_freq) / self.freq_step) #self.max_center_freq = self.min_center_freq + (nsteps * self.freq_step) self.freq_step = 1.5e6 self.min_center_freq = self.min_freq nsteps = 1 self.max_center_freq = self.max_freq self.next_freq = self.min_center_freq tune_delay = max(0, int(round(self.tune_delay * usrp_rate / self.fft_size))) # in fft_frames dwell_delay = max(1, int( round(self.dwell_delay * usrp_rate / self.fft_size))) # in fft_frames self.msgq = gr.msg_queue(16) self._tune_callback = tune( self) # hang on to this to keep it from being GC'd stats = gr.bin_statistics_f(self.fft_size, self.msgq, self._tune_callback, tune_delay, dwell_delay) ###################################################### # Connect Blocks Together ###################################################### #channel-filter-->Probe_Avg_Mag_Sqrd # -->Packet_Receiver (Demod Done Here!!) # # connect FFT sampler to system #self.connect(self, self.gr_stream_to_vector, self.fft, self.gr_vector_sink) # connect block input to channel filter self.connect(self, self.channel_filter) # connect the channel input filter to the carrier power detector self.connect(self.channel_filter, self.probe) # connect channel filter to the packet receiver self.connect(self.channel_filter, self.packet_receiver) # FIXME leave out the log10 until we speed it up #self.connect(self.u, s2v, fft, c2mag, log, stats) self.connect(self.channel_filter, s2v, fft, c2mag, stats)
def __init__(self, frame, panel, vbox, argv): stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) self.frame = frame self.panel = panel parser = OptionParser(option_class=eng_option) parser.add_option("-R", "--rx-subdev-spec", type="subdev", default=(0, 0), help="select USRP Rx side A or B (default=A)") parser.add_option("-d", "--decim", type="int", default=16, help="set fgpa decimation rate to DECIM [default=%default]") parser.add_option("-f", "--freq", type="eng_float", default=None, help="set frequency to FREQ", metavar="FREQ") parser.add_option("-Q", "--observing", type="eng_float", default=0.0, help="set observing frequency to FREQ") parser.add_option("-a", "--avg", type="eng_float", default=1.0, help="set spectral averaging alpha") parser.add_option("-V", "--favg", type="eng_float", default=2.0, help="set folder averaging alpha") parser.add_option("-g", "--gain", type="eng_float", default=None, help="set gain in dB (default is midpoint)") parser.add_option("-l", "--reflevel", type="eng_float", default=30.0, help="Set pulse display reference level") parser.add_option("-L", "--lowest", type="eng_float", default=1.5, help="Lowest valid frequency bin") parser.add_option("-e", "--longitude", type="eng_float", default=-76.02, help="Set Observer Longitude") parser.add_option("-c", "--latitude", type="eng_float", default=44.85, help="Set Observer Latitude") parser.add_option("-F", "--fft_size", type="eng_float", default=1024, help="Size of FFT") parser.add_option ("-t", "--threshold", type="eng_float", default=2.5, help="pulsar threshold") parser.add_option("-p", "--lowpass", type="eng_float", default=100, help="Pulse spectra cutoff freq") parser.add_option("-P", "--prefix", default="./", help="File prefix") parser.add_option("-u", "--pulsefreq", type="eng_float", default=0.748, help="Observation pulse rate") parser.add_option("-D", "--dm", type="eng_float", default=1.0e-5, help="Dispersion Measure") parser.add_option("-O", "--doppler", type="eng_float", default=1.0, help="Doppler ratio") parser.add_option("-B", "--divbase", type="eng_float", default=20, help="Y/Div menu base") parser.add_option("-I", "--division", type="eng_float", default=100, help="Y/Div") parser.add_option("-A", "--audio_source", default="plughw:0,0", help="Audio input device spec") parser.add_option("-N", "--num_pulses", default=1, type="eng_float", help="Number of display pulses") (options, args) = parser.parse_args() if len(args) != 0: parser.print_help() sys.exit(1) self.show_debug_info = True self.reflevel = options.reflevel self.divbase = options.divbase self.division = options.division self.audiodev = options.audio_source self.mult = int(options.num_pulses) # Low-pass cutoff for post-detector filter # Set to 100Hz usually, since lots of pulsars fit in this # range self.lowpass = options.lowpass # What is lowest valid frequency bin in post-detector FFT? # There's some pollution very close to DC self.lowest_freq = options.lowest # What (dB) threshold to use in determining spectral candidates self.threshold = options.threshold # Filename prefix for recording file self.prefix = options.prefix # Dispersion Measure (DM) self.dm = options.dm # Doppler shift, as a ratio # 1.0 == no doppler shift # 1.005 == a little negative shift # 0.995 == a little positive shift self.doppler = options.doppler # # Input frequency and observing frequency--not necessarily the # same thing, if we're looking at the IF of some downconverter # that's ahead of the USRP and daughtercard. This distinction # is important in computing the correct de-dispersion filter. # self.frequency = options.freq if options.observing <= 0: self.observing_freq = options.freq else: self.observing_freq = options.observing # build the graph self.u = usrp.source_c(decim_rate=options.decim) self.u.set_mux(usrp.determine_rx_mux_value(self.u, options.rx_subdev_spec)) # # Recording file, in case we ever need to record baseband data # self.recording = gr.file_sink(gr.sizeof_char, "/dev/null") self.recording_state = False self.pulse_recording = gr.file_sink(gr.sizeof_short, "/dev/null") self.pulse_recording_state = False # # We come up with recording turned off, but the user may # request recording later on self.recording.close() self.pulse_recording.close() # # Need these two for converting 12-bit baseband signals to 8-bit # self.tofloat = gr.complex_to_float() self.tochar = gr.float_to_char() # Need this for recording pulses (post-detector) self.toshort = gr.float_to_short() # # The spectral measurer sets this when it has a valid # average spectral peak-to-peak distance # We can then use this to program the parameters for the epoch folder # # We set a sentimental value here self.pulse_freq = options.pulsefreq # Folder runs at this raw sample rate self.folder_input_rate = 20000 # Each pulse in the epoch folder is sampled at 128 times the nominal # pulse rate self.folding = 128 # # Try to find candidate parameters for rational resampler # save_i = 0 candidates = [] for i in range(20,300): input_rate = self.folder_input_rate output_rate = int(self.pulse_freq * i) interp = gru.lcm(input_rate, output_rate) / input_rate decim = gru.lcm(input_rate, output_rate) / output_rate if (interp < 500 and decim < 250000): candidates.append(i) # We didn't find anything, bail! if (len(candidates) < 1): print "Couldn't converge on resampler parameters" sys.exit(1) # # Now try to find candidate with the least sampling error # mindiff = 999.999 for i in candidates: diff = self.pulse_freq * i diff = diff - int(diff) if (diff < mindiff): mindiff = diff save_i = i # Recompute rates input_rate = self.folder_input_rate output_rate = int(self.pulse_freq * save_i) # Compute new interp and decim, based on best candidate interp = gru.lcm(input_rate, output_rate) / input_rate decim = gru.lcm(input_rate, output_rate) / output_rate # Save optimized folding parameters, used later self.folding = save_i self.interp = int(interp) self.decim = int(decim) # So that we can view N pulses in the pulse viewer window FOLD_MULT=self.mult # determine the daughterboard subdevice we're using self.subdev = usrp.selected_subdev(self.u, options.rx_subdev_spec) self.cardtype = self.u.daughterboard_id(0) # Compute raw input rate input_rate = self.u.adc_freq() / self.u.decim_rate() # BW==input_rate for complex data self.bw = input_rate # # Set baseband filter bandwidth if DBS_RX: # if self.cardtype == usrp_dbid.DBS_RX: lbw = input_rate / 2 if lbw < 1.0e6: lbw = 1.0e6 self.subdev.set_bw(lbw) # # We use this as a crude volume control for the audio output # #self.volume = gr.multiply_const_ff(10**(-1)) # # Create location data for ephem package # self.locality = ephem.Observer() self.locality.long = str(options.longitude) self.locality.lat = str(options.latitude) # # What is the post-detector LPF cutoff for the FFT? # PULSAR_MAX_FREQ=int(options.lowpass) # First low-pass filters down to input_rate/FIRST_FACTOR # and decimates appropriately FIRST_FACTOR=int(input_rate/(self.folder_input_rate/2)) first_filter = gr.firdes.low_pass (1.0, input_rate, input_rate/FIRST_FACTOR, input_rate/(FIRST_FACTOR*20), gr.firdes.WIN_HAMMING) # Second filter runs at the output rate of the first filter, # And low-pass filters down to PULSAR_MAX_FREQ*10 # second_input_rate = int(input_rate/(FIRST_FACTOR/2)) second_filter = gr.firdes.band_pass(1.0, second_input_rate, 0.10, PULSAR_MAX_FREQ*10, PULSAR_MAX_FREQ*1.5, gr.firdes.WIN_HAMMING) # Third filter runs at PULSAR_MAX_FREQ*20 # and filters down to PULSAR_MAX_FREQ # third_input_rate = PULSAR_MAX_FREQ*20 third_filter = gr.firdes_band_pass(1.0, third_input_rate, 0.10, PULSAR_MAX_FREQ, PULSAR_MAX_FREQ/10.0, gr.firdes.WIN_HAMMING) # # Create the appropriate FFT scope # self.scope = ra_fftsink.ra_fft_sink_f (panel, fft_size=int(options.fft_size), sample_rate=PULSAR_MAX_FREQ*2, title="Post-detector spectrum", ofunc=self.pulsarfunc, xydfunc=self.xydfunc, fft_rate=200) # # Tell scope we're looking from DC to PULSAR_MAX_FREQ # self.scope.set_baseband_freq (0.0) # # Setup stripchart for showing pulse profiles # hz = "%5.3fHz " % self.pulse_freq per = "(%5.3f sec)" % (1.0/self.pulse_freq) sr = "%d sps" % (int(self.pulse_freq*self.folding)) times = " %d Pulse Intervals" % self.mult self.chart = ra_stripchartsink.stripchart_sink_f (panel, sample_rate=1, stripsize=self.folding*FOLD_MULT, parallel=True, title="Pulse Profiles: "+hz+per+times, xlabel="Seconds @ "+sr, ylabel="Level", autoscale=True, divbase=self.divbase, scaling=1.0/(self.folding*self.pulse_freq)) self.chart.set_ref_level(self.reflevel) self.chart.set_y_per_div(self.division) # De-dispersion filter setup # # Do this here, just before creating the filter # that will use the taps. # ntaps = self.compute_disp_ntaps(self.dm,self.bw,self.observing_freq) # Taps for the de-dispersion filter self.disp_taps = Numeric.zeros(ntaps,Numeric.Complex64) # Compute the de-dispersion filter now self.compute_dispfilter(self.dm,self.doppler, self.bw,self.observing_freq) # # Call constructors for receive chains # # # Now create the FFT filter using the computed taps self.dispfilt = gr.fft_filter_ccc(1, self.disp_taps) # # Audio sink # #print "input_rate ", second_input_rate, "audiodev ", self.audiodev #self.audio = audio.sink(second_input_rate, self.audiodev) # # The three post-detector filters # Done this way to allow an audio path (up to 10Khz) # ...and also because going from xMhz down to ~100Hz # In a single filter doesn't seem to work. # self.first = gr.fir_filter_fff (FIRST_FACTOR/2, first_filter) p = second_input_rate / (PULSAR_MAX_FREQ*20) self.second = gr.fir_filter_fff (int(p), second_filter) self.third = gr.fir_filter_fff (10, third_filter) # Detector self.detector = gr.complex_to_mag_squared() self.enable_comb_filter = False # Epoch folder comb filter if self.enable_comb_filter == True: bogtaps = Numeric.zeros(512, Numeric.Float64) self.folder_comb = gr.fft_filter_ccc(1,bogtaps) # Rational resampler self.folder_rr = blks2.rational_resampler_fff(self.interp, self.decim) # Epoch folder bandpass bogtaps = Numeric.zeros(1, Numeric.Float64) self.folder_bandpass = gr.fir_filter_fff (1, bogtaps) # Epoch folder F2C/C2F self.folder_f2c = gr.float_to_complex() self.folder_c2f = gr.complex_to_float() # Epoch folder S2P self.folder_s2p = gr.serial_to_parallel (gr.sizeof_float, self.folding*FOLD_MULT) # Epoch folder IIR Filter (produces average pulse profiles) self.folder_iir = gr.single_pole_iir_filter_ff(1.0/options.favg, self.folding*FOLD_MULT) # # Set all the epoch-folder goop up # self.set_folding_params() # # Start connecting configured modules in the receive chain # # Connect raw USRP to de-dispersion filter, detector self.connect(self.u, self.dispfilt, self.detector) # Connect detector output to FIR LPF # in two stages, followed by the FFT scope self.connect(self.detector, self.first, self.second, self.third, self.scope) # Connect audio output #self.connect(self.first, self.volume) #self.connect(self.volume, (self.audio, 0)) #self.connect(self.volume, (self.audio, 1)) # Connect epoch folder if self.enable_comb_filter == True: self.connect (self.first, self.folder_bandpass, self.folder_rr, self.folder_f2c, self.folder_comb, self.folder_c2f, self.folder_s2p, self.folder_iir, self.chart) else: self.connect (self.first, self.folder_bandpass, self.folder_rr, self.folder_s2p, self.folder_iir, self.chart) # Connect baseband recording file (initially /dev/null) self.connect(self.u, self.tofloat, self.tochar, self.recording) # Connect pulse recording file (initially /dev/null) self.connect(self.first, self.toshort, self.pulse_recording) # # Build the GUI elements # self._build_gui(vbox) # Make GUI agree with command-line self.myform['average'].set_value(int(options.avg)) self.myform['foldavg'].set_value(int(options.favg)) # Make spectral averager agree with command line if options.avg != 1.0: self.scope.set_avg_alpha(float(1.0/options.avg)) self.scope.set_average(True) # set initial values if options.gain is None: # if no gain was specified, use the mid-point in dB g = self.subdev.gain_range() options.gain = float(g[0]+g[1])/2 if options.freq is None: # if no freq was specified, use the mid-point r = self.subdev.freq_range() options.freq = float(r[0]+r[1])/2 self.set_gain(options.gain) #self.set_volume(-10.0) if not(self.set_freq(options.freq)): self._set_status_msg("Failed to set initial frequency") self.myform['decim'].set_value(self.u.decim_rate()) self.myform['fs@usb'].set_value(self.u.adc_freq() / self.u.decim_rate()) self.myform['dbname'].set_value(self.subdev.name()) self.myform['DM'].set_value(self.dm) self.myform['Doppler'].set_value(self.doppler) # # Start the timer that shows current LMST on the GUI # self.lmst_timer.Start(1000)
def __init__(self, p, logging=False): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param params: Raw OFDM parameters @type params: ofdm_params @param logging: turn file logging on or off @type logging: bool """ from numpy import fft gr.hier_block2.__init__( self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_gr_complex * p.occupied_tones, gr.sizeof_char)) # Output signature # low-pass filter the input channel bw = (float(p.occupied_tones) / float(p.fft_length)) / 2.0 tb = bw * 0.08 lpf_coeffs = gr.firdes.low_pass( 1.0, # gain 1.0, # sampling rate bw + tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type lpf = gr.fft_filter_ccc(1, lpf_coeffs) #lpf = gr.add_const_cc(0.0) ## no-op low-pass-filter self.connect(self, lpf) # to the synchronization algorithm #from gnuradio import blks2impl #from gnuradio.blks2impl.ofdm_sync_pn import ofdm_sync_pn sync = ofdm_sync(p.fft_length, p.cp_length, p.half_sync, logging) self.connect(lpf, sync) # correct for fine frequency offset computed in sync (up to +-pi/fft_length) # NOTE: frame_acquisition can correct coarse freq offset (i.e. kpi/fft_length) if p.half_sync: nco_sensitivity = 2.0 / p.fft_length else: nco_sensitivity = 1.0 / (p.fft_length + p.cp_length) #nco_sensitivity = 0 nco = gr.frequency_modulator_fc(nco_sensitivity) sigmix = gr.multiply_cc() self.connect((sync, 0), (sigmix, 0)) self.connect((sync, 1), nco, (sigmix, 1)) # sample at symbol boundaries # NOTE: (sync,2) indicates the first sample of the symbol! sampler = raw.ofdm_sampler(p.fft_length, p.fft_length + p.cp_length, timeout=100) self.connect(sigmix, (sampler, 0)) self.connect((sync, 2), (sampler, 1)) # timing signal to sample at # fft on the symbols win = [1 for i in range(p.fft_length)] # see gr_fft_vcc_fftw that it works differently if win = [] fft = gr.fft_vcc(p.fft_length, True, win, True) self.connect((sampler, 0), fft) # use the preamble to correct the coarse frequency offset and initial equalizer frame_acq = raw.ofdm_frame_acquisition(p.fft_length, p.cp_length, p.preambles) self.frame_acq = frame_acq self.connect(fft, (frame_acq, 0)) self.connect((sampler, 1), (frame_acq, 1)) self.connect((frame_acq, 0), (self, 0)) # finished with fine/coarse freq correction self.connect((frame_acq, 1), (self, 1)) # frame and symbol timing if logging: self.connect(lpf, gr.file_sink(gr.sizeof_gr_complex, "rx-filt.dat")) self.connect( fft, gr.file_sink(gr.sizeof_gr_complex * p.fft_length, "rx-fft.dat")) self.connect((frame_acq, 0), gr.file_sink(gr.sizeof_gr_complex * p.occupied_tones, "rx-acq.dat")) self.connect((frame_acq, 1), gr.file_sink(1, "rx-detect.datb")) self.connect( sampler, gr.file_sink(gr.sizeof_gr_complex * p.fft_length, "rx-sampler.dat")) self.connect(sigmix, gr.file_sink(gr.sizeof_gr_complex, "rx-sigmix.dat")) self.connect(nco, gr.file_sink(gr.sizeof_gr_complex, "rx-nco.dat"))
def __init__(self, mpoints, taps=None): """ Takes M complex streams in, produces single complex stream out that runs at M times the input sample rate @param mpoints: number of freq bins/interpolation factor/subbands @param taps: filter taps for subband filter The channel spacing is equal to the input sample rate. The total bandwidth and output sample rate are equal the input sample rate * nchannels. Output stream to frequency mapping: channel zero is at zero frequency. if mpoints is odd: Channels with increasing positive frequencies come from channels 1 through (N-1)/2. Channel (N+1)/2 is the maximum negative frequency, and frequency increases through N-1 which is one channel lower than the zero frequency. if mpoints is even: Channels with increasing positive frequencies come from channels 1 through (N/2)-1. Channel (N/2) is evenly split between the max positive and negative bins. Channel (N/2)+1 is the maximum negative frequency, and frequency increases through N-1 which is one channel lower than the zero frequency. Channels near the frequency extremes end up getting cut off by subsequent filters and therefore have diminished utility. """ item_size = gr.sizeof_gr_complex gr.hier_block2.__init__(self, "synthesis_filterbank", gr.io_signature(mpoints, mpoints, item_size), # Input signature gr.io_signature(1, 1, item_size)) # Output signature if taps is None: taps = _generate_synthesis_taps(mpoints) # pad taps to multiple of mpoints r = len(taps) % mpoints if r != 0: taps = taps + (mpoints - r) * (0,) # split in mpoints separate set of taps sub_taps = _split_taps(taps, mpoints) self.ss2v = gr.streams_to_vector(item_size, mpoints) self.ifft = gr.fft_vcc(mpoints, False, []) self.v2ss = gr.vector_to_streams(item_size, mpoints) # mpoints filters go in here... self.ss2s = gr.streams_to_stream(item_size, mpoints) for i in range(mpoints): self.connect((self, i), (self.ss2v, i)) self.connect(self.ss2v, self.ifft, self.v2ss) # build mpoints fir filters... for i in range(mpoints): f = gr.fft_filter_ccc(1, sub_taps[i]) self.connect((self.v2ss, i), f) self.connect(f, (self.ss2s, i)) self.connect(self.ss2s, self)
def __init__(self, ahw="default", freq=150.0e6, ppm=0.0, vol=1.0, ftune=0.0, xftune=0.0, srate=1.0e6, upclo=0.0, devinfo="rtl=0", agc=0, arate=48.0e3, upce=0, mthresh=-10.0, offs=50.e3, flist="", dfifo="multimode_fifo", mbw=2.0e3, deemph=75.0e-6, dmode="NFM1"): grc_wxgui.top_block_gui.__init__(self, title="Multimode Radio Receiver") _icon_path = "/usr/share/icons/hicolor/32x32/apps/gnuradio-grc.png" self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) ################################################## # Parameters ################################################## self.ahw = ahw self.freq = freq self.ppm = ppm self.vol = vol self.ftune = ftune self.xftune = xftune self.srate = srate self.upclo = upclo self.devinfo = devinfo self.agc = agc self.arate = arate self.upce = upce self.mthresh = mthresh self.offs = offs self.flist = flist self.dfifo = dfifo self.mbw = mbw self.deemph = deemph self.dmode = dmode ################################################## # Variables ################################################## self.sc_list_str = sc_list_str = flist self.zoom = zoom = 1 self.thresh = thresh = mthresh self.scan_rate = scan_rate = 15 self.scan_power = scan_power = 0 self.sc_low = sc_low = 150e6 self.sc_listm = sc_listm = False self.sc_list = sc_list = eval("["+sc_list_str+"]") self.sc_incr = sc_incr = 12.5e3 self.sc_high = sc_high = 300e6 self.sc_ena = sc_ena = False self.samp_rate = samp_rate = int(mh.get_good_rate(devinfo,srate)) self.rf_power = rf_power = 0 self.ifreq = ifreq = freq self.zoomed_lp = zoomed_lp = (samp_rate/2.1)/zoom self.wbfm = wbfm = 200e3 self.rf_d_power = rf_d_power = 0 self.mode = mode = dmode self.logpower = logpower = math.log10(rf_power+1.0e-14)*10.0 self.cur_freq = cur_freq = mh.scan_freq_out(sc_ena,sc_low,sc_high,freq,ifreq,scan_power+1.0e-14,thresh,sc_incr,scan_rate,sc_listm,sc_list) self.bw = bw = mbw self.audio_int_rate = audio_int_rate = 40e3 self.zoom_taps = zoom_taps = firdes.low_pass(1.0,samp_rate,zoomed_lp,zoomed_lp/3,firdes.WIN_HAMMING,6.76) self.xfine = xfine = xftune self.volume = volume = vol self.variable_static_text_1 = variable_static_text_1 = cur_freq self.variable_static_text_0_0 = variable_static_text_0_0 = samp_rate self.variable_static_text_0 = variable_static_text_0 = float(int(math.log10(rf_d_power+1.0e-14)*100.0)/10.0) self.upc_offset = upc_offset = upclo self.upc = upc = upce self.ssbo = ssbo = -bw/2 if mode == "LSB" else 0.0 self.sc_list_len = sc_list_len = len(sc_list) self.rfgain = rfgain = 25 self.record_file = record_file = "recording.wav" self.record = record = False self.offset = offset = offs self.muted = muted = 0.0 if logpower >= thresh else 1 self.main_taps = main_taps = firdes.low_pass(1.0,wbfm,mh.get_mode_deviation(mode,bw)*1.05,mh.get_mode_deviation(mode,bw)/2.0,firdes.WIN_HAMMING,6.76) self.k = k = wbfm/(2*math.pi*mh.get_mode_deviation(mode,bw)) self.iagc = iagc = agc self.freq_update = freq_update = 0 self.fine = fine = ftune self.digi_rate = digi_rate = 50e3 self.aratio = aratio = int(wbfm/audio_int_rate) ################################################## # Blocks ################################################## self.rf_probe = gr.probe_avg_mag_sqrd_c(0, 0.015) self.Main = self.Main = wx.Notebook(self.GetWin(), style=wx.NB_TOP) self.Main.AddPage(grc_wxgui.Panel(self.Main), "Main Controls") self.Main.AddPage(grc_wxgui.Panel(self.Main), "Scan/Upconv Controls") self.Add(self.Main) self._zoom_chooser = forms.drop_down( parent=self.Main.GetPage(0).GetWin(), value=self.zoom, callback=self.set_zoom, label="Spectral Zoom Ratio", choices=[1, 2, 5, 10, 20, 50, 100], labels=[], ) self.Main.GetPage(0).GridAdd(self._zoom_chooser, 1, 4, 1, 1) _xfine_sizer = wx.BoxSizer(wx.VERTICAL) self._xfine_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), sizer=_xfine_sizer, value=self.xfine, callback=self.set_xfine, label="Extra Fine Tuning", converter=forms.float_converter(), proportion=0, ) self._xfine_slider = forms.slider( parent=self.Main.GetPage(0).GetWin(), sizer=_xfine_sizer, value=self.xfine, callback=self.set_xfine, minimum=-1.0e3, maximum=1.0e3, num_steps=200, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Main.GetPage(0).GridAdd(_xfine_sizer, 0, 3, 1, 1) _volume_sizer = wx.BoxSizer(wx.VERTICAL) self._volume_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), sizer=_volume_sizer, value=self.volume, callback=self.set_volume, label="Volume", converter=forms.float_converter(), proportion=0, ) self._volume_slider = forms.slider( parent=self.Main.GetPage(0).GetWin(), sizer=_volume_sizer, value=self.volume, callback=self.set_volume, minimum=1.0, maximum=10.0, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Main.GetPage(0).GridAdd(_volume_sizer, 0, 0, 1, 1) self._upc_offset_text_box = forms.text_box( parent=self.Main.GetPage(1).GetWin(), value=self.upc_offset, callback=self.set_upc_offset, label="Upconv. LO Freq", converter=forms.float_converter(), ) self.Main.GetPage(1).GridAdd(self._upc_offset_text_box, 3, 2, 1, 2) self._upc_check_box = forms.check_box( parent=self.Main.GetPage(1).GetWin(), value=self.upc, callback=self.set_upc, label="Ext. Upconv.", true=1, false=0, ) self.Main.GetPage(1).GridAdd(self._upc_check_box, 3, 0, 1, 1) _rfgain_sizer = wx.BoxSizer(wx.VERTICAL) self._rfgain_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), sizer=_rfgain_sizer, value=self.rfgain, callback=self.set_rfgain, label="RF Gain", converter=forms.float_converter(), proportion=0, ) self._rfgain_slider = forms.slider( parent=self.Main.GetPage(0).GetWin(), sizer=_rfgain_sizer, value=self.rfgain, callback=self.set_rfgain, minimum=0, maximum=50, num_steps=200, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Main.GetPage(0).GridAdd(_rfgain_sizer, 2, 1, 1, 1) self._record_file_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), value=self.record_file, callback=self.set_record_file, label="Recording Filename", converter=forms.str_converter(), ) self.Main.GetPage(0).GridAdd(self._record_file_text_box, 2, 3, 1, 3) self._record_check_box = forms.check_box( parent=self.Main.GetPage(0).GetWin(), value=self.record, callback=self.set_record, label="Record", true=True, false=False, ) self.Main.GetPage(0).GridAdd(self._record_check_box, 2, 2, 1, 1) _offset_sizer = wx.BoxSizer(wx.VERTICAL) self._offset_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), sizer=_offset_sizer, value=self.offset, callback=self.set_offset, label="LO Offset", converter=forms.float_converter(), proportion=0, ) self._offset_slider = forms.slider( parent=self.Main.GetPage(0).GetWin(), sizer=_offset_sizer, value=self.offset, callback=self.set_offset, minimum=25e3, maximum=500e3, num_steps=200, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Main.GetPage(0).GridAdd(_offset_sizer, 1, 3, 1, 1) self._mode_chooser = forms.drop_down( parent=self.Main.GetPage(0).GetWin(), value=self.mode, callback=self.set_mode, label="Mode", choices=mh.get_modes_values(), labels=mh.get_modes_names(), ) self.Main.GetPage(0).GridAdd(self._mode_chooser, 0, 4, 1, 1) self._iagc_check_box = forms.check_box( parent=self.Main.GetPage(0).GetWin(), value=self.iagc, callback=self.set_iagc, label="AGC", true=1, false=0, ) self.Main.GetPage(0).GridAdd(self._iagc_check_box, 2, 0, 1, 1) def _freq_update_probe(): while True: val = self.rf_probe.level() try: self.set_freq_update(val) except AttributeError, e: pass time.sleep(1.0/(1.0/(2.5))) _freq_update_thread = threading.Thread(target=_freq_update_probe) _freq_update_thread.daemon = True _freq_update_thread.start() _fine_sizer = wx.BoxSizer(wx.VERTICAL) self._fine_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), sizer=_fine_sizer, value=self.fine, callback=self.set_fine, label="Fine Tuning", converter=forms.float_converter(), proportion=0, ) self._fine_slider = forms.slider( parent=self.Main.GetPage(0).GetWin(), sizer=_fine_sizer, value=self.fine, callback=self.set_fine, minimum=-35e3, maximum=35e3, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Main.GetPage(0).GridAdd(_fine_sizer, 0, 2, 1, 1) self.display_probe = gr.probe_avg_mag_sqrd_c(0, 0.002) _bw_sizer = wx.BoxSizer(wx.VERTICAL) self._bw_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), sizer=_bw_sizer, value=self.bw, callback=self.set_bw, label="AM/SSB Bandwidth", converter=forms.float_converter(), proportion=0, ) self._bw_slider = forms.slider( parent=self.Main.GetPage(0).GetWin(), sizer=_bw_sizer, value=self.bw, callback=self.set_bw, minimum=1.0e3, maximum=audio_int_rate/2, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Main.GetPage(0).GridAdd(_bw_sizer, 1, 2, 1, 1) self.wxgui_waterfallsink2_0 = waterfallsink2.waterfall_sink_c( self.Main.GetPage(0).GetWin(), baseband_freq=mh.get_last_returned(freq_update), dynamic_range=40, ref_level=0, ref_scale=2.0, sample_rate=samp_rate/zoom, fft_size=1024, fft_rate=4, average=True, avg_alpha=None, title="Spectrogram", win=window.hamming, ) self.Main.GetPage(0).Add(self.wxgui_waterfallsink2_0.win) def wxgui_waterfallsink2_0_callback(x, y): self.set_freq(x) self.wxgui_waterfallsink2_0.set_callback(wxgui_waterfallsink2_0_callback) self.wxgui_fftsink2_0 = fftsink2.fft_sink_c( self.Main.GetPage(0).GetWin(), baseband_freq=mh.get_last_returned(freq_update), y_per_div=10, y_divs=10, ref_level=0, ref_scale=2.0, sample_rate=samp_rate/zoom, fft_size=1024, fft_rate=4, average=True, avg_alpha=0.1, title="Panorama", peak_hold=False, win=window.hamming, ) self.Main.GetPage(0).Add(self.wxgui_fftsink2_0.win) def wxgui_fftsink2_0_callback(x, y): self.set_freq(x) self.wxgui_fftsink2_0.set_callback(wxgui_fftsink2_0_callback) self._variable_static_text_1_static_text = forms.static_text( parent=self.Main.GetPage(1).GetWin(), value=self.variable_static_text_1, callback=self.set_variable_static_text_1, label="Current Scan Freq", converter=forms.float_converter(), ) self.Main.GetPage(1).GridAdd(self._variable_static_text_1_static_text, 0, 5, 1, 2) self._variable_static_text_0_0_static_text = forms.static_text( parent=self.Main.GetPage(0).GetWin(), value=self.variable_static_text_0_0, callback=self.set_variable_static_text_0_0, label="Actual srate", converter=forms.float_converter(), ) self.Main.GetPage(0).GridAdd(self._variable_static_text_0_0_static_text, 1, 5, 1, 1) self._variable_static_text_0_static_text = forms.static_text( parent=self.Main.GetPage(0).GetWin(), value=self.variable_static_text_0, callback=self.set_variable_static_text_0, label="RF Level", converter=forms.float_converter(), ) self.Main.GetPage(0).GridAdd(self._variable_static_text_0_static_text, 1, 0, 1, 1) _thresh_sizer = wx.BoxSizer(wx.VERTICAL) self._thresh_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), sizer=_thresh_sizer, value=self.thresh, callback=self.set_thresh, label="Mute Threshold", converter=forms.float_converter(), proportion=0, ) self._thresh_slider = forms.slider( parent=self.Main.GetPage(0).GetWin(), sizer=_thresh_sizer, value=self.thresh, callback=self.set_thresh, minimum=-50, maximum=10, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Main.GetPage(0).GridAdd(_thresh_sizer, 1, 1, 1, 1) def _scan_power_probe(): while True: val = self.rf_probe.level() try: self.set_scan_power(val) except AttributeError, e: pass time.sleep(1.0/(scan_rate)) _scan_power_thread = threading.Thread(target=_scan_power_probe) _scan_power_thread.daemon = True _scan_power_thread.start() self._sc_low_text_box = forms.text_box( parent=self.Main.GetPage(1).GetWin(), value=self.sc_low, callback=self.set_sc_low, label="Scan Low", converter=forms.float_converter(), ) self.Main.GetPage(1).GridAdd(self._sc_low_text_box, 0, 1, 1, 1) self._sc_listm_check_box = forms.check_box( parent=self.Main.GetPage(1).GetWin(), value=self.sc_listm, callback=self.set_sc_listm, label="Scan List Mode", true=True, false=False, ) self.Main.GetPage(1).GridAdd(self._sc_listm_check_box, 2, 0, 1, 1) self._sc_list_str_text_box = forms.text_box( parent=self.Main.GetPage(1).GetWin(), value=self.sc_list_str, callback=self.set_sc_list_str, label="Scan List", converter=forms.str_converter(), ) self.Main.GetPage(1).GridAdd(self._sc_list_str_text_box, 2, 1, 1, 5) self._sc_incr_chooser = forms.drop_down( parent=self.Main.GetPage(1).GetWin(), value=self.sc_incr, callback=self.set_sc_incr, label="Scan Increment (Hz)", choices=[5.0e3,6.25e3,10.0e3,12.5e3,15e3,25e3], labels=[], ) self.Main.GetPage(1).GridAdd(self._sc_incr_chooser, 0, 0, 1, 1) self._sc_high_text_box = forms.text_box( parent=self.Main.GetPage(1).GetWin(), value=self.sc_high, callback=self.set_sc_high, label="Scan High", converter=forms.float_converter(), ) self.Main.GetPage(1).GridAdd(self._sc_high_text_box, 0, 2, 1, 1) self._sc_ena_check_box = forms.check_box( parent=self.Main.GetPage(1).GetWin(), value=self.sc_ena, callback=self.set_sc_ena, label="Scan Enable", true=True, false=False, ) self.Main.GetPage(1).GridAdd(self._sc_ena_check_box, 0, 3, 1, 1) def _rf_power_probe(): while True: val = self.rf_probe.level() try: self.set_rf_power(val) except AttributeError, e: pass time.sleep(1.0/(10)) _rf_power_thread = threading.Thread(target=_rf_power_probe) _rf_power_thread.daemon = True _rf_power_thread.start() def _rf_d_power_probe(): while True: val = self.display_probe.level() try: self.set_rf_d_power(val) except AttributeError, e: pass time.sleep(1.0/(5)) _rf_d_power_thread = threading.Thread(target=_rf_d_power_probe) _rf_d_power_thread.daemon = True _rf_d_power_thread.start() self.osmosdr_source_c_0 = osmosdr.source_c( args="nchan=" + str(1) + " " + devinfo ) self.osmosdr_source_c_0.set_sample_rate(samp_rate) self.osmosdr_source_c_0.set_center_freq(cur_freq+offset+(upc_offset*float(upc)), 0) self.osmosdr_source_c_0.set_freq_corr(ppm, 0) self.osmosdr_source_c_0.set_gain_mode(iagc, 0) self.osmosdr_source_c_0.set_gain(25 if iagc == 1 else rfgain, 0) self.osmosdr_source_c_0.set_if_gain(20, 0) self._ifreq_text_box = forms.text_box( parent=self.Main.GetPage(0).GetWin(), value=self.ifreq, callback=self.set_ifreq, label="Frequency", converter=forms.float_converter(), ) self.Main.GetPage(0).GridAdd(self._ifreq_text_box, 0, 1, 1, 1) self.gr_wavfile_sink_0 = gr.wavfile_sink("/dev/null" if record == False else record_file, 1, int(audio_int_rate), 8) self.gr_quadrature_demod_cf_0 = gr.quadrature_demod_cf(k) self.gr_multiply_const_vxx_2 = gr.multiply_const_vff((1.0 if mh.get_mode_type(mode) == "FM" else 0.0, )) self.gr_multiply_const_vxx_1 = gr.multiply_const_vff((0.0 if muted else volume/4.5, )) self.gr_multiply_const_vxx_0_0_0 = gr.multiply_const_vff((0.85 if mh.get_mode_type(mode) == "AM" else 0.0, )) self.gr_multiply_const_vxx_0_0 = gr.multiply_const_vff((0.85 if mh.get_mode_type(mode) == "SSB" else 0.0, )) self.gr_multiply_const_vxx_0 = gr.multiply_const_vcc(((1.0/math.sqrt(mh.get_mode_deviation(mode,bw))*250), )) self.gr_keep_one_in_n_1 = gr.keep_one_in_n(gr.sizeof_gr_complex*1, aratio) self.gr_keep_one_in_n_0_0 = gr.keep_one_in_n(gr.sizeof_gr_complex*1, zoom) self.gr_keep_one_in_n_0 = gr.keep_one_in_n(gr.sizeof_gr_complex*1, int(wbfm/digi_rate)) self.gr_freq_xlating_fir_filter_xxx_0_1 = gr.freq_xlating_fir_filter_ccc(1, (1.0, ), (offset+fine+xfine)/(samp_rate/1.0e6), samp_rate) self.gr_fractional_interpolator_xx_0 = gr.fractional_interpolator_ff(0, audio_int_rate/arate) self.gr_file_sink_0 = gr.file_sink(gr.sizeof_gr_complex*1, "/dev/null" if mh.get_mode_type(mode) != "DIG" else dfifo) self.gr_file_sink_0.set_unbuffered(True) self.gr_fft_filter_xxx_3 = gr.fft_filter_ccc(1, (zoom_taps), 1) self.gr_fft_filter_xxx_2_0 = gr.fft_filter_fff(5, (firdes.low_pass(1.0,wbfm,14.5e3,8.5e3,firdes.WIN_HAMMING,6.76)), 1) self.gr_fft_filter_xxx_2 = gr.fft_filter_ccc(1, (main_taps), 1) self.gr_fft_filter_xxx_0 = gr.fft_filter_ccc(int(samp_rate/wbfm), (firdes.low_pass(1.0,samp_rate,98.5e3,66e3,firdes.WIN_HAMMING,6.76)), 1) self.gr_feedforward_agc_cc_0 = gr.feedforward_agc_cc(1024, 0.75) self.gr_complex_to_real_0 = gr.complex_to_real(1) self.gr_complex_to_mag_squared_0 = gr.complex_to_mag_squared(1) self.gr_add_xx_0 = gr.add_vff(1) self.blks2_fm_deemph_0 = blks2.fm_deemph(fs=audio_int_rate, tau=deemph) self.audio_sink_0 = audio.sink(int(arate), ahw, True) ################################################## # Connections ################################################## self.connect((self.gr_multiply_const_vxx_0_0, 0), (self.gr_add_xx_0, 1)) self.connect((self.gr_fractional_interpolator_xx_0, 0), (self.gr_multiply_const_vxx_1, 0)) self.connect((self.gr_multiply_const_vxx_1, 0), (self.audio_sink_0, 0)) self.connect((self.gr_multiply_const_vxx_1, 0), (self.audio_sink_0, 1)) self.connect((self.gr_feedforward_agc_cc_0, 0), (self.gr_complex_to_mag_squared_0, 0)) self.connect((self.osmosdr_source_c_0, 0), (self.gr_freq_xlating_fir_filter_xxx_0_1, 0)) self.connect((self.gr_multiply_const_vxx_0_0_0, 0), (self.gr_add_xx_0, 2)) self.connect((self.gr_feedforward_agc_cc_0, 0), (self.gr_complex_to_real_0, 0)) self.connect((self.gr_complex_to_real_0, 0), (self.gr_multiply_const_vxx_0_0, 0)) self.connect((self.gr_multiply_const_vxx_2, 0), (self.gr_add_xx_0, 0)) self.connect((self.gr_complex_to_mag_squared_0, 0), (self.gr_multiply_const_vxx_0_0_0, 0)) self.connect((self.gr_multiply_const_vxx_0, 0), (self.display_probe, 0)) self.connect((self.gr_multiply_const_vxx_0, 0), (self.rf_probe, 0)) self.connect((self.gr_add_xx_0, 0), (self.gr_fractional_interpolator_xx_0, 0)) self.connect((self.gr_add_xx_0, 0), (self.gr_wavfile_sink_0, 0)) self.connect((self.gr_freq_xlating_fir_filter_xxx_0_1, 0), (self.gr_fft_filter_xxx_0, 0)) self.connect((self.gr_keep_one_in_n_0, 0), (self.gr_file_sink_0, 0)) self.connect((self.gr_freq_xlating_fir_filter_xxx_0_1, 0), (self.gr_fft_filter_xxx_3, 0)) self.connect((self.gr_fft_filter_xxx_3, 0), (self.gr_keep_one_in_n_0_0, 0)) self.connect((self.gr_keep_one_in_n_0_0, 0), (self.wxgui_fftsink2_0, 0)) self.connect((self.gr_keep_one_in_n_0_0, 0), (self.wxgui_waterfallsink2_0, 0)) self.connect((self.blks2_fm_deemph_0, 0), (self.gr_multiply_const_vxx_2, 0)) self.connect((self.gr_quadrature_demod_cf_0, 0), (self.gr_fft_filter_xxx_2_0, 0)) self.connect((self.gr_fft_filter_xxx_2, 0), (self.gr_keep_one_in_n_0, 0)) self.connect((self.gr_fft_filter_xxx_2, 0), (self.gr_multiply_const_vxx_0, 0)) self.connect((self.gr_fft_filter_xxx_0, 0), (self.gr_fft_filter_xxx_2, 0)) self.connect((self.gr_keep_one_in_n_1, 0), (self.gr_feedforward_agc_cc_0, 0)) self.connect((self.gr_fft_filter_xxx_2, 0), (self.gr_keep_one_in_n_1, 0)) self.connect((self.gr_fft_filter_xxx_2, 0), (self.gr_quadrature_demod_cf_0, 0)) self.connect((self.gr_fft_filter_xxx_2_0, 0), (self.blks2_fm_deemph_0, 0))
def __init__(self, p, logging=False): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param params: Raw OFDM parameters @type params: ofdm_params @param logging: turn file logging on or off @type logging: bool """ from numpy import fft gr.hier_block2.__init__(self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_gr_complex*p.occupied_tones, gr.sizeof_char)) # Output signature # low-pass filter the input channel bw = (float(p.occupied_tones) / float(p.fft_length)) / 2.0 tb = bw*0.08 lpf_coeffs = gr.firdes.low_pass (1.0, # gain 1.0, # sampling rate bw+tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type lpf = gr.fft_filter_ccc(1, lpf_coeffs) #lpf = gr.add_const_cc(0.0) ## no-op low-pass-filter self.connect(self, lpf) # to the synchronization algorithm #from gnuradio import blks2impl #from gnuradio.blks2impl.ofdm_sync_pn import ofdm_sync_pn sync = ofdm_sync(p.fft_length, p.cp_length, p.half_sync, logging) self.connect(lpf, sync) # correct for fine frequency offset computed in sync (up to +-pi/fft_length) # NOTE: frame_acquisition can correct coarse freq offset (i.e. kpi/fft_length) if p.half_sync: nco_sensitivity = 2.0/p.fft_length else: nco_sensitivity = 1.0/(p.fft_length + p.cp_length) #nco_sensitivity = 0 nco = gr.frequency_modulator_fc(nco_sensitivity) sigmix = gr.multiply_cc() self.connect((sync,0), (sigmix,0)) self.connect((sync,1), nco, (sigmix,1)) # sample at symbol boundaries # NOTE: (sync,2) indicates the first sample of the symbol! sampler = raw.ofdm_sampler(p.fft_length, p.fft_length+p.cp_length, timeout=100) self.connect(sigmix, (sampler,0)) self.connect((sync,2), (sampler,1)) # timing signal to sample at # fft on the symbols win = [1 for i in range(p.fft_length)] # see gr_fft_vcc_fftw that it works differently if win = [] fft = gr.fft_vcc(p.fft_length, True, win, True) self.connect((sampler,0), fft) # use the preamble to correct the coarse frequency offset and initial equalizer frame_acq = raw.ofdm_frame_acquisition(p.fft_length, p.cp_length, p.preambles) self.frame_acq = frame_acq self.connect(fft, (frame_acq,0)) self.connect((sampler,1), (frame_acq,1)) self.connect((frame_acq,0), (self,0)) # finished with fine/coarse freq correction self.connect((frame_acq,1), (self,1)) # frame and symbol timing if logging: self.connect(lpf, gr.file_sink(gr.sizeof_gr_complex, "rx-filt.dat")) self.connect(fft, gr.file_sink(gr.sizeof_gr_complex*p.fft_length, "rx-fft.dat")) self.connect((frame_acq,0), gr.file_sink(gr.sizeof_gr_complex*p.occupied_tones, "rx-acq.dat")) self.connect((frame_acq,1), gr.file_sink(1, "rx-detect.datb")) self.connect(sampler, gr.file_sink(gr.sizeof_gr_complex*p.fft_length, "rx-sampler.dat")) self.connect(sigmix, gr.file_sink(gr.sizeof_gr_complex, "rx-sigmix.dat")) self.connect(nco, gr.file_sink(gr.sizeof_gr_complex, "rx-nco.dat"))
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 __init__(self, options): gr.top_block.__init__(self) if(options.rx_freq is not None): symbol_rate = 500000 self.source = uhd_receiver(options.args, symbol_rate, 2, options.rx_freq, 30, #options.spec, "RX2", options.spec, "TX/RX", 1) #options.verbose) options.samples_per_symbol = self.source._sps elif(options.from_file is not None): sys.stderr.write(("Reading samples from '%s'.\n\n" % (options.from_file))) self.source = gr.file_source(gr.sizeof_gr_complex, options.from_file) else: sys.stderr.write("No source defined, pulling samples from null source.\n\n") self.source = gr.null_source(gr.sizeof_gr_complex) # Set up receive path # Correlator block self.correlator = correlator_cc.correlator_cc() # Despreader block self.despreader = spreader.despreader_cb() # RS Decoder block self.decoder = rscoding_bb.decode_bb() # CRC RX Block self.crcrx = crc.crcrx() # Packetizer receiver self.packetrx = packetizer.packet_sink() # Print block self.printer = print_bb.print_bb() # NULL sink block self.nullsink = gr.null_sink(1) # RRC filter #nfilts = 32 #ntaps = nfilts * 11 * int(options.samples_per_symbol) #self.rrc_taps = gr.firdes.root_raised_cosine( #1.0, # gain #1000000, # sampling rate based on 32 fliters in resampler #symbol_rate, # symbol rate #1.0, # excess bandwidth or roll-off factor #ntaps) #self.rrc_filter = gr.pfb_arb_resampler_ccf(options.samples_per_symbol, # self.rrc_taps) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass (1.0, # gain #sw_decim * self.samples_per_symbol(), # sampling rate #1000000, # sampling rate 2, #self._chbw_factor, # midpoint of trans. band 1, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # Connect the blocks #self.connect(self.source, self.correlator) #self.connect(self.source, self.rrc_filter) #self.connect(self.rrc_filter, self.correlator) # connect block input to channel filter self.connect(self.source, self.channel_filter) self.connect(self.channel_filter, self.correlator) #self.connect(self.source, self.correlator) self.connect(self.correlator, self.despreader) self.connect(self.despreader, self.decoder) self.connect(self.decoder, self.crcrx) #self.connect(self.decoder, self.printer) #self.connect(self.printer, self.crcrx) #self.connect(self.despreader, self.printer) #self.connect(self.printer, self.nullsink) #self.connect(self.printer, self.filesink) #self.connect(self.crcrx, self.filesink) self.connect(self.crcrx, self.packetrx)
def __init__(self, demod_class, rx_callback, options): gr.hier_block2.__init__(self, "receive_path", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(0, 0, 0)) options = copy.copy( options) # make a copy so we can destructively modify self._verbose = options.verbose self._bitrate = options.bitrate # desired bit rate self._rx_callback = rx_callback # this callback is fired when a packet arrives self._demod_class = demod_class # the demodulator_class we're using self._chbw_factor = options.chbw_factor # channel filter bandwidth factor # Get demod_kwargs demod_kwargs = self._demod_class.extract_kwargs_from_options(options) # Build the demodulator self.demodulator = self._demod_class(**demod_kwargs) # Make sure the channel BW factor is between 1 and sps/2 # or the filter won't work. if (self._chbw_factor < 1.0 or self._chbw_factor > self.samples_per_symbol() / 2): sys.stderr.write( "Channel bandwidth factor ({0}) must be within the range [1.0, {1}].\n" .format(self._chbw_factor, self.samples_per_symbol() / 2)) sys.exit(1) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass( 1.0, # gain sw_decim * self.samples_per_symbol(), # sampling rate self._chbw_factor, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # receiver self.packet_receiver = \ digital.demod_pkts(self.demodulator, access_code=None, callback=self._rx_callback, threshold=-1) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.probe = gr.probe_avg_mag_sqrd_c(thresh, alpha) # Display some information about the setup if self._verbose: self._print_verbage() # connect block input to channel filter self.connect(self, self.channel_filter) # connect the channel input filter to the carrier power detector self.connect(self.channel_filter, self.probe) # connect channel filter to the packet receiver self.connect(self.channel_filter, self.packet_receiver)
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 main(): N = 10000 fs = 2000.0 Ts = 1.0 / fs t = scipy.arange(0, N * Ts, Ts) # When playing with the number of channels, be careful about the filter # specs and the channel map of the synthesizer set below. nchans = 10 # Build the filter(s) bw = 1000 tb = 400 proto_taps = gr.firdes.low_pass_2(1, nchans * fs, bw, tb, 80, gr.firdes.WIN_BLACKMAN_hARRIS) print "Filter length: ", len(proto_taps) # Create a modulated signal npwr = 0.01 data = scipy.random.randint(0, 256, N) rrc_taps = gr.firdes.root_raised_cosine(1, 2, 1, 0.35, 41) src = gr.vector_source_b(data.astype(scipy.uint8).tolist(), False) mod = digital.bpsk_mod(samples_per_symbol=2) chan = gr.channel_model(npwr) rrc = gr.fft_filter_ccc(1, rrc_taps) # Split it up into pieces channelizer = blks2.pfb_channelizer_ccf(nchans, proto_taps, 2) # Put the pieces back together again syn_taps = [nchans * t for t in proto_taps] synthesizer = gr.pfb_synthesizer_ccf(nchans, syn_taps, True) src_snk = gr.vector_sink_c() snk = gr.vector_sink_c() # Remap the location of the channels # Can be done in synth or channelizer (watch out for rotattions in # the channelizer) synthesizer.set_channel_map([0, 1, 2, 3, 4, 15, 16, 17, 18, 19]) tb = gr.top_block() tb.connect(src, mod, chan, rrc, channelizer) tb.connect(rrc, src_snk) vsnk = [] for i in xrange(nchans): tb.connect((channelizer, i), (synthesizer, i)) vsnk.append(gr.vector_sink_c()) tb.connect((channelizer, i), vsnk[i]) tb.connect(synthesizer, snk) tb.run() sin = scipy.array(src_snk.data()[1000:]) sout = scipy.array(snk.data()[1000:]) # Plot original signal fs_in = nchans * fs f1 = pylab.figure(1, figsize=(16, 12), facecolor='w') s11 = f1.add_subplot(2, 2, 1) s11.psd(sin, NFFT=fftlen, Fs=fs_in) s11.set_title("PSD of Original Signal") s11.set_ylim([-200, -20]) s12 = f1.add_subplot(2, 2, 2) s12.plot(sin.real[1000:1500], "o-b") s12.plot(sin.imag[1000:1500], "o-r") s12.set_title("Original Signal in Time") start = 1 skip = 4 s13 = f1.add_subplot(2, 2, 3) s13.plot(sin.real[start::skip], sin.imag[start::skip], "o") s13.set_title("Constellation") s13.set_xlim([-2, 2]) s13.set_ylim([-2, 2]) # Plot channels nrows = int(scipy.sqrt(nchans)) ncols = int(scipy.ceil(float(nchans) / float(nrows))) f2 = pylab.figure(2, figsize=(16, 12), facecolor='w') for n in xrange(nchans): s = f2.add_subplot(nrows, ncols, n + 1) s.psd(vsnk[n].data(), NFFT=fftlen, Fs=fs_in) s.set_title("Channel {0}".format(n)) s.set_ylim([-200, -20]) # Plot reconstructed signal fs_out = 2 * nchans * fs f3 = pylab.figure(3, figsize=(16, 12), facecolor='w') s31 = f3.add_subplot(2, 2, 1) s31.psd(sout, NFFT=fftlen, Fs=fs_out) s31.set_title("PSD of Reconstructed Signal") s31.set_ylim([-200, -20]) s32 = f3.add_subplot(2, 2, 2) s32.plot(sout.real[1000:1500], "o-b") s32.plot(sout.imag[1000:1500], "o-r") s32.set_title("Reconstructed Signal in Time") start = 2 skip = 4 s33 = f3.add_subplot(2, 2, 3) s33.plot(sout.real[start::skip], sout.imag[start::skip], "o") s33.set_title("Constellation") s33.set_xlim([-2, 2]) s33.set_ylim([-2, 2]) pylab.show()
def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, threshold, options, logging=False): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param fft_length: total number of subcarriers @type fft_length: int @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) @type cp_length: int @param occupied_tones: number of subcarriers used for data @type occupied_tones: int @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer @type snr: float @param ks: known symbols used as preambles to each packet @type ks: list of lists @param logging: turn file logging on or off @type logging: bool """ gr.hier_block2.__init__( self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature #gr.io_signature2(2, 2, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char)) # Output signature apurv-- gr.io_signature3(3, 3, gr.sizeof_gr_complex * occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex * occupied_tones )) # apurv++, goes into frame sink for hestimates #gr.io_signature4(4, 4, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_gr_complex*fft_length)) # apurv++, goes into frame sink for hestimates bw = (float(occupied_tones) / float(fft_length)) / 2.0 tb = bw * 0.08 chan_coeffs = gr.firdes.low_pass( 1.0, # gain 1.0, # sampling rate bw + tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) win = [1 for i in range(fft_length)] zeros_on_left = int(math.ceil((fft_length - occupied_tones) / 2.0)) ks0 = fft_length * [ 0, ] ks0[zeros_on_left:zeros_on_left + occupied_tones] = ks[0] ks0 = fft.ifftshift(ks0) ks0time = fft.ifft(ks0) # ADD SCALING FACTOR ks0time = ks0time.tolist() nco_sensitivity = -2.0 / fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pn(fft_length, cp_length, ks0time, threshold, options.threshold_type, options.threshold_gap, logging) # apurv++ # Set up blocks self.nco = gr.frequency_modulator_fc( nco_sensitivity ) # generate a signal proportional to frequency error of sync block self.sigmix = gr.multiply_cc() self.sampler = digital_swig.ofdm_sampler( fft_length, fft_length + cp_length, len(ks) + 1, 100 ) # 1 for the extra preamble which ofdm_rx doesn't know about (check frame_sink) self.fft_demod = gr.fft_vcc(fft_length, True, win, True) self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition( occupied_tones, fft_length, cp_length, ks) if options.verbose: self._print_verbage(options) # apurv++ modified to allow collected time domain data to artifically pass through the rx chain # # to replay the input manually, use this # #self.connect(self, gr.null_sink(gr.sizeof_gr_complex)) #self.connect(gr.file_source(gr.sizeof_gr_complex, "input.dat"), self.chan_filt) ############# input -> chan_filt ############## self.connect(self, self.chan_filt) use_chan_filt = options.use_chan_filt correct_freq_offset = 0 if use_chan_filt == 1: ##### chan_filt -> SYNC, chan_filt -> SIGMIX #### self.connect(self.chan_filt, self.ofdm_sync) if correct_freq_offset == 1: # enable if frequency offset correction is required # self.connect(self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sigmix, 0)) # apurv++ follow freq offset else: self.connect(self.chan_filt, (self.sampler, 0)) ###self.connect(self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sampler, 0)) ## extra delay #self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) elif use_chan_filt == 2: #### alternative: chan_filt-> NULL, file_source -> SYNC, file_source -> SIGMIX #### self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex)) if correct_freq_offset == 1: self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), self.ofdm_sync) self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sigmix, 0)) else: self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), self.ofdm_sync) self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), (self.sampler, 0)) else: # chan_filt->NULL # self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex)) method = options.method if method == -1: ################## for offline analysis, dump sampler input till the frame_sink, using io_signature4 ################# if correct_freq_offset == 1: # enable if frequency offset correction is required # self.connect((self.ofdm_sync, 0), self.nco, (self.sigmix, 1)) # freq offset (0'ed :/) self.connect(self.sigmix, (self.sampler, 0)) # corrected output (0'ed FF) self.connect((self.ofdm_sync, 1), gr.delay(gr.sizeof_char, fft_length), (self.sampler, 1)) # timing signal else: # disable frequency offset correction completely # self.connect((self.ofdm_sync, 0), gr.null_sink(gr.sizeof_float)) self.connect((self.ofdm_sync, 1), (self.sampler, 1)) # timing signal, #self.connect((self.ofdm_sync,0), (self.sampler, 2)) ##added ##self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length+cp_length), (self.sampler, 1)) # timing signal, ##extra delay # route received time domain to sink (all-the-way) for offline analysis # self.connect((self.sampler, 0), (self.ofdm_frame_acq, 2)) #self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "sampler_timing.dat")) elif method == 0: # NORMAL functioning # if correct_freq_offset == 1: self.connect( (self.ofdm_sync, 0), self.nco, (self.sigmix, 1) ) # use sync freq. offset output to derotate input signal self.connect( self.sigmix, (self.sampler, 0)) # sample off timing signal detected in sync alg self.connect((self.ofdm_sync, 1), gr.delay(gr.sizeof_char, fft_length), (self.sampler, 1)) # delay? else: self.connect((self.ofdm_sync, 1), (self.sampler, 1)) #self.connect((self.sampler, 2), (self.ofdm_frame_acq, 2)) ####################################################################### use_default = options.use_default if use_default == 0: #(set method == 0) # sampler-> NULL, replay trace->fft_demod, ofdm_frame_acq (time domain) # #self.connect((self.sampler, 0), gr.null_sink(gr.sizeof_gr_complex*fft_length)) #self.connect((self.sampler, 1), gr.null_sink(gr.sizeof_char*fft_length)) self.connect( gr.file_source(gr.sizeof_gr_complex * fft_length, "symbols_src.dat"), self.fft_demod) self.connect( gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"), (self.ofdm_frame_acq, 1)) self.connect(self.fft_demod, (self.ofdm_frame_acq, 0)) elif use_default == 1: #(set method == -1) # normal functioning # self.connect( (self.sampler, 0), self.fft_demod) # send derotated sampled signal to FFT self.connect((self.sampler, 1), (self.ofdm_frame_acq, 1)) # send timing signal to signal frame start self.connect(self.fft_demod, (self.ofdm_frame_acq, 0)) elif use_default == 2: # replay directly to ofdm_frame_acq (frequency domain) # self.connect( gr.file_source(gr.sizeof_gr_complex * fft_length, "symbols_src.dat"), (self.ofdm_frame_acq, 0)) self.connect( gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"), (self.ofdm_frame_acq, 1)) ########################### some logging start ############################## #self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length), gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) #self.connect((self.sampler, 0), gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat")) #self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "ofdm_timing_sampler_c.dat")) ############################ some logging end ############################### self.connect((self.ofdm_frame_acq, 0), (self, 0)) # finished with fine/coarse freq correction, self.connect((self.ofdm_frame_acq, 1), (self, 1)) # frame and symbol timing, and equalization self.connect((self.ofdm_frame_acq, 2), (self, 2)) # equalizer: hestimates #self.connect((self.ofdm_frame_acq,3), (self,3)) # ref sampler above # apurv++ ends # #self.connect(self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_receiver-frame_acq_c.dat")) #self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) # apurv++ log the fine frequency offset corrected symbols # #self.connect((self.ofdm_frame_acq, 1), gr.file_sink(gr.sizeof_char, "ofdm_timing_frame_acq_c.dat")) #self.connect((self.ofdm_frame_acq, 2), gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_hestimates_c.dat")) #self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-fft_out_c.dat")) #self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) #self.connect(self, gr.file_sink(gr.sizeof_gr_complex, "ofdm_input_c.dat")) # apurv++ end # if logging: self.connect( self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) self.connect( self.fft_demod, gr.file_sink(gr.sizeof_gr_complex * fft_length, "ofdm_receiver-fft_out_c.dat")) self.connect( self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex * occupied_tones, "ofdm_receiver-frame_acq_c.dat")) self.connect((self.ofdm_frame_acq, 1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) self.connect( self.sampler, gr.file_sink(gr.sizeof_gr_complex * fft_length, "ofdm_receiver-sampler_c.dat")) #self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat")) self.connect( self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat"))
def __init__(self, demod_class, rx_callback, options): gr.hier_block2.__init__(self, "receive_path", gr.io_signature(0, 0, 0), # Input signature gr.io_signature(0, 0, 0)) # Output signature options = copy.copy(options) # make a copy so we can destructively modify self._verbose = options.verbose self._rx_freq = options.rx_freq # receiver's center frequency self._rx_gain = options.rx_gain # receiver's gain self._rx_subdev_spec = options.rx_subdev_spec # daughterboard to use self._bitrate = options.bitrate # desired bit rate self._decim = options.decim # Decimating rate for the USRP (prelim) self._samples_per_symbol = options.samples_per_symbol # desired samples/symbol self._fusb_block_size = options.fusb_block_size # usb info for USRP self._fusb_nblocks = options.fusb_nblocks # usb info for USRP self._rx_callback = rx_callback # this callback is fired when there's a packet available self._demod_class = demod_class # the demodulator_class we're using if self._rx_freq is None: sys.stderr.write("-f FREQ or --freq FREQ or --rx-freq FREQ must be specified\n") raise SystemExit # Set up USRP source; also adjusts decim, samples_per_symbol, and bitrate self._setup_usrp_source() g = self.subdev.gain_range() if options.show_rx_gain_range: print "Rx Gain Range: minimum = %g, maximum = %g, step size = %g" \ % (g[0], g[1], g[2]) self.set_gain(options.rx_gain) self.set_auto_tr(True) # enable Auto Transmit/Receive switching # Set RF frequency ok = self.set_freq(self._rx_freq) if not ok: print "Failed to set Rx frequency to %s" % (eng_notation.num_to_str(self._rx_freq)) raise ValueError, eng_notation.num_to_str(self._rx_freq) # copy the final answers back into options for use by demodulator options.samples_per_symbol = self._samples_per_symbol options.bitrate = self._bitrate options.decim = self._decim # Get demod_kwargs demod_kwargs = self._demod_class.extract_kwargs_from_options(options) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass (1.0, # gain sw_decim * self._samples_per_symbol, # sampling rate 1.0, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type # Decimating channel filter # complex in and out, float taps self.chan_filt = gr.fft_filter_ccc(sw_decim, chan_coeffs) #self.chan_filt = gr.fir_filter_ccf(sw_decim, chan_coeffs) # receiver self.packet_receiver = \ blks2.demod_pkts(self._demod_class(**demod_kwargs), access_code=None, callback=self._rx_callback, threshold=-1) # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.sequelcher = gr.simple_squelch_cc(45) if options.log_rx_power == True: self.probe = gr.probe_avg_mag_sqrd_cf(thresh,alpha) self.power_sink = gr.file_sink(gr.sizeof_float, "rxpower.dat") self.connect(self.chan_filt, self.probe, self.power_sink) else: self.probe = gr.probe_avg_mag_sqrd_c(thresh,alpha) #self.connect(self.chan_filt, self.sequelcher, self.probe) # Display some information about the setup if self._verbose: self._print_verbage() #filter the noise and pass only the transmitted data #file_source = gr.file_source(gr.sizeof_gr_complex,"modulated_data.dat") #self.connect(self.chan_filt,file_sink) self.connect(self.u, self.chan_filt,self.sequelcher, self.packet_receiver)
def __init__(self, demod_class, rx_callback, options, log_index=-1, use_new_pkt=False): gr.hier_block2.__init__(self, "receive_path", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(0, 0, 0)) options = copy.copy(options) # make a copy so we can destructively modify self._verbose = options.verbose self._bitrate = options.modulation_bitrate # desired bit rate self._rx_callback = rx_callback # this callback is fired when a packet arrives self._demod_class = demod_class # the demodulator_class we're using self._chbw_factor = options.chbw_factor # channel filter bandwidth factor self._access_code = options.rx_access_code self._threshold = options.access_code_threshold if self._access_code == '0': self._access_code = None elif self._access_code == '1': self._access_code = '0000010000110001010011110100011100100101101110110011010101111110' print 'rx access code: %s' % self._access_code elif self._access_code == '2': self._access_code = '1110001100101100010110110001110101100000110001011101000100001110' # Get demod_kwargs demod_kwargs = self._demod_class.extract_kwargs_from_options(options) # Build the demodulator self.demodulator = self._demod_class(**demod_kwargs) print self.demodulator self._use_coding = False self._rfcenterfreq = options.rf_rx_freq # make sure option exists before adding member variable if hasattr(options, 'my_bandwidth'): self._bandwidth = options.my_bandwidth self._logging = lincolnlog.LincolnLog(__name__) #if options.pcktlog != -1: # self._logging = lincolnlog.LincolnLog(__name__) #else: # self._logging = -1 # Make sure the channel BW factor is between 1 and sps/2 # or the filter won't work. if(self._chbw_factor < 1.0 or self._chbw_factor > self.samples_per_symbol()/2): sys.stderr.write("Channel bandwidth factor ({0}) must be within the range [1.0, {1}].\n".format(self._chbw_factor, self.samples_per_symbol()/2)) sys.exit(1) # Design filter to get actual channel we want sw_decim = 1 chan_coeffs = gr.firdes.low_pass (1.0, # gain sw_decim * self.samples_per_symbol(), # sampling rate self._chbw_factor, # midpoint of trans. band 0.5, # width of trans. band gr.firdes.WIN_HANN) # filter type self.channel_filter = gr.fft_filter_ccc(sw_decim, chan_coeffs) # receiver self.packet_receiver = \ digital_ll.pkt2.demod_pkts(self.demodulator, options, #added on 9/28/12 access_code=self._access_code, callback=self._rx_callback, threshold=self._threshold, use_coding=self._use_coding, logging=self._logging) # Display some information about the setup if self._verbose: self._print_verbage() # connect block input to channel filter self.connect(self, self.channel_filter) # connect channel filter to the packet receiver self.connect(self.channel_filter, self.packet_receiver) # added to make logging easier self._modulation = options.modulation if "_constellation" in vars(self.demodulator): self._constellation_points = len(self.demodulator._constellation.points()) else: self._constellation_points = None if "_excess_bw" in vars(self.demodulator): self._excess_bw = self.demodulator._excess_bw else: self._excess_bw = None if "_freq_bw" in vars(self.demodulator): self._freq_bw = self.demodulator._freq_bw else: self._freq_bw = None if "_phase_bw" in vars(self.demodulator): self._phase_bw = self.demodulator._phase_bw else: self._phase_bw = None if "_timing_bw" in vars(self.demodulator): self._timing_bw = self.demodulator._timing_bw else: self._timing_bw = None
def __init__(self, fg, mpoints, taps=None): """ Takes M complex streams in, produces single complex stream out that runs at M times the input sample rate @param fg: flow_graph @param mpoints: number of freq bins/interpolation factor/subbands @param taps: filter taps for subband filter The channel spacing is equal to the input sample rate. The total bandwidth and output sample rate are equal the input sample rate * nchannels. Output stream to frequency mapping: channel zero is at zero frequency. if mpoints is odd: Channels with increasing positive frequencies come from channels 1 through (N-1)/2. Channel (N+1)/2 is the maximum negative frequency, and frequency increases through N-1 which is one channel lower than the zero frequency. if mpoints is even: Channels with increasing positive frequencies come from channels 1 through (N/2)-1. Channel (N/2) is evenly split between the max positive and negative bins. Channel (N/2)+1 is the maximum negative frequency, and frequency increases through N-1 which is one channel lower than the zero frequency. Channels near the frequency extremes end up getting cut off by subsequent filters and therefore have diminished utility. """ item_size = gr.sizeof_gr_complex if taps is None: taps = _generate_synthesis_taps(mpoints) # pad taps to multiple of mpoints r = len(taps) % mpoints if r != 0: taps = taps + (mpoints - r) * (0,) # split in mpoints separate set of taps sub_taps = _split_taps(taps, mpoints) self.ss2v = gr.streams_to_vector(item_size, mpoints) self.ifft = gr.fft_vcc(mpoints, False, []) self.v2ss = gr.vector_to_streams(item_size, mpoints) # mpoints filters go in here... self.ss2s = gr.streams_to_stream(item_size, mpoints) fg.connect(self.ss2v, self.ifft, self.v2ss) # build mpoints fir filters... for i in range(mpoints): f = gr.fft_filter_ccc(1, sub_taps[i]) fg.connect((self.v2ss, i), f) fg.connect(f, (self.ss2s, i)) gr.hier_block.__init__(self, fg, self.ss2v, self.ss2s)
def __init__(self, modulator_class, options): ''' See below for what options should hold ''' gr.hier_block2.__init__(self, "transmit_path", gr.io_signature(0,0,0), gr.io_signature(1,1,gr.sizeof_gr_complex)) options = copy.copy(options) # make a copy so we can destructively modify self._verbose = options.verbose self._tx_amplitude = options.tx_amplitude # digital amplitude sent to USRP self._bitrate = options.bitrate # desired bit rate self._modulator_class = modulator_class # the modulator_class we are using # Get mod_kwargs mod_kwargs = self._modulator_class.extract_kwargs_from_options(options) # transmitter self.modulator = self._modulator_class(**mod_kwargs) self.packet_transmitter = \ digital.mod_pkts(self.modulator, access_code=None, msgq_limit=4, pad_for_usrp=True) self.amp = gr.multiply_const_cc(1) self.set_tx_amplitude(self._tx_amplitude) # Figure out how to calculate symbol_rate later symbol_rate = 5000000 # Hardcoding rx_freq and rx_gain self.source = uhd_receiver(options.args, symbol_rate, options.samples_per_symbol, options.tx_freq-1250000, 30, options.spec, options.antenna, options.verbose) options.samples_per_symbol = self.source._sps # Display some information about the setup if self._verbose: self._print_verbage() low_pass_taps = [ -0.0401, 0.0663, 0.0468, -0.0235, -0.0222, 0.0572, 0.0299, -0.1001, -0.0294, 0.3166, 0.5302, 0.3166, -0.0294, -0.1001, 0.0299, 0.0572, -0.0222, -0.0235, 0.0468, 0.0663, -0.0401] high_pass_taps = [ -0.0389, -0.0026, 0.0302, 0.0181, -0.0357, -0.0394, 0.0450, 0.0923, -0.0472, -0.3119, 0.5512, -0.3119, -0.0472, 0.0923, 0.0450, -0.0394, -0.0357, 0.0181, 0.0302, -0.0026, -0.0389] # Carrier Sensing Blocks alpha = 0.001 thresh = 30 # in dB, will have to adjust self.probe_lp = gr.probe_avg_mag_sqrd_c(thresh,alpha) self.probe_hp = gr.probe_avg_mag_sqrd_c(thresh,alpha) self.lp = gr.fft_filter_ccc(65536, low_pass_taps) self.hp = gr.fft_filter_ccc(65536, high_pass_taps) self.connect(self.source, self.lp) self.connect(self.lp, self.probe_lp) self.connect(self.source, self.hp) self.connect(self.hp, self.probe_hp) # Connect components in the flowgraph self.connect(self.packet_transmitter, self.amp, self)