def test_003(self): # Need to add padding padding = tuple([0 for i in range(29)]) scale = 2 vlen = 3 src_data = (0.0, 1.1, 2.2,) + padding + \ (3.2, 4.2, 5.5,) + padding + \ (-1.1, -2.2, -3.2,) + padding expected_result = [0, 2, 4] expected_result.extend(padding) expected_result.extend([6, 8, 11]) expected_result.extend(padding) expected_result.extend([-2, -4, -6]) expected_result.extend(padding) src = gr.vector_source_f(src_data) s2v = gr.stream_to_vector(gr.sizeof_float, vlen) op = gr.float_to_char(vlen, scale) v2s = gr.vector_to_stream(gr.sizeof_char, vlen) dst = gr.vector_sink_c() self.tb.connect(src, s2v, op, v2s, dst) self.tb.run() result_data = list(dst.data()) self.assertEqual(expected_result, result_data)
def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="Top Block") _icon_path = "/usr/share/icons/hicolor/32x32/apps/gnuradio-grc.png" self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) ################################################## # Variables ################################################## self.samp_rate = samp_rate = 1000 ################################################## # Blocks ################################################## self.blks2_ofdm_mod_0 = grc_blks2.packet_mod_b(blks2.ofdm_mod( options=grc_blks2.options( modulation="bpsk", fft_length=1024, occupied_tones=256, cp_length=512, pad_for_usrp=True, log=None, verbose=None, ), ), payload_length=0, ) self.gr_float_to_char_0 = gr.float_to_char() self.gr_sig_source_x_0 = gr.sig_source_f(samp_rate, gr.GR_SIN_WAVE, 100, 127, 0) self.gr_throttle_0 = gr.throttle(gr.sizeof_char*1, samp_rate) self.wxgui_fftsink2_0 = fftsink2.fft_sink_c( self.GetWin(), baseband_freq=0, y_per_div=10, y_divs=10, ref_level=50, ref_scale=2.0, sample_rate=samp_rate*1000, fft_size=1024, fft_rate=30, average=False, avg_alpha=None, title="FFT Plot", peak_hold=False, ) self.Add(self.wxgui_fftsink2_0.win) ################################################## # Connections ################################################## self.connect((self.gr_throttle_0, 0), (self.blks2_ofdm_mod_0, 0)) self.connect((self.gr_sig_source_x_0, 0), (self.gr_float_to_char_0, 0)) self.connect((self.gr_float_to_char_0, 0), (self.gr_throttle_0, 0)) self.connect((self.blks2_ofdm_mod_0, 0), (self.wxgui_fftsink2_0, 0))
def test_001(self): src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3) expected_result = [0, 1, 2, 3, 4, 5, 255, 254, 253] src = gr.vector_source_f(src_data) op = gr.float_to_char() dst = gr.vector_sink_b() self.tb.connect(src, op, dst) self.tb.run() result_data = list(dst.data()) self.assertEqual(expected_result, result_data)
def test_002(self): src_data = ( 126.0, 127.0, 128.0) expected_result = [ 126, 127, 127 ] src = gr.vector_source_f(src_data) op = gr.float_to_char() # Note: vector_sink_b returns uchar dst = gr.vector_sink_b() self.tb.connect(src, op, dst) self.tb.run() result_data = list(dst.data()) self.assertEqual(expected_result, result_data)
def test_002(self): src_data = (126.0, 127.0, 128.0) expected_result = [126, 127, 127] src = gr.vector_source_f(src_data) op = gr.float_to_char() # Note: vector_sink_b returns uchar dst = gr.vector_sink_b() self.tb.connect(src, op, dst) self.tb.run() result_data = list(dst.data()) self.assertEqual(expected_result, result_data)
def test_001(self): # Need to add padding padding = tuple([0 for i in range(23)]) src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.4, -1.1, -2.2, -3.3) + padding expected_result = [0, 1, 2, 3, 4, 5, -1, -2, -3] expected_result.extend(padding) src = gr.vector_source_f(src_data) op = gr.float_to_char() dst = gr.vector_sink_c() self.tb.connect(src, op, dst) self.tb.run() result_data = list(dst.data()) self.assertEqual(expected_result, result_data)
def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="Top Block") _icon_path = "/home/pfb/.local/share/icons/hicolor/32x32/apps/gnuradio-grc.png" self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) ################################################## # Variables ################################################## self.samp_rate = samp_rate = 16000000 ################################################## # Blocks ################################################## self.M_Sequence = gr.vector_source_f((-1, -1, 1, 1, -1, 1, 1, 1, 1, -1, 1, 1, 1, -1, 1, -1, -1, -1, -1, -1, -1, -1, 1, -1, 1, -1, 1, 1, -1, -1, -1, 1, 1, -1, -1, 1, 1, 1, -1, -1, -1, -1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, -1, 1, -1, -1, 1, -1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, 1, -1, -1, -1, -1, 1, 1, -1, -1, -1, 1, -1, -1, 1, 1, -1, -1, -1, -1, -1, 1, 1, -1, 1, -1, -1, -1, 1, -1, 1, -1, -1, 1, 1, -1, 1, -1, 1, -1, -1, -1, -1, 1, -1, 1, 1, 1, -1, -1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, -1, -1, -1, 1, 1, 1, -1, -1, 1, -1, 1, -1, -1, -1, 1, 1, 1, 1, -1, -1, -1, -1, 1, -1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, -1, -1, 1, -1, -1, -1, 1, -1, -1, -1, 1, 1, -1, 1, 1, -1, 1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, 1, 1, -1, -1, 1, -1, 1, 1, -1, 1, 1, -1, -1, 1, -1, -1, 1, 1, 1, -1, 1, -1, 1, -1, 1, -1, 1, 1, 1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, 1, 1, 1), True, 1) self.M_Sequence_Baseband_Modulator = blks2.dbpsk_mod( samples_per_symbol=2, excess_bw=0.35, gray_code=False, verbose=False, log=False, ) self.blks2_dxpsk_mod_0 = blks2.dbpsk_mod( samples_per_symbol=2, excess_bw=0.35, gray_code=True, verbose=False, log=False, ) self.gr_add_xx_0 = gr.add_vcc(1) self.gr_delay_0 = gr.delay(gr.sizeof_char*1, 16) self.gr_file_sink_0 = gr.file_sink(gr.sizeof_gr_complex*1, "../M_Sequence_Baseband.cmplx") self.gr_float_to_char_0 = gr.float_to_char() self.gr_multiply_const_vxx_0 = gr.multiply_const_vcc((0, )) self.gr_throttle_0 = gr.throttle(gr.sizeof_gr_complex*1, samp_rate) ################################################## # Connections ################################################## self.connect((self.gr_throttle_0, 0), (self.gr_file_sink_0, 0)) self.connect((self.gr_delay_0, 0), (self.blks2_dxpsk_mod_0, 0)) self.connect((self.blks2_dxpsk_mod_0, 0), (self.gr_multiply_const_vxx_0, 0)) self.connect((self.gr_multiply_const_vxx_0, 0), (self.gr_add_xx_0, 1)) self.connect((self.M_Sequence_Baseband_Modulator, 0), (self.gr_add_xx_0, 0)) self.connect((self.gr_add_xx_0, 0), (self.gr_throttle_0, 0)) self.connect((self.M_Sequence, 0), (self.gr_float_to_char_0, 0)) self.connect((self.gr_float_to_char_0, 0), (self.M_Sequence_Baseband_Modulator, 0)) self.connect((self.gr_float_to_char_0, 0), (self.gr_delay_0, 0))
def test_002(self): # Need to add padding padding = tuple([0 for i in range(29)]) src_data = (126.0, 127.0, 128.0) + padding expected_result = [126, 127, 127] expected_result.extend(padding) src = gr.vector_source_f(src_data) op = gr.float_to_char() dst = gr.vector_sink_c() self.tb.connect(src, op, dst) self.tb.run() result_data = list(dst.data()) self.assertEqual(expected_result, result_data)
def test_003(self): scale = 2 vlen = 3 src_data = (0.0, 1.1, 2.2, 3.3, 4.4, 5.5, -1.1, -2.2, -3.3) expected_result = [0, 2, 4, 6, 8, 11, 254, 252, 250] src = gr.vector_source_f(src_data) s2v = gr.stream_to_vector(gr.sizeof_float, vlen) op = gr.float_to_char(vlen, scale) v2s = gr.vector_to_stream(gr.sizeof_char, vlen) dst = gr.vector_sink_b() self.tb.connect(src, s2v, op, v2s, dst) self.tb.run() result_data = list(dst.data()) self.assertEqual(expected_result, result_data)
def test_002(self): # Need to add padding padding = tuple([0 for i in range(29)]) src_data = ( 126.0, 127.0, 128.0) + padding expected_result = [ 126, 127, 127 ] expected_result.extend(padding) src = gr.vector_source_f(src_data) op = gr.float_to_char() dst = gr.vector_sink_c() self.tb.connect(src, op, dst) self.tb.run() result_data = list(dst.data()) self.assertEqual(expected_result, result_data)
def __init__(self): gr.top_block.__init__(self, "CC1101 Burst Detector") def rx_callback(): print "Callback Fired" # Variables self.samp_rate = samp_rate = 125e3 self.f_center = f_center = 510e6 self.bandwidth = bandwidth = 200e3 self.gain = gain = 25 # Blocks self.uhd_sink = uhd.usrp_sink( device_addr="serial=E4R11Y0B1", #cheetara stream_args=uhd.stream_args( cpu_format="fc32", channels=range(1), ), ) self.uhd_sink.set_samp_rate(samp_rate) self.uhd_sink.set_center_freq(f_center, 0) self.uhd_sink.set_gain(gain, 0) self.uhd_sink.set_antenna("TX/RX", 0) self.uhd_sink.set_bandwidth(bandwidth, 0) self.uhd_sink.set_samp_rate(self.samp_rate) self.uhd_sink.set_center_freq(self.f_center, 0) self.uhd_sink.set_gain(self.gain, 0) self.src = gr.sig_source_f(samp_rate, gr.GR_COS_WAVE, 1000, 1, 0) self.f_to_b = gr.float_to_char() self.msk = level.msk_mod_bc() # Connections self.connect(self.src, self.f_to_b, self.msk, self.uhd_sink)
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, logging=False): """ OFDM synchronization using PN Correlation and initial cross-correlation: F. Tufvesson, O. Edfors, and M. Faulkner, "Time and Frequency Synchronization for OFDM using PN-Sequency Preambles," IEEE Proc. VTC, 1999, pp. 2203-2207. This implementation is meant to be a more robust version of the Schmidl and Cox receiver design. By correlating against the preamble and using that as the input to the time-delayed correlation, this circuit produces a very clean timing signal at the end of the preamble. The timing is more accurate and does not have the problem associated with determining the timing from the plateau structure in the Schmidl and Cox. This implementation appears to require that the signal is received with a normalized power or signal scalling factor to reduce ambiguities intorduced from partial correlation of the cyclic prefix and the peak detection. A better peak detection block might fix this. Also, the cross-correlation falls apart as the frequency offset gets larger and completely fails when an integer offset is introduced. Another thing to look at. """ gr.hier_block2.__init__(self, "ofdm_sync_pnac", 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) symbol_length = fft_length + cp_length # PN Sync with cross-correlation input # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime[0:fft_length//2]] kstime.reverse() self.crosscorr_filter = filter.fir_filter_ccc(1, kstime) # 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 input self.mag = gr.complex_to_mag_squared() movingsum_taps = (fft_length//1)*[1.0,] self.power = filter.fir_filter_fff(1,movingsum_taps) # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.compare = gr.sub_ff() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.threshold = gr.threshold_ff(0,0,0) # threshold detection might need to be tweaked self.peaks = gr.float_to_char() self.connect(self, self.input) # Cross-correlate input signal with known preamble self.connect(self.input, self.crosscorr_filter) # use the output of the cross-correlation as input time-shifted correlation self.connect(self.crosscorr_filter, self.delay) self.connect(self.crosscorr_filter, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.c2mag) self.connect(self.corr, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # Get the power of the input signal to compare against the correlation self.connect(self.crosscorr_filter, self.mag, self.power) # Compare the power to the correlator output to determine timing peak # When the peak occurs, it peaks above zero, so the thresholder detects this self.connect(self.c2mag, (self.compare,0)) self.connect(self.power, (self.compare,1)) self.connect(self.compare, self.threshold) self.connect(self.threshold, self.peaks, (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.peaks, (self,1)) if logging: self.connect(self.compare, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-compare_f.dat")) self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-theta_f.dat")) self.connect(self.power, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-inputpower_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-epsilon_f.dat")) self.connect(self.threshold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-threshold_f.dat")) self.connect(self.peaks, gr.file_sink(gr.sizeof_char, "ofdm_sync_pnac-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pnac-input_c.dat"))
def __init__(self, fft_length, cp_length, snr, kstime, logging): ''' Maximum Likelihood OFDM synchronizer: J. van de Beek, M. Sandell, and P. O. Borjesson, "ML Estimation of Time and Frequency Offset in OFDM Systems," IEEE Trans. Signal Processing, vol. 45, no. 7, pp. 1800-1805, 1997. ''' gr.hier_block2.__init__(self, "ofdm_sync_ml", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) SNR = 10.0**(snr/10.0) rho = SNR / (SNR + 1.0) symbol_length = fft_length + cp_length # ML Sync # Energy Detection from ML Sync self.connect(self, self.input) # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length) self.connect(self.input, self.delay) # magnitude squared blocks self.magsqrd1 = gr.complex_to_mag_squared() self.magsqrd2 = gr.complex_to_mag_squared() self.adder = gr.add_ff() moving_sum_taps = [rho/2 for i in range(cp_length)] self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps) self.connect(self.input,self.magsqrd1) self.connect(self.delay,self.magsqrd2) self.connect(self.magsqrd1,(self.adder,0)) self.connect(self.magsqrd2,(self.adder,1)) self.connect(self.adder,self.moving_sum_filter) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.mixer = gr.multiply_cc(); movingsum2_taps = [1.0 for i in range(cp_length)] self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps) # Correlator data handler self.c2mag = gr.complex_to_mag() self.angle = gr.complex_to_arg() self.connect(self.input,(self.mixer,1)) self.connect(self.delay,self.conjg,(self.mixer,0)) self.connect(self.mixer,self.movingsum2,self.c2mag) self.connect(self.movingsum2,self.angle) # ML Sync output arg, need to find maximum point of this self.diff = gr.sub_ff() self.connect(self.c2mag,(self.diff,0)) self.connect(self.moving_sum_filter,(self.diff,1)) #ML measurements input to sampler block and detect self.f2c = gr.float_to_complex() self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005) self.sample_and_hold = gr.sample_and_hold_ff() # use the sync loop values to set the sampler and the NCO # self.diff = theta # self.angle = epsilon self.connect(self.diff, self.pk_detect) # The DPLL corrects for timing differences between CP correlations use_dpll = 0 if use_dpll: self.dpll = gr.dpll_bb(float(symbol_length),0.01) self.connect(self.pk_detect, self.dpll) self.connect(self.dpll, (self.sample_and_hold,1)) else: self.connect(self.pk_detect, (self.sample_and_hold,1)) self.connect(self.angle, (self.sample_and_hold,0)) ################################ # correlate against known symbol # This gives us the same timing signal as the PN sync block only on the preamble # we don't use the signal generated from the CP correlation because we don't want # to readjust the timing in the middle of the packet or we ruin the equalizer settings. kstime = [k.conjugate() for k in kstime] kstime.reverse() self.kscorr = gr.fir_filter_ccc(1, kstime) self.corrmag = gr.complex_to_mag_squared() self.div = gr.divide_ff() # The output signature of the correlation has a few spikes because the rest of the # system uses the repeated preamble symbol. It needs to work that generically if # anyone wants to use this against a WiMAX-like signal since it, too, repeats. # The output theta of the correlator above is multiplied with this correlation to # identify the proper peak and remove other products in this cross-correlation self.threshold_factor = 0.1 self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0) self.f2b = gr.float_to_char() self.b2f = gr.char_to_float() self.mul = gr.multiply_ff() # Normalize the power of the corr output by the energy. This is not really needed # and could be removed for performance, but it makes for a cleaner signal. # if this is removed, the threshold value needs adjustment. self.connect(self.input, self.kscorr, self.corrmag, (self.div,0)) self.connect(self.moving_sum_filter, (self.div,1)) self.connect(self.div, (self.mul,0)) self.connect(self.pk_detect, self.b2f, (self.mul,1)) self.connect(self.mul, self.slice) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.slice, self.f2b, (self,1)) if logging: self.connect(self.moving_sum_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-energy_f.dat")) self.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat")) self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-corrmag_f.dat")) self.connect(self.kscorr, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-kscorr_c.dat")) self.connect(self.div, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-div_f.dat")) self.connect(self.mul, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-mul_f.dat")) self.connect(self.slice, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-slice_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat")) if use_dpll: self.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat"))
def __init__(self, agc_max=100, agc_decay=0.1, freq_offset=1000000, outfile="datafifo", bandpass_bandwidth=20, threshold_buffer=0.25, threshold_center=0.5, agc_attack=0.1, bandpass_transition_width=1000000): gr.top_block.__init__(self, "Collect") ################################################## # Parameters ################################################## self.agc_max = agc_max self.agc_decay = agc_decay self.freq_offset = freq_offset self.outfile = outfile self.bandpass_bandwidth = bandpass_bandwidth self.threshold_buffer = threshold_buffer self.threshold_center = threshold_center self.agc_attack = agc_attack self.bandpass_transition_width = bandpass_transition_width ################################################## # Variables ################################################## self.samp_rate = samp_rate = 64000000 ################################################## # Blocks ################################################## self.uhd_usrp_source_0 = uhd.usrp_source( device_addr="", io_type=uhd.io_type.COMPLEX_FLOAT32, num_channels=1, ) self.uhd_usrp_source_0.set_samp_rate(samp_rate) self.uhd_usrp_source_0.set_center_freq(915000000 - freq_offset, 0) self.uhd_usrp_source_0.set_gain(0, 0) self.uhd_usrp_source_0.set_antenna("TX/RX", 0) self.gr_threshold_ff_0 = gr.threshold_ff( threshold_center - threshold_buffer, threshold_center + threshold_buffer, 0) self.gr_map_bb_0 = gr.map_bb(([48, 49])) self.gr_float_to_char_0 = gr.float_to_char() self.gr_file_sink_0 = gr.file_sink(gr.sizeof_char * 1, outfile) self.gr_file_sink_0.set_unbuffered(False) self.gr_complex_to_mag_0 = gr.complex_to_mag(1) self.gr_agc2_xx_0_0 = gr.agc2_cc(agc_attack, agc_decay, 1.0, 1.0, agc_max) self.band_pass_filter_0 = gr.fir_filter_ccf( 1, firdes.band_pass(1, samp_rate, freq_offset - bandpass_bandwidth / 2, freq_offset + bandpass_bandwidth / 2, bandpass_transition_width, firdes.WIN_HAMMING, 6.76)) ################################################## # Connections ################################################## self.connect((self.gr_float_to_char_0, 0), (self.gr_map_bb_0, 0)) self.connect((self.gr_map_bb_0, 0), (self.gr_file_sink_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.gr_agc2_xx_0_0, 0)) self.connect((self.gr_agc2_xx_0_0, 0), (self.band_pass_filter_0, 0)) self.connect((self.gr_threshold_ff_0, 0), (self.gr_float_to_char_0, 0)) self.connect((self.gr_complex_to_mag_0, 0), (self.gr_threshold_ff_0, 0)) self.connect((self.band_pass_filter_0, 0), (self.gr_complex_to_mag_0, 0))
def __init__(self, fft_length, cp_length, kstime, threshold, threshold_type, threshold_gap, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) else: moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)] self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() movingsum2_taps = [1.0 for i in range(fft_length//2)] #movingsum2_taps = [0.5 for i in range(fft_length*4)] #apurv - implementing Veljo's suggestion, when pause b/w packets if 1: self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.sub1 = gr.add_const_ff(-1) self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) #apurv - implementing Veljo's suggestion, when pause b/w packets self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) #self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # apurv-- #self.connect(self.angle, gr.delay(gr.sizeof_float, offset), (self.sample_and_hold, 0)) #apurv++ cross_correlate = 1 if cross_correlate==1: # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) # get the magnitude # self.corrmag = gr.complex_to_mag_squared() self.f2b = gr.float_to_char() self.threshold_factor = threshold #0.0012 #0.012 #0.0015 if 0: self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0, fft_length) else: #thresholds = [self.threshold_factor, 9e-5] self.slice = gr.threshold_ff(threshold, threshold, 0, fft_length, threshold_type, threshold_gap) self.connect(self.input, self.crosscorr_filter, self.corrmag, self.slice, self.f2b) # some debug dump # self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_corrmag.dat")) #self.connect(self.f2b, gr.file_sink(gr.sizeof_char, "ofdm_f2b.dat")) self.connect(self.f2b, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) #self.connect(self.pk_detect, (self,1)) #removed #self.connect(self.f2b, gr.delay(gr.sizeof_char, 1), (self, 1)) self.connect(self.f2b, (self, 1)) if logging: self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
def __init__(self, frame, panel, vbox, argv): stdgui2.std_top_block.__init__(self, frame, panel, vbox, argv) 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, options): gr.top_block.__init__(self) if options.rx_freq is not None: u = uhd_receiver(options.args, options.bandwidth, options.rx_freq, options.rx_gain, options.spec, options.antenna, options.verbose) elif options.infile is not None: u = gr.file_source(gr.sizeof_gr_complex, options.infile) else: import sys sys.stderr.write("--freq or --infile must be specified\n") raise SystemExit self.scope = None if options.outfile is not None: rx = gr.file_sink(gr.sizeof_gr_complex, options.outfile) else: rx = ofdm_rxtx.RX(options) data_tones = rx.params.data_tones if options.rxdata is not None: if options.rxdata == '.': import scope # scope it out rxs = gr.vector_to_stream(gr.sizeof_gr_complex, data_tones) self.connect(rx, rxs) self.scope = scope.scope(self, rxs, 'Frame SNR', isComplex=True) else: if options.char > 0: # rail and scale self.connect(rx, gr.vector_to_stream(gr.sizeof_float, data_tones * 2), gr.multiply_const_ff(128.0 * (2**0.5)/ options.char), gr.rail_ff(-128.0, 127.0), gr.float_to_char(), gr.file_sink(gr.sizeof_char, options.rxdata)) else: self.connect(rx, gr.file_sink(data_tones * gr.sizeof_gr_complex, options.rxdata)) if options.snrdata is not None: # select one of the snr modes snr = ofdm_rxtx.SNR(rx.params.data_tones, options.size, mode=options.snrmode) if options.char > 0: # NOTE: we use repeat, assuming the file is long enough or properly aligned data = gr.stream_to_vector(gr.sizeof_float, data_tones * 2) self.connect(gr.file_source(gr.sizeof_char, options.txdata, repeat=True), gr.char_to_float(), gr.multiply_const_ff(options.char * (2**-0.5) / 128.0), data) else: data = ofdm_rxtx.make_data(rx.params.data_tones, options.size, options.txdata) self.connect(rx, (snr,0)) self.connect(data, (snr,1)) if options.snrdata == '-': # print it out msgq = gr.msg_queue(16) self.connect(snr, gr.message_sink(gr.sizeof_float, msgq, True)) self.watcher = ofdm_rxtx.queue_watcher(msgq) elif options.snrdata == '.': import scope # scope it out self.scope = scope.scope(self, snr, 'Frame SNR') else: self.connect(snr, gr.file_sink(gr.sizeof_float, options.snrdata)) else: pass #self.connect(rx, gr.null_sink(symbol_size)) # XXX do we still need this? self.connect(u, rx)
def setUp (self): self.tb = gr.top_block () # Read in successfully decoded live data from Matlab linf=open('/home/demel/exchange/matlab_d.txt') lintu=range(120) for i in lintu: lintu[i]=float(linf.readline()) #print lintu # source for live data self.srcl = gr.vector_source_f(lintu,False,120) # Read in .txt file with example MIB encoded + CRC checksum inf=open('/home/demel/exchange/crc.txt') self.intu=range(40) for i in self.intu: self.intu[i]=float(inf.readline()) #inf=open('/home/demel/exchange/matlab_d.txt') #intu=range(120) #for i in range(120): # intu[i]=float(inf.readline()) # Source and conversions self.src = gr.vector_source_f(self.intu,False,40) self.conv = gr.float_to_char(40,1) # Resize vector with repetition of last part # Vector to stream for encoder my_map1=range(46) for i in range(40): my_map1[i+6]=i for i in range(6): my_map1[i]=i+40 self.map1 = lte.vector_resize_vbvb(my_map1,40,46) self.vtos = gr.vector_to_stream(1*gr.sizeof_char,46) # Encoding of input data self.fsm = trellis.fsm(1,3,[91,121,117]) self.enc = trellis.encoder_bb(self.fsm,0) # unpack packed bits from encoder self.unp = gr.unpack_k_bits_bb(3) # stream to vector self.stov = gr.stream_to_vector(1*gr.sizeof_char,138) # Remove first part which contains tail-biting init stuff map2 = range(120) for i in map2: map2[i]= i+18 self.map2 = lte.vector_resize_vbvb(map2,138,120) # conversion from char to float to match input of decoder self.conv2= gr.char_to_float(120,1) ############################################### # From here on only "receiver side" processing ############################################### # like QPSK demodulation: NRZ coding. vec2=range(120) for i in vec2: vec2[i]=float(-2.0) self.mult = gr.multiply_const_vff(vec2) vec=range(120) for i in vec: vec[i]=1 self.add = gr.add_const_vff(vec) # this is the actual unit under test self.vit = lte.viterbi_vfvb() # Sinks self.snk = gr.vector_sink_b(40) self.snk2 = gr.vector_sink_f(120) # connecting blocks self.tb.connect(self.src,self.conv,self.map1,self.vtos,self.enc,self.unp) self.tb.connect(self.unp,self.stov,self.map2,self.conv2) self.tb.connect(self.conv2,self.mult,self.add) self.tb.connect(self.srcl,self.vit,self.snk) self.tb.connect(self.add,self.snk2)
def __init__(self, fft_length, cp_length, kstime, logging=False): """ OFDM synchronization using PN Correlation and initial cross-correlation: F. Tufvesson, O. Edfors, and M. Faulkner, "Time and Frequency Synchronization for OFDM using PN-Sequency Preambles," IEEE Proc. VTC, 1999, pp. 2203-2207. This implementation is meant to be a more robust version of the Schmidl and Cox receiver design. By correlating against the preamble and using that as the input to the time-delayed correlation, this circuit produces a very clean timing signal at the end of the preamble. The timing is more accurate and does not have the problem associated with determining the timing from the plateau structure in the Schmidl and Cox. This implementation appears to require that the signal is received with a normalized power or signal scalling factor to reduce ambiguities intorduced from partial correlation of the cyclic prefix and the peak detection. A better peak detection block might fix this. Also, the cross-correlation falls apart as the frequency offset gets larger and completely fails when an integer offset is introduced. Another thing to look at. """ gr.hier_block2.__init__( self, "ofdm_sync_pnac", 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) symbol_length = fft_length + cp_length # PN Sync with cross-correlation input # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime[0:fft_length // 2]] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) # 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 input self.mag = gr.complex_to_mag_squared() movingsum_taps = (fft_length // 1) * [ 1.0, ] self.power = gr.fir_filter_fff(1, movingsum_taps) # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.compare = gr.sub_ff() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.threshold = gr.threshold_ff( 0, 0, 0) # threshold detection might need to be tweaked self.peaks = gr.float_to_char() self.connect(self, self.input) # Cross-correlate input signal with known preamble self.connect(self.input, self.crosscorr_filter) # use the output of the cross-correlation as input time-shifted correlation self.connect(self.crosscorr_filter, self.delay) self.connect(self.crosscorr_filter, (self.corr, 0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr, 1)) self.connect(self.corr, self.c2mag) self.connect(self.corr, self.angle) self.connect(self.angle, (self.sample_and_hold, 0)) # Get the power of the input signal to compare against the correlation self.connect(self.crosscorr_filter, self.mag, self.power) # Compare the power to the correlator output to determine timing peak # When the peak occurs, it peaks above zero, so the thresholder detects this self.connect(self.c2mag, (self.compare, 0)) self.connect(self.power, (self.compare, 1)) self.connect(self.compare, self.threshold) self.connect(self.threshold, self.peaks, (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.peaks, (self, 1)) if logging: self.connect( self.compare, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-compare_f.dat")) self.connect( self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-theta_f.dat")) self.connect( self.power, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-inputpower_f.dat")) self.connect( self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-epsilon_f.dat")) self.connect( self.threshold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-threshold_f.dat")) self.connect( self.peaks, gr.file_sink(gr.sizeof_char, "ofdm_sync_pnac-peaks_b.dat")) self.connect( self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-sample_and_hold_f.dat")) self.connect( self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pnac-input_c.dat"))
def float_to_char(N): op = gr.float_to_char() tb = helper(N, op, gr.sizeof_float, gr.sizeof_char, 1, 1) return tb
def __init__(self, options): gr.hier_block2.__init__(self, "fbmc_receive_path", gr.io_signature(1,1,gr.sizeof_gr_complex), gr.io_signature(0,0,0)) print "This is FBMC receive path 1x1" common_options.defaults(options) config = self.config = station_configuration() config.data_subcarriers = dsubc = options.subcarriers config.cp_length = 0 config.frame_data_blocks = options.data_blocks config._verbose = options.verbose #TODO: update config.fft_length = options.fft_length config.dc_null = options.dc_null config.training_data = default_block_header(dsubc, config.fft_length,config.dc_null,options) config.coding = options.coding config.ber_window = options.ber_window config.periodic_parts = 8 config.frame_id_blocks = 1 # FIXME self._options = copy.copy(options) #FIXME: do we need this? config.fbmc = options.fbmc config.block_length = config.fft_length + config.cp_length config.frame_data_part = config.frame_data_blocks + config.frame_id_blocks config.frame_length = config.training_data.fbmc_no_preambles + 2*config.frame_data_part config.postpro_frame_length = config.frame_data_part + \ config.training_data.no_pilotsyms config.subcarriers = dsubc + \ config.training_data.pilot_subcarriers config.virtual_subcarriers = config.fft_length - config.subcarriers - config.dc_null total_subc = config.subcarriers # check some bounds if config.fft_length < config.subcarriers: raise SystemError, "Subcarrier number must be less than FFT length" if config.fft_length < config.cp_length: raise SystemError, "Cyclic prefix length must be less than FFT length" #self.input = gr.kludge_copy(gr.sizeof_gr_complex) #self.connect( self, self.input ) self.input = self self.ideal = options.ideal self.ideal2 = options.ideal2 ## Inner receiver ## Timing & Frequency Synchronization ## Channel estimation + Equalization ## Phase Tracking for sampling clock frequency offset correction inner_receiver = self.inner_receiver = fbmc_inner_receiver( options, options.log ) self.connect( self.input, inner_receiver ) ofdm_blocks = ( inner_receiver, 2 ) frame_start = ( inner_receiver, 1 ) disp_ctf = ( inner_receiver, 0 ) #self.snr_est_preamble = ( inner_receiver, 3 ) #terminate_stream(self,snr_est_preamble) disp_cfo = ( inner_receiver, 3 ) if self.ideal is False and self.ideal2 is False: self.zmq_probe_freqoff = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5557") self.connect(disp_cfo, self.zmq_probe_freqoff) else: self.connect(disp_cfo, blocks.null_sink(gr.sizeof_float)) # for ID decoder used_id_bits = config.used_id_bits = 8 #TODO: constant in source code! rep_id_bits = config.rep_id_bits = dsubc/used_id_bits #BPSK if options.log: print "rep_id_bits %d" % (rep_id_bits) if dsubc % used_id_bits <> 0: raise SystemError,"Data subcarriers need to be multiple of 10" ## Workaround to avoid periodic structure seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] ## NOTE!!! BIG HACK!!! ## first preamble ain't equalized .... ## for Milan's SNR estimator ## Outer Receiver ## Make new inner receiver compatible with old outer receiver ## FIXME: renew outer receiver self.ctf = disp_ctf #frame_sampler = ofdm_frame_sampler(options) frame_sampler = fbmc_frame_sampler(options) self.connect( ofdm_blocks, frame_sampler) self.connect( frame_start, (frame_sampler,1) ) # # ft = [0] * config.frame_length # ft[0] = 1 # # # The next block ensures that only complete frames find their way into # # the old outer receiver. The dynamic frame start trigger is hence # # replaced with a static one, fixed to the frame length. # # frame_sampler = ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, # config.frame_length ) # self.symbol_output = blocks.vector_to_stream( gr.sizeof_gr_complex * total_subc, # config.frame_length ) # delayed_frame_start = blocks.delay( gr.sizeof_char, config.frame_length - 1 ) # damn_static_frame_trigger = blocks.vector_source_b( ft, True ) # # if options.enable_erasure_decision: # frame_gate = vector_sampler( # gr.sizeof_gr_complex * total_subc * config.frame_length, 1 ) # self.connect( ofdm_blocks, frame_sampler, frame_gate, # self.symbol_output ) # else: # self.connect( ofdm_blocks, frame_sampler, self.symbol_output ) # # self.connect( frame_start, delayed_frame_start, ( frame_sampler, 1 ) ) if options.enable_erasure_decision: frame_gate = frame_sampler.frame_gate self.symbol_output = frame_sampler orig_frame_start = frame_start frame_start = (frame_sampler,1) self.frame_trigger = frame_start #terminate_stream(self, self.frame_trigger) ## Pilot block filter pb_filt = self._pilot_block_filter = fbmc_pilot_block_filter() self.connect(self.symbol_output,pb_filt) self.connect(self.frame_trigger,(pb_filt,1)) self.frame_data_trigger = (pb_filt,1) #self.symbol_output = pb_filt #if options.log: #log_to_file(self, pb_filt, "data/pb_filt_out.compl") if config.fbmc: pda_in = pb_filt else: ## Pilot subcarrier filter ps_filt = self._pilot_subcarrier_filter = pilot_subcarrier_filter() self.connect(self.symbol_output,ps_filt) if options.log: log_to_file(self, ps_filt, "data/ps_filt_out.compl") pda_in = ps_filt ## Workaround to avoid periodic structure # for ID decoder seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] if not options.enable_erasure_decision: ## ID Block Filter # Filter ID block, skip data blocks id_bfilt = self._id_block_filter = vector_sampler( gr.sizeof_gr_complex * dsubc, 1 ) if not config.frame_id_blocks == 1: raise SystemExit, "# ID Blocks > 1 not supported" self.connect( pda_in , id_bfilt ) self.connect( self.frame_data_trigger, ( id_bfilt, 1 ) ) # trigger #log_to_file( self, id_bfilt, "data/id_bfilt.compl" ) ## ID Demapper and Decoder, soft decision self.id_dec = self._id_decoder = ofdm.coded_bpsk_soft_decoder( dsubc, used_id_bits, whitener_pn ) self.connect( id_bfilt, self.id_dec ) print "Using coded BPSK soft decoder for ID detection" else: # options.enable_erasure_decision: id_bfilt = self._id_block_filter = vector_sampler( gr.sizeof_gr_complex * total_subc, config.frame_id_blocks ) id_bfilt_trig_delay = 0 for x in range( config.frame_length ): if x in config.training_data.pilotsym_pos: id_bfilt_trig_delay += 1 else: break print "Position of ID block within complete frame: %d" %(id_bfilt_trig_delay) assert( id_bfilt_trig_delay > 0 ) # else not supported id_bfilt_trig = blocks.delay( gr.sizeof_char, id_bfilt_trig_delay ) self.connect( ofdm_blocks, id_bfilt ) self.connect( orig_frame_start, id_bfilt_trig, ( id_bfilt, 1 ) ) self.id_dec = self._id_decoder = ofdm.coded_bpsk_soft_decoder( total_subc, used_id_bits, whitener_pn, config.training_data.shifted_pilot_tones ) self.connect( id_bfilt, self.id_dec ) print "Using coded BPSK soft decoder for ID detection" # The threshold block either returns 1.0 if the llr-value from the # id decoder is below the threshold, else 0.0. Hence we convert this # into chars, 0 and 1, and use it as trigger for the sampler. min_llr = ( self.id_dec, 1 ) erasure_threshold = gr.threshold_ff( 10.0, 10.0, 0 ) # FIXME is it the optimal threshold? erasure_dec = gr.float_to_char() id_gate = vector_sampler( gr.sizeof_short, 1 ) ctf_gate = vector_sampler( gr.sizeof_float * total_subc, 1 ) self.connect( self.id_dec , id_gate ) self.connect( self.ctf, ctf_gate ) self.connect( min_llr, erasure_threshold, erasure_dec ) self.connect( erasure_dec, ( frame_gate, 1 ) ) self.connect( erasure_dec, ( id_gate, 1 ) ) self.connect( erasure_dec, ( ctf_gate, 1 ) ) self.id_dec = self._id_decoder = id_gate self.ctf = ctf_gate print "Erasure decision for IDs is enabled" if options.log: id_dec_f = gr.short_to_float() self.connect(self.id_dec,id_dec_f) log_to_file(self, id_dec_f, "data/id_dec_out.float") if options.log: log_to_file(self, id_bfilt, "data/id_blockfilter_out.compl") # TODO: refactor names if options.log: map_src_f = gr.char_to_float(dsubc) self.connect(map_src,map_src_f) log_to_file(self, map_src_f, "data/map_src_out.float") ## Allocation Control if options.static_allocation: #DEBUG if options.coding: mode = 1 # Coding mode 1-9 bitspermode = [0.5,1,1.5,2,3,4,4.5,5,6] # Information bits per mode bitcount_vec = [(int)(config.data_subcarriers*config.frame_data_blocks*bitspermode[mode-1])] bitloading = mode else: bitloading = 1 bitcount_vec = [config.data_subcarriers*config.frame_data_blocks*bitloading] #bitcount_vec = [config.data_subcarriers*config.frame_data_blocks] self.bitcount_src = blocks.vector_source_i(bitcount_vec,True,1) # 0s for ID block, then data #bitloading_vec = [0]*dsubc+[0]*(dsubc/2)+[2]*(dsubc/2) bitloading_vec = [0]*dsubc+[bitloading]*dsubc bitloading_src = blocks.vector_source_b(bitloading_vec,True,dsubc) power_vec = [1]*config.data_subcarriers power_src = blocks.vector_source_f(power_vec,True,dsubc) else: self.allocation_buffer = ofdm.allocation_buffer(config.data_subcarriers, config.frame_data_blocks, "tcp://"+options.tx_hostname+":3333",config.coding) self.bitcount_src = (self.allocation_buffer,0) bitloading_src = (self.allocation_buffer,1) power_src = (self.allocation_buffer,2) self.connect(self.id_dec, self.allocation_buffer) if options.benchmarking: self.allocation_buffer.set_allocation([4]*config.data_subcarriers,[1]*config.data_subcarriers) if options.log: log_to_file(self, self.bitcount_src, "data/bitcount_src_rx.int") log_to_file(self, bitloading_src, "data/bitloading_src_rx.char") log_to_file(self, power_src, "data/power_src_rx.cmplx") log_to_file(self, self.id_dec, "data/id_dec_rx.short") ## Power Deallocator pda = self._power_deallocator = multiply_frame_fc(config.frame_data_part, dsubc) self.connect(pda_in,(pda,0)) self.connect(power_src,(pda,1)) ## Demodulator # if 0: # ac_vector = [0.0+0.0j]*208 # ac_vector[0] = (2*10**(-0.452)) # ac_vector[3] = (10**(-0.651)) # ac_vector[7] = (10**(-1.151)) # csi_vector_inv=abs(numpy.fft.fft(numpy.sqrt(ac_vector)))**2 # dm_csi = numpy.fft.fftshift(csi_vector_inv) # TODO dm_csi = [1]*dsubc # TODO dm_csi = blocks.vector_source_f(dm_csi,True) ## Depuncturer dp_trig = [0]*(config.frame_data_blocks/2) dp_trig[0] = 1 dp_trig = blocks.vector_source_b(dp_trig,True) # TODO if(options.coding): fo=ofdm.fsm(1,2,[91,121]) if options.interleave: int_object=trellis.interleaver(2000,666) deinterlv = trellis.permutation(int_object.K(),int_object.DEINTER(),1,gr.sizeof_float) demod = self._data_demodulator = generic_softdemapper_vcf(dsubc, config.frame_data_part, config.coding) #self.connect(dm_csi,blocks.stream_to_vector(gr.sizeof_float,dsubc),(demod,2)) if(options.ideal): self.connect(dm_csi,blocks.stream_to_vector(gr.sizeof_float,dsubc),(demod,2)) else: dm_csi_filter = self.dm_csi_filter = filter.single_pole_iir_filter_ff(0.01,dsubc) self.connect(self.ctf, self.dm_csi_filter,(demod,2)) #log_to_file(self, dm_csi_filter, "data/softs_csi.float") #self.connect(dm_trig,(demod,3)) else: demod = self._data_demodulator = generic_demapper_vcb(dsubc, config.frame_data_part) if options.benchmarking: # Do receiver benchmarking until the number of frames x symbols are collected self.connect(pda,blocks.head(gr.sizeof_gr_complex*dsubc, options.N*config.frame_data_blocks),demod) else: self.connect(pda,demod) self.connect(bitloading_src,(demod,1)) if(options.coding): ## Depuncturing if not options.nopunct: depuncturing = depuncture_ff(dsubc,0) frametrigger_bitmap_filter = blocks.vector_source_b([1,0],True) self.connect(bitloading_src,(depuncturing,1)) self.connect(dp_trig,(depuncturing,2)) ## Decoding chunkdivisor = int(numpy.ceil(config.frame_data_blocks/5.0)) print "Number of chunks at Viterbi decoder: ", chunkdivisor decoding = self._data_decoder = ofdm.viterbi_combined_fb(fo,dsubc,-1,-1,2,chunkdivisor,[-1,-1,-1,1,1,-1,1,1],ofdm.TRELLIS_EUCLIDEAN) if options.log and options.coding: log_to_file(self, decoding, "data/decoded.char") if not options.nopunct: log_to_file(self, depuncturing, "data/vit_in.float") if not options.nopunct: if options.interleave: self.connect(demod,deinterlv,depuncturing,decoding) else: self.connect(demod,depuncturing,decoding) else: self.connect(demod,decoding) self.connect(self.bitcount_src, multiply_const_ii(1./chunkdivisor), (decoding,1)) if options.scatterplot or options.scatter_plot_before_phase_tracking: if self.ideal2 is False: scatter_vec_elem = self._scatter_vec_elem = ofdm.vector_element(dsubc,40) scatter_s2v = self._scatter_s2v = blocks.stream_to_vector(gr.sizeof_gr_complex,config.frame_data_blocks) scatter_id_filt = skip(gr.sizeof_gr_complex*dsubc,config.frame_data_blocks) scatter_id_filt.skip_call(0) scatter_trig = [0]*config.frame_data_part scatter_trig[0] = 1 scatter_trig = blocks.vector_source_b(scatter_trig,True) self.connect(scatter_trig,(scatter_id_filt,1)) self.connect(scatter_vec_elem,scatter_s2v) if not options.scatter_plot_before_phase_tracking: print "Enabling Scatterplot for data subcarriers" self.connect(pda,scatter_id_filt,scatter_vec_elem) # Work on this #scatter_sink = ofdm.scatterplot_sink(dsubc) #self.connect(pda,scatter_sink) #self.connect(map_src,(scatter_sink,1)) #self.connect(dm_trig,(scatter_sink,2)) #print "Enabled scatterplot gui interface" self.zmq_probe_scatter = zeromq.pub_sink(gr.sizeof_gr_complex,config.frame_data_blocks, "tcp://*:5560") self.connect(scatter_s2v, blocks.keep_one_in_n(gr.sizeof_gr_complex*config.frame_data_blocks,20), self.zmq_probe_scatter) else: print "Enabling Scatterplot for data before phase tracking" inner_rx = inner_receiver.before_phase_tracking #scatter_sink2 = ofdm.scatterplot_sink(dsubc,"phase_tracking") op = copy.copy(options) op.enable_erasure_decision = False new_framesampler = ofdm_frame_sampler(op) self.connect( inner_rx, new_framesampler ) self.connect( orig_frame_start, (new_framesampler,1) ) new_ps_filter = pilot_subcarrier_filter() new_pb_filter = fbmc_pilot_block_filter() self.connect( (new_framesampler,1), (new_pb_filter,1) ) self.connect( new_framesampler, new_pb_filter, new_ps_filter, scatter_id_filt, scatter_vec_elem ) #self.connect( new_ps_filter, scatter_sink2 ) #self.connect( map_src, (scatter_sink2,1)) #self.connect( dm_trig, (scatter_sink2,2)) if options.log: if(options.coding): log_to_file(self, demod, "data/data_stream_out.float") else: data_f = gr.char_to_float() self.connect(demod,data_f) log_to_file(self, data_f, "data/data_stream_out.float") if options.sfo_feedback: used_id_bits = 8 rep_id_bits = config.data_subcarriers/used_id_bits seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] id_enc = ofdm.repetition_encoder_sb(used_id_bits,rep_id_bits,whitener_pn) self.connect( self.id_dec, id_enc ) id_mod = ofdm_bpsk_modulator(dsubc) self.connect( id_enc, id_mod ) id_mod_conj = gr.conjugate_cc(dsubc) self.connect( id_mod, id_mod_conj ) id_mult = blocks.multiply_vcc(dsubc) self.connect( id_bfilt, ( id_mult,0) ) self.connect( id_mod_conj, ( id_mult,1) ) # id_mult_avg = filter.single_pole_iir_filter_cc(0.01,dsubc) # self.connect( id_mult, id_mult_avg ) id_phase = gr.complex_to_arg(dsubc) self.connect( id_mult, id_phase ) log_to_file( self, id_phase, "data/id_phase.float" ) est=ofdm.LS_estimator_straight_slope(dsubc) self.connect(id_phase,est) slope=blocks.multiply_const_ff(1e6/2/3.14159265) self.connect( (est,0), slope ) log_to_file( self, slope, "data/slope.float" ) log_to_file( self, (est,1), "data/offset.float" ) # ------------------------------------------------------------------------ # # Display some information about the setup if config._verbose: self._print_verbage() ## debug logging ## if options.log: # log_to_file(self,self.ofdm_symbols,"data/unequalized_rx_ofdm_symbols.compl") # log_to_file(self,self.ofdm_symbols,"data/unequalized_rx_ofdm_symbols.float",mag=True) fftlen = 256 my_window = window.hamming(fftlen) #.blackmanharris(fftlen) rxs_sampler = vector_sampler(gr.sizeof_gr_complex,fftlen) rxs_sampler_vect = concatenate([[1],[0]*49]) rxs_trigger = blocks.vector_source_b(rxs_sampler_vect.tolist(),True) rxs_window = blocks.multiply_const_vcc(my_window) rxs_spectrum = gr.fft_vcc(fftlen,True,[],True) rxs_mag = gr.complex_to_mag(fftlen) rxs_avg = filter.single_pole_iir_filter_ff(0.01,fftlen) #rxs_logdb = blocks.nlog10_ff(20.0,fftlen,-20*log10(fftlen)) rxs_logdb = gr.kludge_copy( gr.sizeof_float * fftlen ) rxs_decimate_rate = gr.keep_one_in_n(gr.sizeof_float*fftlen,1) self.connect(rxs_trigger,(rxs_sampler,1)) self.connect(self.input,rxs_sampler,rxs_window, rxs_spectrum,rxs_mag,rxs_avg,rxs_logdb, rxs_decimate_rate) log_to_file( self, rxs_decimate_rate, "data/psd_input.float" ) #output branches self.publish_rx_performance_measure()
def __init__(self, options): gr.top_block.__init__(self) if options.rx_freq is not None: u = uhd_receiver(options.args, options.bandwidth, options.rx_freq, options.rx_gain, options.spec, options.antenna, options.verbose) elif options.infile is not None: u = gr.file_source(gr.sizeof_gr_complex, options.infile) else: import sys sys.stderr.write("--freq or --infile must be specified\n") raise SystemExit self.scope = None if options.outfile is not None: rx = gr.file_sink(gr.sizeof_gr_complex, options.outfile) else: rx = ofdm_rxtx.RX(options) data_tones = rx.params.data_tones if options.rxdata is not None: if options.rxdata == '.': import scope # scope it out rxs = gr.vector_to_stream(gr.sizeof_gr_complex, data_tones) self.connect(rx, rxs) self.scope = scope.scope(self, rxs, 'Frame SNR', isComplex=True) else: if options.char > 0: # rail and scale self.connect( rx, gr.vector_to_stream(gr.sizeof_float, data_tones * 2), gr.multiply_const_ff(128.0 * (2**0.5) / options.char), gr.rail_ff(-128.0, 127.0), gr.float_to_char(), gr.file_sink(gr.sizeof_char, options.rxdata)) else: self.connect( rx, gr.file_sink(data_tones * gr.sizeof_gr_complex, options.rxdata)) if options.snrdata is not None: # select one of the snr modes snr = ofdm_rxtx.SNR(rx.params.data_tones, options.size, mode=options.snrmode) if options.char > 0: # NOTE: we use repeat, assuming the file is long enough or properly aligned data = gr.stream_to_vector(gr.sizeof_float, data_tones * 2) self.connect( gr.file_source(gr.sizeof_char, options.txdata, repeat=True), gr.char_to_float(), gr.multiply_const_ff(options.char * (2**-0.5) / 128.0), data) else: data = ofdm_rxtx.make_data(rx.params.data_tones, options.size, options.txdata) self.connect(rx, (snr, 0)) self.connect(data, (snr, 1)) if options.snrdata == '-': # print it out msgq = gr.msg_queue(16) self.connect(snr, gr.message_sink(gr.sizeof_float, msgq, True)) self.watcher = ofdm_rxtx.queue_watcher(msgq) elif options.snrdata == '.': import scope # scope it out self.scope = scope.scope(self, snr, 'Frame SNR') else: self.connect( snr, gr.file_sink(gr.sizeof_float, options.snrdata)) else: pass #self.connect(rx, gr.null_sink(symbol_size)) # XXX do we still need this? self.connect(u, rx)
def __init__(self, fft_length, block_length, block_header, range, options): gr.hier_block2.__init__( self, "integer_fo_estimator", gr.io_signature3(3, 3, gr.sizeof_gr_complex, gr.sizeof_float, gr.sizeof_char), gr.io_signature2(3, 3, gr.sizeof_float, gr.sizeof_char)) raise NotImplementedError, "Obsolete class" self._range = range # threshold after integer part frequency offset estimation # if peak value below threshold, assume false triggering self._thr_lo = 0.4 #0.19 # empirically found threshold. see ioe_metric.float self._thr_hi = 0.4 #0.2 # stuff to be removed after bugfix for hierblock2s self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.time_sync = gr.kludge_copy(gr.sizeof_char) self.epsilon = (self, 1) self.connect((self, 0), self.input) self.connect((self, 2), self.time_sync) delay(gr.sizeof_char, block_header.schmidl_fine_sync[0] * block_length) # sample ofdm symbol (preamble 1 and 2) sampler_symbol1 = vector_sampler(gr.sizeof_gr_complex, fft_length) sampler_symbol2 = vector_sampler(gr.sizeof_gr_complex, fft_length) time_delay1 = delay(gr.sizeof_char, block_length * block_header.schmidl_fine_sync[1]) self.connect(self.input, (sampler_symbol1, 0)) self.connect(self.input, (sampler_symbol2, 0)) if block_header.schmidl_fine_sync[0] > 0: time_delay0 = delay( gr.sizeof_char, block_length * block_header.schmidl_fine_sync[0]) self.connect(self.time_sync, time_delay0, (sampler_symbol1, 1)) else: self.connect(self.time_sync, (sampler_symbol1, 1)) self.connect(self.time_sync, time_delay1, (sampler_symbol2, 1)) # negative fractional frequency offset estimate epsilon = gr.multiply_const_ff(-1.0) self.connect(self.epsilon, epsilon) # compensate for fractional frequency offset on per symbol base # freq_shift: vector length, modulator sensitivity # freq_shift third input: reset phase accumulator # symbol/preamble 1 freq_shift_sym1 = frequency_shift_vcc(fft_length, 1.0 / fft_length) self.connect(sampler_symbol1, (freq_shift_sym1, 0)) self.connect(epsilon, (freq_shift_sym1, 1)) self.connect(gr.vector_source_b([1], True), (freq_shift_sym1, 2)) # symbol/preamble 2 freq_shift_sym2 = frequency_shift_vcc(fft_length, 1.0 / fft_length) self.connect(sampler_symbol2, (freq_shift_sym2, 0)) self.connect(epsilon, (freq_shift_sym2, 1)) self.connect(gr.vector_source_b([1], True), (freq_shift_sym2, 2)) # fourier transfrom on both preambles fft_sym1 = gr.fft_vcc(fft_length, True, [], True) # Forward + Blockshift fft_sym2 = gr.fft_vcc(fft_length, True, [], True) # Forward + Blockshift # calculate schmidl's metric for estimation of freq. offset's integer part assert (hasattr(block_header, "schmidl_fine_sync")) pre1 = block_header.pilotsym_fd[block_header.schmidl_fine_sync[0]] pre2 = block_header.pilotsym_fd[block_header.schmidl_fine_sync[1]] diff_pn = concatenate( [[conjugate(math.sqrt(2) * pre2[2 * i] / pre1[2 * i]), 0.0j] for i in arange(len(pre1) / 2)]) cfo_estimator = schmidl_cfo_estimator(fft_length, len(pre1), self._range, diff_pn) self.connect(freq_shift_sym1, fft_sym1, (cfo_estimator, 0)) # preamble 1 self.connect(freq_shift_sym2, fft_sym2, (cfo_estimator, 1)) # preamble 2 # search for maximum and its argument in interval [-range .. +range] #arg_max = arg_max_vff(2*self._range + 1) arg_max_s = gr.argmax_fs(2 * self._range + 1) arg_max = gr.short_to_float() ifo_max = gr.max_ff(2 * self._range + 1) # vlen ifo_estimate = gr.add_const_ff(-self._range) self.connect(cfo_estimator, arg_max_s, arg_max, ifo_estimate) self.connect(cfo_estimator, ifo_max) self.connect((arg_max_s, 1), gr.null_sink(gr.sizeof_short)) # threshold maximal value ifo_threshold = gr.threshold_ff(self._thr_lo, self._thr_hi, 0.0) ifo_thr_f2b = gr.float_to_char() self.connect(ifo_max, ifo_threshold, ifo_thr_f2b) # gating the streams ifo_estimate (integer part) and epsilon (frac. part) # if the metric's peak value was above the chosen threshold, assume to have # found a new burst. peak value below threshold results in blocking the # streams self.gate = gate_ff() self.connect(ifo_thr_f2b, (self.gate, 0)) # threshold stream self.connect(ifo_estimate, (self.gate, 1)) self.connect(epsilon, (self.gate, 2)) # peak filtering # resynchronize and suppress peaks that didn't match a preamble filtered_time_sync = peak_resync_bb(True) # replace self.connect(self.time_sync, (filtered_time_sync, 0)) self.connect(ifo_thr_f2b, (filtered_time_sync, 1)) # find complete estimation for frequency offset # add together fractional and integer part freq_offset = gr.add_ff() self.connect((self.gate, 1), gr.multiply_const_ff(-1.0), (freq_offset, 0)) # integer offset self.connect((self.gate, 2), (freq_offset, 1)) # frac offset # output connections self.connect(freq_offset, (self, 0)) self.connect(filtered_time_sync, (self, 1)) self.connect((self.gate, 0), (self, 2)) # used for frame trigger ######################################### # debugging if options.log: self.epsilon2_sink = gr.vector_sink_f() self.connect(epsilon, self.epsilon2_sink) self.connect( cfo_estimator, gr.file_sink(gr.sizeof_float * (self._range * 2 + 1), "data/ioe_metric.float")) # output joint stream preamble_stream = gr.streams_to_vector( fft_length * gr.sizeof_gr_complex, 2) self.connect(fft_sym1, (preamble_stream, 0)) self.connect(fft_sym2, (preamble_stream, 1)) self.connect( preamble_stream, gr.file_sink(gr.sizeof_gr_complex * 2 * fft_length, "data/preambles.compl")) # output, preambles before and after correction, magnitude and complex spectrum self.connect( sampler_symbol1, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre1_bef.compl")) self.connect( sampler_symbol1, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre1_bef.float")) self.connect( sampler_symbol2, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre2_bef.compl")) self.connect( sampler_symbol2, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre2_bef.float")) self.connect( freq_shift_sym1, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre1.compl")) self.connect( freq_shift_sym1, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre1.float")) self.connect( freq_shift_sym2, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre2.compl")) self.connect( freq_shift_sym2, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre2.float")) # calculate epsilon from corrected source to check function test_cp = cyclic_prefixer(fft_length, block_length) test_eps = foe(fft_length) self.connect(freq_shift_sym1, test_cp, test_eps, gr.file_sink(gr.sizeof_float, "data/eps_after.float")) try: gr.hier_block.update_var_names(self, "ifo_estimator", vars()) gr.hier_block.update_var_names(self, "ifo_estimator", vars(self)) except: pass
def __init__(self, fft_length, block_length, block_header, range, options): gr.hier_block2.__init__(self, "integer_fo_estimator", gr.io_signature3(3,3,gr.sizeof_gr_complex,gr.sizeof_float,gr.sizeof_char), gr.io_signature2(3,3,gr.sizeof_float,gr.sizeof_char)) raise NotImplementedError,"Obsolete class" self._range = range # threshold after integer part frequency offset estimation # if peak value below threshold, assume false triggering self._thr_lo = 0.4 #0.19 # empirically found threshold. see ioe_metric.float self._thr_hi = 0.4 #0.2 # stuff to be removed after bugfix for hierblock2s self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.time_sync = gr.kludge_copy(gr.sizeof_char) self.epsilon = (self,1) self.connect((self,0),self.input) self.connect((self,2),self.time_sync) delay(gr.sizeof_char, block_header.schmidl_fine_sync[0]*block_length) # sample ofdm symbol (preamble 1 and 2) sampler_symbol1 = vector_sampler(gr.sizeof_gr_complex,fft_length) sampler_symbol2 = vector_sampler(gr.sizeof_gr_complex,fft_length) time_delay1 = delay(gr.sizeof_char,block_length*block_header.schmidl_fine_sync[1]) self.connect(self.input, (sampler_symbol1,0)) self.connect(self.input, (sampler_symbol2,0)) if block_header.schmidl_fine_sync[0] > 0: time_delay0 = delay(gr.sizeof_char,block_length*block_header.schmidl_fine_sync[0]) self.connect(self.time_sync, time_delay0, (sampler_symbol1,1)) else: self.connect(self.time_sync, (sampler_symbol1,1)) self.connect(self.time_sync, time_delay1, (sampler_symbol2,1)) # negative fractional frequency offset estimate epsilon = gr.multiply_const_ff(-1.0) self.connect(self.epsilon, epsilon) # compensate for fractional frequency offset on per symbol base # freq_shift: vector length, modulator sensitivity # freq_shift third input: reset phase accumulator # symbol/preamble 1 freq_shift_sym1 = frequency_shift_vcc(fft_length, 1.0/fft_length) self.connect(sampler_symbol1, (freq_shift_sym1,0)) self.connect(epsilon, (freq_shift_sym1,1)) self.connect(gr.vector_source_b([1], True), (freq_shift_sym1,2)) # symbol/preamble 2 freq_shift_sym2 = frequency_shift_vcc(fft_length, 1.0/fft_length) self.connect(sampler_symbol2, (freq_shift_sym2,0)) self.connect(epsilon, (freq_shift_sym2,1)) self.connect(gr.vector_source_b([1], True), (freq_shift_sym2,2)) # fourier transfrom on both preambles fft_sym1 = gr.fft_vcc(fft_length, True, [], True) # Forward + Blockshift fft_sym2 = gr.fft_vcc(fft_length, True, [], True) # Forward + Blockshift # calculate schmidl's metric for estimation of freq. offset's integer part assert(hasattr(block_header, "schmidl_fine_sync")) pre1 = block_header.pilotsym_fd[block_header.schmidl_fine_sync[0]] pre2 = block_header.pilotsym_fd[block_header.schmidl_fine_sync[1]] diff_pn = concatenate([[conjugate(math.sqrt(2)*pre2[2*i]/pre1[2*i]),0.0j] for i in arange(len(pre1)/2)]) cfo_estimator = schmidl_cfo_estimator(fft_length, len(pre1), self._range, diff_pn) self.connect(freq_shift_sym1, fft_sym1, (cfo_estimator,0)) # preamble 1 self.connect(freq_shift_sym2, fft_sym2, (cfo_estimator,1)) # preamble 2 # search for maximum and its argument in interval [-range .. +range] #arg_max = arg_max_vff(2*self._range + 1) arg_max_s = gr.argmax_fs(2*self._range+1) arg_max = gr.short_to_float() ifo_max = gr.max_ff(2*self._range + 1) # vlen ifo_estimate = gr.add_const_ff(-self._range) self.connect(cfo_estimator, arg_max_s, arg_max, ifo_estimate) self.connect(cfo_estimator, ifo_max) self.connect((arg_max_s,1),gr.null_sink(gr.sizeof_short)) # threshold maximal value ifo_threshold = gr.threshold_ff(self._thr_lo, self._thr_hi, 0.0) ifo_thr_f2b = gr.float_to_char() self.connect(ifo_max, ifo_threshold, ifo_thr_f2b) # gating the streams ifo_estimate (integer part) and epsilon (frac. part) # if the metric's peak value was above the chosen threshold, assume to have # found a new burst. peak value below threshold results in blocking the # streams self.gate = gate_ff() self.connect(ifo_thr_f2b, (self.gate,0)) # threshold stream self.connect(ifo_estimate, (self.gate,1)) self.connect(epsilon, (self.gate,2)) # peak filtering # resynchronize and suppress peaks that didn't match a preamble filtered_time_sync = peak_resync_bb(True) # replace self.connect(self.time_sync, (filtered_time_sync,0)) self.connect(ifo_thr_f2b, (filtered_time_sync,1)) # find complete estimation for frequency offset # add together fractional and integer part freq_offset = gr.add_ff() self.connect((self.gate,1), gr.multiply_const_ff(-1.0), (freq_offset,0)) # integer offset self.connect((self.gate,2), (freq_offset,1)) # frac offset # output connections self.connect(freq_offset, (self,0)) self.connect(filtered_time_sync, (self,1)) self.connect((self.gate,0), (self,2)) # used for frame trigger ######################################### # debugging if options.log: self.epsilon2_sink = gr.vector_sink_f() self.connect(epsilon, self.epsilon2_sink) self.connect(cfo_estimator, gr.file_sink(gr.sizeof_float*(self._range*2+1), "data/ioe_metric.float")) # output joint stream preamble_stream = gr.streams_to_vector(fft_length * gr.sizeof_gr_complex, 2) self.connect(fft_sym1, (preamble_stream,0)) self.connect(fft_sym2, (preamble_stream,1)) self.connect(preamble_stream, gr.file_sink(gr.sizeof_gr_complex * 2 * fft_length, "data/preambles.compl")) # output, preambles before and after correction, magnitude and complex spectrum self.connect(sampler_symbol1, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre1_bef.compl")) self.connect(sampler_symbol1, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre1_bef.float")) self.connect(sampler_symbol2, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre2_bef.compl")) self.connect(sampler_symbol2, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre2_bef.float")) self.connect(freq_shift_sym1, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length,"data/pre1.compl")) self.connect(freq_shift_sym1, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length,"data/pre1.float")) self.connect(freq_shift_sym2, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length,"data/pre2.compl")) self.connect(freq_shift_sym2, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length,"data/pre2.float")) # calculate epsilon from corrected source to check function test_cp = cyclic_prefixer(fft_length, block_length) test_eps = foe(fft_length) self.connect(freq_shift_sym1, test_cp, test_eps, gr.file_sink(gr.sizeof_float, "data/eps_after.float")) try: gr.hier_block.update_var_names(self, "ifo_estimator", vars()) gr.hier_block.update_var_names(self, "ifo_estimator", vars(self)) except: pass
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, agc_max=100, agc_decay=0.1, freq_offset=1000000, outfile="datafifo", bandpass_bandwidth=20, threshold_buffer=0.25, threshold_center=0.5, agc_attack=0.1, bandpass_transition_width=1000000, ): gr.top_block.__init__(self, "Collect") ################################################## # Parameters ################################################## self.agc_max = agc_max self.agc_decay = agc_decay self.freq_offset = freq_offset self.outfile = outfile self.bandpass_bandwidth = bandpass_bandwidth self.threshold_buffer = threshold_buffer self.threshold_center = threshold_center self.agc_attack = agc_attack self.bandpass_transition_width = bandpass_transition_width ################################################## # Variables ################################################## self.samp_rate = samp_rate = 64000000 ################################################## # Blocks ################################################## self.uhd_usrp_source_0 = uhd.usrp_source(device_addr="", io_type=uhd.io_type.COMPLEX_FLOAT32, num_channels=1) self.uhd_usrp_source_0.set_samp_rate(samp_rate) self.uhd_usrp_source_0.set_center_freq(915000000 - freq_offset, 0) self.uhd_usrp_source_0.set_gain(0, 0) self.uhd_usrp_source_0.set_antenna("TX/RX", 0) self.gr_threshold_ff_0 = gr.threshold_ff( threshold_center - threshold_buffer, threshold_center + threshold_buffer, 0 ) self.gr_map_bb_0 = gr.map_bb(([48, 49])) self.gr_float_to_char_0 = gr.float_to_char() self.gr_file_sink_0 = gr.file_sink(gr.sizeof_char * 1, outfile) self.gr_file_sink_0.set_unbuffered(False) self.gr_complex_to_mag_0 = gr.complex_to_mag(1) self.gr_agc2_xx_0_0 = gr.agc2_cc(agc_attack, agc_decay, 1.0, 1.0, agc_max) self.band_pass_filter_0 = gr.fir_filter_ccf( 1, firdes.band_pass( 1, samp_rate, freq_offset - bandpass_bandwidth / 2, freq_offset + bandpass_bandwidth / 2, bandpass_transition_width, firdes.WIN_HAMMING, 6.76, ), ) ################################################## # Connections ################################################## self.connect((self.gr_float_to_char_0, 0), (self.gr_map_bb_0, 0)) self.connect((self.gr_map_bb_0, 0), (self.gr_file_sink_0, 0)) self.connect((self.uhd_usrp_source_0, 0), (self.gr_agc2_xx_0_0, 0)) self.connect((self.gr_agc2_xx_0_0, 0), (self.band_pass_filter_0, 0)) self.connect((self.gr_threshold_ff_0, 0), (self.gr_float_to_char_0, 0)) self.connect((self.gr_complex_to_mag_0, 0), (self.gr_threshold_ff_0, 0)) self.connect((self.band_pass_filter_0, 0), (self.gr_complex_to_mag_0, 0))
def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="USRP LRIT Receiver") ################################################## # Variables ################################################## self.config_filename = config_filename = "usrp_rx_lrit.cfg" self._saved_decim_config = ConfigParser.ConfigParser() self._saved_decim_config.read(config_filename) try: saved_decim = self._saved_decim_config.getint("main", "decim") except: saved_decim = 160 self.saved_decim = saved_decim self.decim = decim = saved_decim self.symbol_rate = symbol_rate = 293e3 self._saved_gain_mu_config = ConfigParser.ConfigParser() self._saved_gain_mu_config.read(config_filename) try: saved_gain_mu = self._saved_gain_mu_config.getfloat("main", "gain_mu") except: saved_gain_mu = 0.005 self.saved_gain_mu = saved_gain_mu self._saved_gain_config = ConfigParser.ConfigParser() self._saved_gain_config.read(config_filename) try: saved_gain = self._saved_gain_config.getfloat("main", "gain") except: saved_gain = 33 self.saved_gain = saved_gain self._saved_freq_config = ConfigParser.ConfigParser() self._saved_freq_config.read(config_filename) try: saved_freq = self._saved_freq_config.getfloat("main", "freq") except: saved_freq = 137e6 self.saved_freq = saved_freq self._saved_costas_alpha_config = ConfigParser.ConfigParser() self._saved_costas_alpha_config.read(config_filename) try: saved_costas_alpha = self._saved_costas_alpha_config.getfloat("main", "costas_alpha") except: saved_costas_alpha = 0.005 self.saved_costas_alpha = saved_costas_alpha self.samp_rate = samp_rate = 64e6/decim self.sps = sps = samp_rate/symbol_rate self.gain_mu = gain_mu = saved_gain_mu self.gain = gain = saved_gain self.freq = freq = saved_freq self.costas_alpha = costas_alpha = saved_costas_alpha ################################################## # Notebooks ################################################## self.displays = wx.Notebook(self.GetWin(), style=wx.NB_TOP) self.displays.AddPage(grc_wxgui.Panel(self.displays), "USRP RX") self.displays.AddPage(grc_wxgui.Panel(self.displays), "Costas Output") self.GridAdd(self.displays, 2, 0, 1, 3) ################################################## # Controls ################################################## self._decim_text_box = forms.text_box( parent=self.GetWin(), value=self.decim, callback=self.set_decim, label="Decim", converter=forms.int_converter(), ) self.GridAdd(self._decim_text_box, 0, 0, 1, 1) _gain_mu_sizer = wx.BoxSizer(wx.VERTICAL) self._gain_mu_text_box = forms.text_box( parent=self.GetWin(), sizer=_gain_mu_sizer, value=self.gain_mu, callback=self.set_gain_mu, label="Gain Mu", converter=forms.float_converter(), proportion=0, ) self._gain_mu_slider = forms.slider( parent=self.GetWin(), sizer=_gain_mu_sizer, value=self.gain_mu, callback=self.set_gain_mu, minimum=0, maximum=0.5, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_gain_mu_sizer, 1, 1, 1, 1) _gain_sizer = wx.BoxSizer(wx.VERTICAL) self._gain_text_box = forms.text_box( parent=self.GetWin(), sizer=_gain_sizer, value=self.gain, callback=self.set_gain, label="Gain", converter=forms.float_converter(), proportion=0, ) self._gain_slider = forms.slider( parent=self.GetWin(), sizer=_gain_sizer, value=self.gain, callback=self.set_gain, minimum=0, maximum=115, num_steps=115, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_gain_sizer, 0, 1, 1, 1) _freq_sizer = wx.BoxSizer(wx.VERTICAL) self._freq_text_box = forms.text_box( parent=self.GetWin(), sizer=_freq_sizer, value=self.freq, callback=self.set_freq, label="Frequency", converter=forms.float_converter(), proportion=0, ) self._freq_slider = forms.slider( parent=self.GetWin(), sizer=_freq_sizer, value=self.freq, callback=self.set_freq, minimum=135e6, maximum=139e6, num_steps=400, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_freq_sizer, 0, 2, 1, 1) _costas_alpha_sizer = wx.BoxSizer(wx.VERTICAL) self._costas_alpha_text_box = forms.text_box( parent=self.GetWin(), sizer=_costas_alpha_sizer, value=self.costas_alpha, callback=self.set_costas_alpha, label="Costas Alpha", converter=forms.float_converter(), proportion=0, ) self._costas_alpha_slider = forms.slider( parent=self.GetWin(), sizer=_costas_alpha_sizer, value=self.costas_alpha, callback=self.set_costas_alpha, minimum=0, maximum=0.5, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_costas_alpha_sizer, 1, 0, 1, 1) ################################################## # Blocks ################################################## self.gr_add_const_vxx_0 = gr.add_const_vff((48.0, )) self.gr_agc_xx_0 = gr.agc_cc(1e-6, 1.0, 1.0/32767.0, 1.0) self.gr_binary_slicer_fb_0 = gr.binary_slicer_fb() self.gr_char_to_float_0 = gr.char_to_float() self.gr_complex_to_real_0 = gr.complex_to_real(1) self.gr_file_sink_0 = gr.file_sink(gr.sizeof_char*1, "bits.dat") self.gr_file_source_0 = gr.file_source(gr.sizeof_gr_complex*1, "lrit.dat", False) self.gr_float_to_char_0 = gr.float_to_char() self.gr_mpsk_receiver_cc_0 = gr.mpsk_receiver_cc(2, 0, costas_alpha, costas_alpha*costas_alpha/4.0, -0.05, 0.05, 0.5, gain_mu, sps, gain_mu*gain_mu/4.0, 0.05) self.gr_probe_mpsk_snr_c_0 = grc_blks2.probe_mpsk_snr_c( type='snr', alpha=0.0001, probe_rate=10, ) self.gr_throttle_0 = gr.throttle(gr.sizeof_gr_complex*1, samp_rate) self.wxgui_fftsink2_0 = fftsink2.fft_sink_c( self.displays.GetPage(0).GetWin(), baseband_freq=freq, y_per_div=10, y_divs=10, ref_level=50, sample_rate=samp_rate, fft_size=1024, fft_rate=30, average=False, avg_alpha=None, title="Spectrum", peak_hold=False, ) self.displays.GetPage(0).GridAdd(self.wxgui_fftsink2_0.win, 0, 0, 1, 1) self.wxgui_numbersink2_0 = numbersink2.number_sink_f( self.displays.GetPage(1).GetWin(), unit="dB", minval=0, maxval=30, factor=1.0, decimal_places=1, ref_level=0, sample_rate=10, number_rate=10, average=False, avg_alpha=None, label="SNR", peak_hold=False, show_gauge=True, ) self.displays.GetPage(1).GridAdd(self.wxgui_numbersink2_0.win, 2, 0, 1, 1) self.wxgui_scopesink2_0 = scopesink2.scope_sink_c( self.displays.GetPage(0).GetWin(), title="Waveform", sample_rate=samp_rate, v_scale=0.5, t_scale=20.0/samp_rate, ac_couple=False, xy_mode=True, num_inputs=1, ) self.displays.GetPage(0).GridAdd(self.wxgui_scopesink2_0.win, 1, 0, 1, 1) self.wxgui_scopesink2_1 = scopesink2.scope_sink_c( self.displays.GetPage(1).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0.4, t_scale=20.0/samp_rate, ac_couple=False, xy_mode=True, num_inputs=1, ) self.displays.GetPage(1).GridAdd(self.wxgui_scopesink2_1.win, 0, 0, 1, 1) ################################################## # Connections ################################################## self.connect((self.gr_agc_xx_0, 0), (self.wxgui_scopesink2_0, 0)) self.connect((self.gr_file_source_0, 0), (self.gr_throttle_0, 0)) self.connect((self.gr_throttle_0, 0), (self.gr_agc_xx_0, 0)) self.connect((self.gr_probe_mpsk_snr_c_0, 0), (self.wxgui_numbersink2_0, 0)) self.connect((self.gr_mpsk_receiver_cc_0, 0), (self.gr_probe_mpsk_snr_c_0, 0)) self.connect((self.gr_agc_xx_0, 0), (self.gr_mpsk_receiver_cc_0, 0)) self.connect((self.gr_mpsk_receiver_cc_0, 0), (self.wxgui_scopesink2_1, 0)) self.connect((self.gr_agc_xx_0, 0), (self.wxgui_fftsink2_0, 0)) self.connect((self.gr_mpsk_receiver_cc_0, 0), (self.gr_complex_to_real_0, 0)) self.connect((self.gr_complex_to_real_0, 0), (self.gr_binary_slicer_fb_0, 0)) self.connect((self.gr_binary_slicer_fb_0, 0), (self.gr_char_to_float_0, 0)) self.connect((self.gr_char_to_float_0, 0), (self.gr_add_const_vxx_0, 0)) self.connect((self.gr_add_const_vxx_0, 0), (self.gr_float_to_char_0, 0)) self.connect((self.gr_float_to_char_0, 0), (self.gr_file_sink_0, 0))