def __init__(self, samp_rate,sps,alpha,mu,nB,nF,nW,description_name,mode): gr.hier_block2.__init__(self, "physical_layer_driver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3(3, 3, gr.sizeof_gr_complex, gr.sizeof_gr_complex, gr.sizeof_gr_complex*(1+sps*(nB+nF)))) # Output signature self._sps = sps self._alpha = alpha self._mu = mu self._nB = nB self._nF = nF self._nW = nW m = importlib.import_module('digitalhf.physical_layer.'+description_name) self._physical_layer_driver_description = m.PhysicalLayer(sps) self._physical_layer_driver_description.set_mode(mode) ## TODO: get rrc tap information from physical layer description self._rrc_taps = filter.firdes.root_raised_cosine(1.0, samp_rate, samp_rate/sps, 0.35, 11*sps) preamble_offset,preamble_samples = self._physical_layer_driver_description.get_preamble_z() preamble_length = len(preamble_samples) self._rrc_filter = filter.fir_filter_ccc(1, (self._rrc_taps)) self._corr_est = digital.corr_est_cc(symbols = (preamble_samples.tolist()), sps = sps, mark_delay = preamble_offset, threshold = 0.5, threshold_method = 1) self._doppler_correction = digitalhf.doppler_correction_cc(preamble_length, len(preamble_samples)) self._adaptive_filter = digitalhf.adaptive_dfe(sps, nB, nF, nW, mu, alpha) self._msg_proxy = digitalhf.msg_proxy(self._physical_layer_driver_description) self.connect((self, 0), (self._rrc_filter, 0), (self._corr_est, 0), (self._doppler_correction, 0), (self._adaptive_filter, 0), (self, 0)) self.connect((self._corr_est, 1), ## correlation (self, 1)) self.connect((self._adaptive_filter, 1), ## taps (self, 2)) self.msg_connect((self._doppler_correction, 'doppler'), (self._msg_proxy, 'doppler')) self.msg_connect((self._msg_proxy, 'doppler'), (self._doppler_correction, 'doppler')) self.msg_connect((self._adaptive_filter, 'frame_info'), (self._msg_proxy, 'frame_info')) self.msg_connect((self._msg_proxy, 'frame_info'), (self._adaptive_filter, 'frame_info')) constellations_data = self._physical_layer_driver_description.get_constellations() constellations_msg = pmt.to_pmt([{'idx': idx, 'points': c['points'], 'symbols': c['symbols']} for (idx,c) in enumerate(constellations_data)]) self._adaptive_filter.to_basic_block()._post(pmt.intern('constellations'), constellations_msg) self.message_port_register_hier_out('soft_dec') self.msg_connect((self._adaptive_filter, 'soft_dec'), (self, 'soft_dec')) self.msg_connect((self._msg_proxy, 'soft_dec'), (self, 'soft_dec'))
def __init__(self, m, n, nsamples, angular_resolution, frequency, array_spacing, antenna_array, output_spectrum=False): self.m = m self.n = n self.nsamples = nsamples self.angular_resolution = angular_resolution self.l = 299792458.0 / frequency self.antenna_array = [[array_spacing * x, array_spacing * y] for [x, y] in antenna_array] if (nsamples % m) != 0: raise Exception("nsamples must be multiple of m") if output_spectrum: output_sig = gr.io_signature3( 3, 3, (gr.sizeof_float * n), (gr.sizeof_float * n), (gr.sizeof_float * angular_resolution)) else: output_sig = gr.io_signature2(2, 2, (gr.sizeof_float * n), (gr.sizeof_float * n)) # output_sig = gr.io_signature2(2, 2, (gr.sizeof_float * n), (gr.sizeof_float * angular_resolution)) #else: # output_sig = gr.io_signature(1, 1, (gr.sizeof_float * n)) gr.hier_block2.__init__( self, "music_doa_helper", gr.io_signature(1, 1, (gr.sizeof_gr_complex * nsamples)), output_sig) #gr.io_signature3(1, 3, (gr.sizeof_float * n), # (gr.sizeof_float * angular_resolution), # (gr.sizeof_float * angular_resolution))) print "MUSIC DOA Helper: M: %d, N: %d, # samples: %d, steps of %f degress, lambda: %f, array: %s" % ( self.m, self.n, self.nsamples, (360.0 / self.angular_resolution), self.l, str(self.antenna_array)) #print "--> Calculating array response..." self.array_response = calculate_antenna_array_response( self.antenna_array, self.angular_resolution, self.l) #print "--> Done." #print self.array_response self.impl = baz.music_doa(self.m, self.n, self.nsamples, self.array_response, self.angular_resolution) self.connect(self, self.impl) self.connect((self.impl, 0), (self, 0)) self.connect((self.impl, 1), (self, 1)) if output_spectrum: self.connect((self.impl, 2), (self, 2))
def __init__(self): gr.hier_block2.__init__(self,"rpsinkdummy", gr.io_signature3(4,4,gr.sizeof_short, gr.sizeof_float*vlen, gr.sizeof_float), gr.io_signature (0,0,0)) terminate_stream( self, (self,0) ) terminate_stream( self, (self,1) ) terminate_stream( self, (self,2) ) terminate_stream( self, (self,3) )
def __init__(self, m, n, nsamples, angular_resolution, frequency, array_spacing, antenna_array, output_spectrum=False): self.m = m self.n = n self.nsamples = nsamples self.angular_resolution = angular_resolution self.l = 299792458.0 / frequency self.antenna_array = [[array_spacing * x, array_spacing * y] for [x,y] in antenna_array] if (nsamples % m) != 0: raise Exception("nsamples must be multiple of m") if output_spectrum: output_sig = gr.io_signature3(3, 3, (gr.sizeof_float * n), (gr.sizeof_float * n), (gr.sizeof_float * angular_resolution)) else: output_sig = gr.io_signature2(2, 2, (gr.sizeof_float * n), (gr.sizeof_float * n)) # output_sig = gr.io_signature2(2, 2, (gr.sizeof_float * n), (gr.sizeof_float * angular_resolution)) #else: # output_sig = gr.io_signature(1, 1, (gr.sizeof_float * n)) gr.hier_block2.__init__(self, "music_doa_helper", gr.io_signature(1, 1, (gr.sizeof_gr_complex * nsamples)), output_sig) #gr.io_signature3(1, 3, (gr.sizeof_float * n), # (gr.sizeof_float * angular_resolution), # (gr.sizeof_float * angular_resolution))) print "MUSIC DOA Helper: M: %d, N: %d, # samples: %d, steps of %f degress, lambda: %f, array: %s" % ( self.m, self.n, self.nsamples, (360.0/self.angular_resolution), self.l, str(self.antenna_array) ) #print "--> Calculating array response..." self.array_response = calculate_antenna_array_response(self.antenna_array, self.angular_resolution, self.l) #print "--> Done." #print self.array_response self.impl = baz.music_doa(self.m, self.n, self.nsamples, self.array_response, self.angular_resolution) self.connect(self, self.impl) self.connect((self.impl, 0), (self, 0)) self.connect((self.impl, 1), (self, 1)) if output_spectrum: self.connect((self.impl, 2), (self, 2))
def __init__(self, samp_rate_hz, sps, SF, shr, filtered_preamble_code, alpha=1e-3, beta=5, time_gap_chips=11, max_offset_hz=0, max_num_filters=1, output_correlator_index=0): gr.hier_block2.__init__( self, "SpaRSe_synchronization_cc", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3(3, 3, gr.sizeof_gr_complex, gr.sizeof_gr_complex, gr.sizeof_float)) # Output signature self.delta_phi = lpwan.SpaRSe_utils.calculate_phase_increments( samp_rate_hz, SF, sps, max_offset_hz, max_num_filters) # Define blocks self.rotators = [blocks.rotator_cc(-phi) for phi in self.delta_phi] self.matched_filters = [ filter.fft_filter_ccf(1, np.flipud(np.conj(filtered_preamble_code))) for i in xrange(len(self.delta_phi)) ] self.preamble_detector = preamble_detector_cc(shr, sps, SF, time_gap_chips, alpha, beta, self.delta_phi, output_correlator_index) self.skiphead = blocks.skiphead( gr.sizeof_gr_complex, sps * (SF + time_gap_chips) + 4) # the +4 is "empirical" but well tested for sps=4 # Connect blocks with preamble detector and outputs for i in xrange(len(self.delta_phi)): self.connect(self, self.rotators[i], self.matched_filters[i], (self.preamble_detector, i)) self.connect(self, self.skiphead, (self.preamble_detector, len(self.delta_phi))) for i in xrange(3): self.connect((self.preamble_detector, i), (self, i))
def __init__(self, m, n, nsamples, angular_resolution, frequency, array_spacing, antenna_array, output_spectrum=False): self.c = 299792458.0 # speed of light in m/s [SciPy could be used for this but would introduce a new dependency] self.m = m self.n = n self.nsamples = nsamples self.angular_resolution = angular_resolution self.l_lambda = self.c / frequency # wavelength! Unfortunately 'lambda' is a Python key word. self.antenna_array = [[array_spacing * x, array_spacing * y] for [x, y] in antenna_array] if (nsamples % m) != 0: raise Exception("nsamples must be multiple of m") if output_spectrum: output_sig = gr.io_signature3(3, 3, (gr.sizeof_float * n), (gr.sizeof_float * n), (gr.sizeof_float * angular_resolution)) else: output_sig = gr.io_signature2(2, 2, (gr.sizeof_float * n), (gr.sizeof_float * n)) gr.hier_block2.__init__(self, "music_doa_helper", gr.io_signature(1, 1, (gr.sizeof_gr_complex * nsamples)), output_sig) print "MUSIC DOA Helper: M: %d, N: %d, # samples: %d, steps of %f degress, lambda: %f, array: %s" % ( self.m, self.n, self.nsamples, (360.0 / self.angular_resolution), self.l_lambda, str(self.antenna_array) ) #print "--> Calculating array response..." self.array_response = calculate_antenna_array_response(self.antenna_array, self.angular_resolution, self.l_lambda) #print "--> Done." #print self.array_response self.impl = misc.music_doa(self.m, self.n, self.nsamples, self.array_response, self.angular_resolution) self.connect(self, self.impl) self.connect((self.impl, 0), (self, 0)) self.connect((self.impl, 1), (self, 1)) if output_spectrum: self.connect((self.impl, 2), (self, 2))
def __init__(self, N, sample_rate, search_bw = 1, threshold = 10, threshold_mtm = 0.2, tune_freq = 0, alpha_avg = 1, test_duration = 1, period = 3600, stats = False, output = False, rate = 10, subject_channels = [], valve_callback = None): gr.hier_block2.__init__(self, "coherence_detector", gr.io_signature3(3, 3, gr.sizeof_float*N, gr.sizeof_float*N, gr.sizeof_float*N), gr.io_signature(0, 0, 0)) self.N = N #lenght of the fft for spectral analysis self.sample_rate = sample_rate self.search_bw = search_bw #search bandwidth within each channel self.threshold = threshold #threshold comparison self.threshold_mtm = threshold_mtm self.tune_freq = tune_freq #center frequency self.alpha_avg = alpha_avg #averaging factor for noise level between consecutive measurements self.output = output self.subject_channels = subject_channels self.subject_channels_outcome = [0.1]*len(subject_channels) self.rate = rate self.valve_callback = valve_callback #data queue to share data between theads self.q0 = Queue.Queue() #gnuradio msg queues self.msgq = gr.msg_queue(2) self.msgq1 = gr.msg_queue(2) self.msgq2 = gr.msg_queue(2) #######BLOCKS##### self.sink = blocks.message_sink(gr.sizeof_float * self.N, self.msgq, True) self.sink1 = blocks.message_sink(gr.sizeof_float * self.N, self.msgq1, True) self.sink2 = blocks.message_sink(gr.sizeof_float * self.N, self.msgq2, True) #####CONNECTIONS#### self.connect((self,0), self.sink) self.connect((self,1), self.sink1) self.connect((self,2), self.sink2) self._watcher = watcher(self.msgq, self.msgq1, self.msgq2, self.tune_freq, self.threshold, self.threshold_mtm, self.search_bw, self.N, self.sample_rate, self.q0, self.subject_channels, self.set_subject_channels_outcome, self.rate, self.valve_callback) if self.output != False: self._output_data = output_data(self.q0, self.sample_rate, self.tune_freq, self.N, self.search_bw, self.output, self.subject_channels, self.get_subject_channels_outcome)
def __init__(self, SF, Ku): gr.hier_block2.__init__( self, "mu_demod", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3(3, 3, gr.sizeof_short, gr.sizeof_short, gr.sizeof_float)) # Output signature aggregator = ml_aggreg(SF, Ku) self.connect((aggregator, 0), (self, 0)) self.connect((aggregator, 1), (self, 1)) self.connect((aggregator, 2), (self, 2)) for i in range(Ku): b = partial_ml(SF, i) self.connect((self, 0), b) self.connect((b, 0), (aggregator, i * 3 + 0)) self.connect((b, 1), (aggregator, i * 3 + 1)) self.connect((b, 2), (aggregator, i * 3 + 2))
def __init__(self, sample_rate, ber_threshold=0, # Above which to do search ber_smoothing=0, # Alpha of BER smoother (0.01) ber_duration=0, # Length before trying next combo ber_sample_decimation=1, settling_period=0, pre_lock_duration=0, #ber_sample_skip=0 **kwargs): use_throttle = False base_duration = 1024 if sample_rate > 0: use_throttle = True base_duration *= 4 # Has to be high enough for block-delay if ber_threshold == 0: ber_threshold = 512 * 4 if ber_smoothing == 0: ber_smoothing = 0.01 if ber_duration == 0: ber_duration = base_duration * 2 # 1000ms if settling_period == 0: settling_period = base_duration * 1 # 500ms if pre_lock_duration == 0: pre_lock_duration = base_duration * 2 #1000ms print "Creating Auto-FEC:" print "\tsample_rate:\t\t", sample_rate print "\tber_threshold:\t\t", ber_threshold print "\tber_smoothing:\t\t", ber_smoothing print "\tber_duration:\t\t", ber_duration print "\tber_sample_decimation:\t", ber_sample_decimation print "\tsettling_period:\t", settling_period print "\tpre_lock_duration:\t", pre_lock_duration print "" self.sample_rate = sample_rate self.ber_threshold = ber_threshold #self.ber_smoothing = ber_smoothing self.ber_duration = ber_duration self.settling_period = settling_period self.pre_lock_duration = pre_lock_duration #self.ber_sample_skip = ber_sample_skip self.data_lock = threading.Lock() gr.hier_block2.__init__(self, "auto_fec", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Post MPSK-receiver complex input gr.io_signature3(3, 3, gr.sizeof_char, gr.sizeof_float, gr.sizeof_float)) # Decoded packed bytes, BER metric, lock self.input_watcher = auto_fec_input_watcher(self) default_xform = self.input_watcher.xform_lock self.gr_conjugate_cc_0 = gr.conjugate_cc() self.connect((self, 0), (self.gr_conjugate_cc_0, 0)) # Input self.blks2_selector_0 = grc_blks2.selector( item_size=gr.sizeof_gr_complex*1, num_inputs=2, num_outputs=1, input_index=default_xform.get_conjugation_index(), output_index=0, ) self.connect((self.gr_conjugate_cc_0, 0), (self.blks2_selector_0, 0)) self.connect((self, 0), (self.blks2_selector_0, 1)) # Input self.gr_multiply_const_vxx_3 = gr.multiply_const_vcc((0.707*(1+1j), )) self.connect((self.blks2_selector_0, 0), (self.gr_multiply_const_vxx_3, 0)) self.gr_multiply_const_vxx_2 = gr.multiply_const_vcc((default_xform.get_rotation(), )) # phase_mult self.connect((self.gr_multiply_const_vxx_3, 0), (self.gr_multiply_const_vxx_2, 0)) self.gr_complex_to_float_0_0 = gr.complex_to_float(1) self.connect((self.gr_multiply_const_vxx_2, 0), (self.gr_complex_to_float_0_0, 0)) self.gr_interleave_1 = gr.interleave(gr.sizeof_float*1) self.connect((self.gr_complex_to_float_0_0, 1), (self.gr_interleave_1, 1)) self.connect((self.gr_complex_to_float_0_0, 0), (self.gr_interleave_1, 0)) self.gr_multiply_const_vxx_0 = gr.multiply_const_vff((1, )) # invert self.connect((self.gr_interleave_1, 0), (self.gr_multiply_const_vxx_0, 0)) self.baz_delay_2 = baz.delay(gr.sizeof_float*1, default_xform.get_puncture_delay()) # delay_puncture self.connect((self.gr_multiply_const_vxx_0, 0), (self.baz_delay_2, 0)) self.depuncture_ff_0 = baz.depuncture_ff((_puncture_matrices[self.input_watcher.puncture_matrix][1])) # puncture_matrix self.connect((self.baz_delay_2, 0), (self.depuncture_ff_0, 0)) self.baz_delay_1 = baz.delay(gr.sizeof_float*1, default_xform.get_viterbi_delay()) # delay_viterbi self.connect((self.depuncture_ff_0, 0), (self.baz_delay_1, 0)) self.swap_ff_0 = baz.swap_ff(default_xform.get_viterbi_swap()) # swap_viterbi self.connect((self.baz_delay_1, 0), (self.swap_ff_0, 0)) self.gr_decode_ccsds_27_fb_0 = gr.decode_ccsds_27_fb() if use_throttle: print "==> Using throttle at sample rate:", self.sample_rate self.gr_throttle_0 = gr.throttle(gr.sizeof_float, self.sample_rate) self.connect((self.swap_ff_0, 0), (self.gr_throttle_0, 0)) self.connect((self.gr_throttle_0, 0), (self.gr_decode_ccsds_27_fb_0, 0)) else: self.connect((self.swap_ff_0, 0), (self.gr_decode_ccsds_27_fb_0, 0)) self.connect((self.gr_decode_ccsds_27_fb_0, 0), (self, 0)) # Output bytes self.gr_add_const_vxx_1 = gr.add_const_vff((-4096, )) self.connect((self.gr_decode_ccsds_27_fb_0, 1), (self.gr_add_const_vxx_1, 0)) self.gr_multiply_const_vxx_1 = gr.multiply_const_vff((-1, )) self.connect((self.gr_add_const_vxx_1, 0), (self.gr_multiply_const_vxx_1, 0)) self.connect((self.gr_multiply_const_vxx_1, 0), (self, 1)) # Output BER self.gr_single_pole_iir_filter_xx_0 = gr.single_pole_iir_filter_ff(ber_smoothing, 1) self.connect((self.gr_multiply_const_vxx_1, 0), (self.gr_single_pole_iir_filter_xx_0, 0)) self.gr_keep_one_in_n_0 = blocks.keep_one_in_n(gr.sizeof_float, ber_sample_decimation) self.connect((self.gr_single_pole_iir_filter_xx_0, 0), (self.gr_keep_one_in_n_0, 0)) self.const_source_x_0 = gr.sig_source_f(0, gr.GR_CONST_WAVE, 0, 0, 0) # Last param is const value if use_throttle: lock_throttle_rate = self.sample_rate // 16 print "==> Using lock throttle rate:", lock_throttle_rate self.gr_throttle_1 = gr.throttle(gr.sizeof_float, lock_throttle_rate) self.connect((self.const_source_x_0, 0), (self.gr_throttle_1, 0)) self.connect((self.gr_throttle_1, 0), (self, 2)) else: self.connect((self.const_source_x_0, 0), (self, 2)) self.msg_q = gr.msg_queue(2*256) # message queue that holds at most 2 messages, increase to speed up process self.msg_sink = gr.message_sink(gr.sizeof_float, self.msg_q, dont_block=0) # Block to speed up process self.connect((self.gr_keep_one_in_n_0, 0), self.msg_sink) self.input_watcher.start()
def __init__(self, fft_length, cp_length, preambles, mode='RAW', logging=False): gr.hier_block2.__init__( self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3( 3, 3, # Output signature gr.sizeof_gr_complex, # delayed input gr.sizeof_float, # fine frequency offset gr.sizeof_char # timing indicator )) period = fft_length / 2 window = fft_length / 2 # Calculate the frequency offset from the correlation of the preamble x_corr = blocks.multiply_cc() self.connect(self, blocks.conjugate_cc(), (x_corr, 0)) self.connect(self, blocks.delay(gr.sizeof_gr_complex, period), (x_corr, 1)) P_d = blocks.moving_average_cc(window, 1.0) self.connect(x_corr, P_d) P_d_angle = blocks.complex_to_arg() self.connect(P_d, P_d_angle) # Get the power of the input signal to normalize the output of the correlation R_d = blocks.moving_average_ff(window, 1.0) self.connect(self, blocks.complex_to_mag_squared(), R_d) R_d_squared = blocks.multiply_ff() # this is retarded self.connect(R_d, (R_d_squared, 0)) self.connect(R_d, (R_d_squared, 1)) M_d = blocks.divide_ff() self.connect(P_d, blocks.complex_to_mag_squared(), (M_d, 0)) self.connect(R_d_squared, (M_d, 1)) # Now we need to detect peak of M_d # the peak is up to cp_length long, but noisy, so average it out matched_filter = blocks.moving_average_ff(cp_length, 1.0 / cp_length) # NOTE: the look_ahead parameter doesn't do anything # these parameters are kind of magic, increase 1 and 2 (==) to be more tolerant peak_detect = raw.peak_detector_fb(0.25, 0.55, 30, 0.001) # offset by -1 self.connect(M_d, matched_filter, blocks.add_const_ff(-1), peak_detect) # peak_detect indicates the time M_d is highest, which is the end of the symbol. # nco(t) = P_d_angle(t-offset) sampled at peak_detect(t) # modulate input(t - fft_length) by nco(t) # signal to sample input(t) at t-offset # ########################################################## # IMPORTANT NOTES: # We can't delay by < 0 so instead: # input is delayed by some_length # signal to sample is delayed by some_length - offset ########################################################## # peak_delay and offset are for cutting CP # FIXME: until we figure out how to do this, just offset by 6 or cp_length/2 symbol_length = fft_length + cp_length peak_delay = cp_length offset = 6 #cp_length/2 self.offset = offset # regenerate peak using cross-correlation # * RAW mode: use raw_generate_peak2 block to filter peaks # * PNC mode: use raw_generate_peak3 block to identify the proper peak for two users # PNC mode delays all input peak_delay samples # * cp_length: delay for cross-correlation since auto-correlation is not so accurate # * 3 symbol_length: delay for identifying mode for PNC # * -offset: for cp cut if mode == 'PNC': # The block structure is # signal -> 3*symbol_length+cp_length-offset -> (self,0) [signal] # signal -> cp_length delay -> auto -> (peak,0) -> (self,2) [peak] # signal -> cp_length delay -> (peak,1) -> (self,1) [cfo] # cfo -> cp_length delay -> (peak,2) # # we let cross's signal go faster to identify the beginning # we use cross's peak output to find cfo, so the delay of cfo should = cross delay # we use raw_regenerate_peak to do cross-corr, and symbols energy after STS to identify mode # so the signal is further delayed by three symbols more self.signal_cross_delay = blocks.delay(gr.sizeof_gr_complex, peak_delay) self.cfo_cross_delay = blocks.delay(gr.sizeof_float, peak_delay) LOOK_AHEAD_NSYM = 3 self.connect(self, self.signal_cross_delay) self.connect(P_d_angle, self.cfo_cross_delay) peak = raw.regenerate_peak3(fft_length, fft_length + cp_length, LOOK_AHEAD_NSYM, peak_delay, preambles, False) self.connect(peak_detect, (peak, 0)) self.connect(self.signal_cross_delay, (peak, 1)) self.connect(self.cfo_cross_delay, (peak, 2)) self.signal_delay = blocks.delay( gr.sizeof_gr_complex, LOOK_AHEAD_NSYM * symbol_length + peak_delay - offset) self.connect(self, self.signal_delay, (self, 0)) # signal output self.connect((peak, 1), (self, 1)) # cfo output self.connect((peak, 0), (self, 2)) # peak output if logging: self.connect( self.cfo_cross_delay, blocks.file_sink(gr.sizeof_float, 'logs/input-cfo.dat')) #self.connect(cross, blocks.file_sink(gr.sizeof_float, 'logs/cross.dat')) self.connect( self.signal_cross_delay, blocks.file_sink(gr.sizeof_gr_complex, 'logs/cross-signal.dat')) if mode == 'RAW': LOOK_AHEAD_NSYM = 0 peak = raw.regenerate_peak2(fft_length, fft_length + cp_length, LOOK_AHEAD_NSYM, preambles) self.signal_delay1 = blocks.delay(gr.sizeof_gr_complex, peak_delay - offset) self.signal_delay2 = blocks.delay(gr.sizeof_gr_complex, offset) self.cfo_delay = blocks.delay( gr.sizeof_float, peak_delay) # we generate the same delay with signal self.connect(peak_detect, (peak, 0)) self.connect(P_d_angle, self.cfo_delay, (peak, 1)) self.connect(self, self.signal_delay1, self.signal_delay2, (peak, 2)) self.connect(self.signal_delay1, (self, 0)) # signal output self.connect((peak, 1), (self, 1)) # cfo output self.connect((peak, 0), (self, 2)) # raw peak output if logging: self.connect( self.signal_delay1, blocks.file_sink(gr.sizeof_gr_complex, 'logs/test-out-signal.dat')) self.connect((peak, 0), blocks.file_sink(gr.sizeof_char, 'logs/test-out-peak.datb')) self.connect( self.cfo_delay, blocks.file_sink(gr.sizeof_float, 'logs/test-cfo.dat')) self.connect( peak_detect, blocks.file_sink(gr.sizeof_char, 'logs/test-out-auto-peak.datb')) if logging: #self.connect(self.signal_delay1, blocks.file_sink(gr.sizeof_gr_complex, 'logs/test-signal.dat')) self.connect((peak, 0), blocks.file_sink(gr.sizeof_char, 'logs/peak.datb')) self.connect((peak, 1), blocks.file_sink(gr.sizeof_float, 'logs/peak-cfo.dat')) self.connect(matched_filter, blocks.file_sink(gr.sizeof_float, 'logs/sync-mf.dat')) self.connect(M_d, blocks.file_sink(gr.sizeof_float, 'logs/sync-M.dat')) self.connect( P_d, blocks.file_sink(gr.sizeof_gr_complex, 'logs/sync-pd.dat')) self.connect(R_d, blocks.file_sink(gr.sizeof_float, 'logs/sync-rd.dat')) self.connect( R_d_squared, blocks.file_sink(gr.sizeof_float, 'logs/sync-rd-squared.dat')) self.connect( P_d_angle, blocks.file_sink(gr.sizeof_float, 'logs/sync-angle.dat')) self.connect( peak_detect, blocks.file_sink(gr.sizeof_char, 'logs/sync-peaks.datb'))
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, half_sync, logging=False): gr.hier_block2.__init__( self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3( 3, 3, # Output signature gr.sizeof_gr_complex, # delayed input gr.sizeof_float, # fine frequency offset gr.sizeof_char # timing indicator )) if half_sync: period = fft_length / 2 window = fft_length / 2 else: # full symbol period = fft_length + cp_length window = fft_length # makes the plateau cp_length long # Calculate the frequency offset from the correlation of the preamble x_corr = gr.multiply_cc() self.connect(self, gr.conjugate_cc(), (x_corr, 0)) self.connect(self, gr.delay(gr.sizeof_gr_complex, period), (x_corr, 1)) P_d = gr.moving_average_cc(window, 1.0) self.connect(x_corr, P_d) P_d_angle = gr.complex_to_arg() self.connect(P_d, P_d_angle) # Get the power of the input signal to normalize the output of the correlation R_d = gr.moving_average_ff(window, 1.0) self.connect(self, gr.complex_to_mag_squared(), R_d) R_d_squared = gr.multiply_ff() # this is retarded self.connect(R_d, (R_d_squared, 0)) self.connect(R_d, (R_d_squared, 1)) M_d = gr.divide_ff() self.connect(P_d, gr.complex_to_mag_squared(), (M_d, 0)) self.connect(R_d_squared, (M_d, 1)) # Now we need to detect peak of M_d # NOTE: replaced fir_filter with moving_average for clarity # the peak is up to cp_length long, but noisy, so average it out #matched_filter_taps = [1.0/cp_length for i in range(cp_length)] #matched_filter = gr.fir_filter_fff(1, matched_filter_taps) matched_filter = gr.moving_average_ff(cp_length, 1.0 / cp_length) # NOTE: the look_ahead parameter doesn't do anything # these parameters are kind of magic, increase 1 and 2 (==) to be more tolerant #peak_detect = raw.peak_detector_fb(0.55, 0.55, 30, 0.001) peak_detect = raw.peak_detector_fb(0.25, 0.25, 30, 0.001) # NOTE: gr.peak_detector_fb is broken! #peak_detect = gr.peak_detector_fb(0.55, 0.55, 30, 0.001) #peak_detect = gr.peak_detector_fb(0.45, 0.45, 30, 0.001) #peak_detect = gr.peak_detector_fb(0.30, 0.30, 30, 0.001) # offset by -1 self.connect(M_d, matched_filter, gr.add_const_ff(-1), peak_detect) # peak_detect indicates the time M_d is highest, which is the end of the symbol. # We should try to sample in the middle of the plateau!! # FIXME until we figure out how to do this, just offset by cp_length/2 offset = 6 #cp_length/2 # nco(t) = P_d_angle(t-offset) sampled at peak_detect(t) # modulate input(t - fft_length) by nco(t) # signal to sample input(t) at t-offset # # We can't delay by < 0 so instead: # input is delayed by fft_length # P_d_angle is delayed by offset # signal to sample is delayed by fft_length - offset # phi = gr.sample_and_hold_ff() self.connect(peak_detect, (phi, 1)) self.connect(P_d_angle, gr.delay(gr.sizeof_float, offset), (phi, 0)) #self.connect(P_d_angle, matched_filter2, (phi,0)) # why isn't this better?!? # FIXME: we add fft_length delay so that the preamble is nco corrected too # BUT is this buffering worth it? consider implementing sync as a proper block # delay the input signal to follow the frequency offset signal self.connect(self, gr.delay(gr.sizeof_gr_complex, (fft_length + offset)), (self, 0)) self.connect(phi, (self, 1)) self.connect(peak_detect, (self, 2)) if logging: self.connect(matched_filter, gr.file_sink(gr.sizeof_float, "sync-mf.dat")) self.connect(M_d, gr.file_sink(gr.sizeof_float, "sync-M.dat")) self.connect(P_d_angle, gr.file_sink(gr.sizeof_float, "sync-angle.dat")) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks.datb")) self.connect(phi, gr.file_sink(gr.sizeof_float, "sync-phi.dat"))
def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, threshold, options, logging=False): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param fft_length: total number of subcarriers @type fft_length: int @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) @type cp_length: int @param occupied_tones: number of subcarriers used for data @type occupied_tones: int @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer @type snr: float @param ks: known symbols used as preambles to each packet @type ks: list of lists @param logging: turn file logging on or off @type logging: bool """ gr.hier_block2.__init__( self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature # gr.io_signature2(2, 2, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char)) # Output signature apurv-- gr.io_signature3( 3, 3, gr.sizeof_gr_complex * occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex * occupied_tones ), ) # apurv++, goes into frame sink for hestimates # gr.io_signature4(4, 4, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_gr_complex*fft_length)) # apurv++, goes into frame sink for hestimates bw = (float(occupied_tones) / float(fft_length)) / 2.0 tb = bw * 0.08 chan_coeffs = gr.firdes.low_pass( 1.0, # gain 1.0, # sampling rate bw + tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING, ) # filter type self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) win = [1 for i in range(fft_length)] zeros_on_left = int(math.ceil((fft_length - occupied_tones) / 2.0)) ks0 = fft_length * [0] ks0[zeros_on_left : zeros_on_left + occupied_tones] = ks[0] ks0 = fft.ifftshift(ks0) ks0time = fft.ifft(ks0) # ADD SCALING FACTOR ks0time = ks0time.tolist() nco_sensitivity = -2.0 / fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pn( fft_length, cp_length, ks0time, threshold, options.threshold_type, options.threshold_gap, logging ) # apurv++ # Set up blocks self.nco = gr.frequency_modulator_fc( nco_sensitivity ) # generate a signal proportional to frequency error of sync block self.sigmix = gr.multiply_cc() self.sampler = digital_swig.ofdm_sampler( fft_length, fft_length + cp_length, len(ks) + 1, 100 ) # 1 for the extra preamble which ofdm_rx doesn't know about (check frame_sink) self.fft_demod = gr.fft_vcc(fft_length, True, win, True) self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition(occupied_tones, fft_length, cp_length, ks) if options.verbose: self._print_verbage(options) # apurv++ modified to allow collected time domain data to artifically pass through the rx chain # # to replay the input manually, use this # # self.connect(self, gr.null_sink(gr.sizeof_gr_complex)) # self.connect(gr.file_source(gr.sizeof_gr_complex, "input.dat"), self.chan_filt) ############# input -> chan_filt ############## self.connect(self, self.chan_filt) use_chan_filt = options.use_chan_filt correct_freq_offset = 0 if use_chan_filt == 1: ##### chan_filt -> SYNC, chan_filt -> SIGMIX #### self.connect(self.chan_filt, self.ofdm_sync) if correct_freq_offset == 1: # enable if frequency offset correction is required # self.connect( self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sigmix, 0) ) # apurv++ follow freq offset else: self.connect(self.chan_filt, (self.sampler, 0)) ###self.connect(self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sampler, 0)) ## extra delay # self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) elif use_chan_filt == 2: #### alternative: chan_filt-> NULL, file_source -> SYNC, file_source -> SIGMIX #### self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex)) if correct_freq_offset == 1: self.connect(gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), self.ofdm_sync) self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sigmix, 0), ) else: self.connect(gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), self.ofdm_sync) self.connect(gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), (self.sampler, 0)) else: # chan_filt->NULL # self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex)) method = options.method if method == -1: ################## for offline analysis, dump sampler input till the frame_sink, using io_signature4 ################# if correct_freq_offset == 1: # enable if frequency offset correction is required # self.connect((self.ofdm_sync, 0), self.nco, (self.sigmix, 1)) # freq offset (0'ed :/) self.connect(self.sigmix, (self.sampler, 0)) # corrected output (0'ed FF) self.connect( (self.ofdm_sync, 1), gr.delay(gr.sizeof_char, fft_length), (self.sampler, 1) ) # timing signal else: # disable frequency offset correction completely # self.connect((self.ofdm_sync, 0), gr.null_sink(gr.sizeof_float)) self.connect((self.ofdm_sync, 1), (self.sampler, 1)) # timing signal, # self.connect((self.ofdm_sync,0), (self.sampler, 2)) ##added ##self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length+cp_length), (self.sampler, 1)) # timing signal, ##extra delay # route received time domain to sink (all-the-way) for offline analysis # self.connect((self.sampler, 0), (self.ofdm_frame_acq, 2)) # self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "sampler_timing.dat")) elif method == 0: # NORMAL functioning # if correct_freq_offset == 1: self.connect( (self.ofdm_sync, 0), self.nco, (self.sigmix, 1) ) # use sync freq. offset output to derotate input signal self.connect(self.sigmix, (self.sampler, 0)) # sample off timing signal detected in sync alg self.connect((self.ofdm_sync, 1), gr.delay(gr.sizeof_char, fft_length), (self.sampler, 1)) # delay? else: self.connect((self.ofdm_sync, 1), (self.sampler, 1)) # self.connect((self.sampler, 2), (self.ofdm_frame_acq, 2)) ####################################################################### use_default = options.use_default if use_default == 0: # (set method == 0) # sampler-> NULL, replay trace->fft_demod, ofdm_frame_acq (time domain) # # self.connect((self.sampler, 0), gr.null_sink(gr.sizeof_gr_complex*fft_length)) # self.connect((self.sampler, 1), gr.null_sink(gr.sizeof_char*fft_length)) self.connect(gr.file_source(gr.sizeof_gr_complex * fft_length, "symbols_src.dat"), self.fft_demod) self.connect(gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"), (self.ofdm_frame_acq, 1)) self.connect(self.fft_demod, (self.ofdm_frame_acq, 0)) elif use_default == 1: # (set method == -1) # normal functioning # self.connect((self.sampler, 0), self.fft_demod) # send derotated sampled signal to FFT self.connect((self.sampler, 1), (self.ofdm_frame_acq, 1)) # send timing signal to signal frame start self.connect(self.fft_demod, (self.ofdm_frame_acq, 0)) elif use_default == 2: # replay directly to ofdm_frame_acq (frequency domain) # self.connect(gr.file_source(gr.sizeof_gr_complex * fft_length, "symbols_src.dat"), (self.ofdm_frame_acq, 0)) self.connect(gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"), (self.ofdm_frame_acq, 1)) ########################### some logging start ############################## # self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length), gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) # self.connect((self.sampler, 0), gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat")) # self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "ofdm_timing_sampler_c.dat")) ############################ some logging end ############################### self.connect((self.ofdm_frame_acq, 0), (self, 0)) # finished with fine/coarse freq correction, self.connect((self.ofdm_frame_acq, 1), (self, 1)) # frame and symbol timing, and equalization self.connect((self.ofdm_frame_acq, 2), (self, 2)) # equalizer: hestimates # self.connect((self.ofdm_frame_acq,3), (self,3)) # ref sampler above # apurv++ ends # # self.connect(self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_receiver-frame_acq_c.dat")) # self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) # apurv++ log the fine frequency offset corrected symbols # # self.connect((self.ofdm_frame_acq, 1), gr.file_sink(gr.sizeof_char, "ofdm_timing_frame_acq_c.dat")) # self.connect((self.ofdm_frame_acq, 2), gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_hestimates_c.dat")) # self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-fft_out_c.dat")) # self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) # self.connect(self, gr.file_sink(gr.sizeof_gr_complex, "ofdm_input_c.dat")) # apurv++ end # if logging: self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex * fft_length, "ofdm_receiver-fft_out_c.dat")) self.connect( self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex * occupied_tones, "ofdm_receiver-frame_acq_c.dat"), ) self.connect((self.ofdm_frame_acq, 1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex * fft_length, "ofdm_receiver-sampler_c.dat")) # self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat")) self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat"))
def __init__(self, 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, options, noutputs=2): """ @param options: parsed raw.ofdm_params """ self.params = ofdm_params(options) params = self.params if noutputs == 2: output_signature = gr.io_signature2( 2, 2, gr.sizeof_gr_complex * params.data_tones, gr.sizeof_char) elif noutputs == 3: output_signature = gr.io_signature3( 3, 3, gr.sizeof_gr_complex * params.data_tones, gr.sizeof_char, gr.sizeof_float) elif noutputs == 4: output_signature = gr.io_signature4( 4, 4, gr.sizeof_gr_complex * params.data_tones, gr.sizeof_char, gr.sizeof_float, gr.sizeof_float) else: # error raise Exception("unsupported number of outputs") gr.hier_block2.__init__(self, "ofdm_demod", gr.io_signature(1, 1, gr.sizeof_gr_complex), output_signature) self.ofdm_recv = ofdm_receiver(params, options.log) # FIXME: magic parameters phgain = 0.4 frgain = phgain * phgain / 4.0 eqgain = 0.05 self.ofdm_demod = raw.ofdm_demapper(params.carriers, phgain, frgain, eqgain) # the studios can't handle the whole ofdm in one thread #ofdm_recv = raw.wrap_sts(self.ofdm_recv) ofdm_recv = self.ofdm_recv self.connect(self, ofdm_recv) self.connect((ofdm_recv, 0), (self.ofdm_demod, 0)) self.connect((ofdm_recv, 1), (self.ofdm_demod, 1)) self.connect(self.ofdm_demod, (self, 0)) self.connect((ofdm_recv, 1), (self, 1)) if noutputs > 2: # average noise power per (pilot) subcarrier self.connect((self.ofdm_demod, 1), (self, 2)) if noutputs > 3: # average signal power per subcarrier self.connect( (ofdm_recv, 0), gr.vector_to_stream(gr.sizeof_float, params.occupied_tones), gr.integrate_ff(params.occupied_tones), gr.multiply_ff(1.0 / params.occupied_tones), (self, 3)) if options.log: self.connect( (self.ofdm_demod, 2), gr.file_sink(gr.sizeof_gr_complex * params.occupied_tones, 'rx-eq.dat')) self.connect((self.ofdm_demod, 1), gr.file_sink(gr.sizeof_float, 'rx-noise.dat')) self.connect((self.ofdm_demod, 0), gr.file_sink(gr.sizeof_gr_complex * params.data_tones, 'rx-demap.dat'))
def __init__(self, fft_length, cp_length, half_sync, kstime, ks1time, threshold, logging=False): gr.hier_block2.__init__( self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3( 3, 3, # Output signature gr.sizeof_gr_complex, # delayed input gr.sizeof_float, # fine frequency offset gr.sizeof_char, # timing indicator ), ) if half_sync: period = fft_length / 2 window = fft_length / 2 else: # full symbol period = fft_length + cp_length window = fft_length # makes the plateau cp_length long # Calculate the frequency offset from the correlation of the preamble x_corr = gr.multiply_cc() self.connect(self, gr.conjugate_cc(), (x_corr, 0)) self.connect(self, gr.delay(gr.sizeof_gr_complex, period), (x_corr, 1)) P_d = gr.moving_average_cc(window, 1.0) self.connect(x_corr, P_d) # offset by -1 phi = gr.sample_and_hold_ff() self.corrmag = gr.complex_to_mag_squared() P_d_angle = gr.complex_to_arg() self.connect(P_d, P_d_angle, (phi, 0)) cross_correlate = 1 if cross_correlate == 1: # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) """ self.f2b = gr.float_to_char() self.slice = gr.threshold_ff(threshold, threshold, 0, fft_length) #self.connect(self, self.crosscorr_filter, self.corrmag, self.slice, self.f2b) self.connect(self.f2b, (phi,1)) self.connect(self.f2b, (self,2)) self.connect(self.f2b, gr.file_sink(gr.sizeof_char, "ofdm_f2b.dat")) """ # new method starts here - only crosscorrelate and use peak_detect block # peak_detect = gr.peak_detector_fb(100, 100, 30, 0.001) self.corrmag1 = gr.complex_to_mag_squared() self.connect(self, self.crosscorr_filter, self.corrmag, peak_detect) self.connect(peak_detect, (phi, 1)) self.connect(peak_detect, (self, 2)) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks_b.dat")) self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_corrmag.dat")) self.connect(self, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self, 0)) else: # Get the power of the input signal to normalize the output of the correlation R_d = gr.moving_average_ff(window, 1.0) self.connect(self, gr.complex_to_mag_squared(), R_d) R_d_squared = gr.multiply_ff() # this is retarded self.connect(R_d, (R_d_squared, 0)) self.connect(R_d, (R_d_squared, 1)) M_d = gr.divide_ff() self.connect(P_d, gr.complex_to_mag_squared(), (M_d, 0)) self.connect(R_d_squared, (M_d, 1)) # Now we need to detect peak of M_d matched_filter = gr.moving_average_ff(cp_length, 1.0 / cp_length) peak_detect = gr.peak_detector_fb(0.25, 0.25, 30, 0.001) self.connect(M_d, matched_filter, gr.add_const_ff(-1), peak_detect) offset = cp_length / 2 # cp_length/2 self.connect(peak_detect, (phi, 1)) self.connect(peak_detect, (self, 2)) self.connect(P_d_angle, gr.delay(gr.sizeof_float, offset), (phi, 0)) self.connect( self, gr.delay(gr.sizeof_gr_complex, (fft_length + offset)), (self, 0) ) # delay the input to follow the freq offset self.connect(peak_detect, gr.delay(gr.sizeof_char, (fft_length + offset)), (self, 2)) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks_b.dat")) self.connect(matched_filter, gr.file_sink(gr.sizeof_float, "sync-mf.dat")) self.connect(phi, (self, 1)) if logging: self.connect(matched_filter, gr.file_sink(gr.sizeof_float, "sync-mf.dat")) self.connect(M_d, gr.file_sink(gr.sizeof_float, "sync-M.dat")) self.connect(P_d_angle, gr.file_sink(gr.sizeof_float, "sync-angle.dat")) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks.datb")) self.connect(phi, gr.file_sink(gr.sizeof_float, "sync-phi.dat"))
def __init__( self, fft_length, cp_length, occupied_tones, snr, ks, threshold, options, logging=False ): # apurv++: added num_symbols, use_chan_filt """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param params: Raw OFDM parameters @type params: ofdm_params @param logging: turn file logging on or off @type logging: bool """ gr.hier_block2.__init__( self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3( 3, 3, gr.sizeof_gr_complex * occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex * occupied_tones ), ) # Output signature # low-pass filter the input channel bw = (float(occupied_tones) / float(fft_length)) / 2.0 tb = bw * 0.08 lpf_coeffs = gr.firdes.low_pass( 1.0, # gain 1.0, # sampling rate bw + tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING, ) # filter type self.chan_filt = gr.fft_filter_ccc(1, lpf_coeffs) self.connect(self, self.chan_filt) self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "rx-filt.dat")) zeros_on_left = int(math.ceil((fft_length - occupied_tones) / 2.0)) ks0 = fft_length * [0] ks0[zeros_on_left : zeros_on_left + occupied_tones] = ks[0] ks0 = fft.ifftshift(ks0) ks0time = fft.ifft(ks0) ks0time = ks0time.tolist() ks1 = fft_length * [0] ks1[zeros_on_left : zeros_on_left + occupied_tones] = ks[1] ks1 = fft.ifftshift(ks1) ks1time = fft.ifft(ks1) ks1time = ks1time.tolist() # sync = ofdm_sync_pn(fft_length, cp_length, True, logging) # raw sync = ofdm_sync_pn(fft_length, cp_length, True, ks0time, ks1time, threshold, logging) # crosscorr version use_chan_filt = 1 if use_chan_filt == 0: self.connect(gr.file_source(gr.sizeof_gr_complex, "chan-filt.dat"), sync) else: self.connect(self.chan_filt, sync) # correct for fine frequency offset computed in sync (up to +-pi/fft_length) nco_sensitivity = 2.0 / fft_length nco = gr.frequency_modulator_fc(nco_sensitivity) sigmix = gr.multiply_cc() # sample at symbol boundaries # NOTE: (sync,2) indicates the first sample of the symbol! sampler = digital_swig.ofdm_sampler(fft_length, fft_length + cp_length, len(ks) + 1, timeout=100) # apurv-- # frequency offset correction # self.connect((sync, 0), (sigmix, 0)) self.connect((sync, 1), nco, (sigmix, 1)) self.connect(sigmix, (sampler, 0)) self.connect((sync, 2), (sampler, 1)) """ self.connect((sync,0), (sampler,0)) self.connect((sync,2), (sampler,1)) # timing signal to sample at self.connect((sync,1), gr.file_sink(gr.sizeof_float, "offset.dat")) """ self.connect((sampler, 1), gr.file_sink(gr.sizeof_char, "sampler_timing.dat")) # self.connect((sampler, 2), gr.file_sink(gr.sizeof_char*fft_length, "sampler_timing_fft.dat")) # fft on the symbols win = [1 for i in range(fft_length)] # see gr_fft_vcc_fftw that it works differently if win = [] fft1 = gr.fft_vcc(fft_length, True, win, True) self.connect((sampler, 0), fft1) # use the preamble to correct the coarse frequency offset and initial equalizer ###frame_acq = raw.ofdm_frame_acquisition(fft_length, cp_length, preambles_raw, carriers) frame_acq = digital_swig.ofdm_frame_acquisition(occupied_tones, fft_length, cp_length, ks) self.frame_acq = frame_acq # normal operation # self.connect((fft1, 0), gr.file_sink(gr.sizeof_gr_complex * options.fft_length, "fft-out.dat")) self.connect((sampler, 1), gr.file_sink(gr.sizeof_char, "sampler_timing.dat")) self.connect(fft1, (frame_acq, 0)) self.connect((sampler, 1), (frame_acq, 1)) """ # enable to have manual input to frame_acq # self.connect(fft1, gr.null_sink(gr.sizeof_gr_complex*options.fft_length)) self.connect(gr.file_source(gr.sizeof_gr_complex*options.fft_length, "combined.dat"), (frame_acq,0)) self.connect(gr.file_source(gr.sizeof_char, "combined_t.dat"), (frame_acq,1)) """ # self.connect(fft, gr.null_sink(gr.sizeof_gr_complex*options.fft_length)) # self.connect((sampler, 1), gr.null_sink(gr.sizeof_char)) # self.connect(gr.file_source(gr.sizeof_gr_complex*options.fft_length, "symbols_src.dat"), (frame_acq, 0)) # self.connect(gr.file_source(gr.sizeof_char, "timing.dat"), (frame_acq, 1)) self.connect((frame_acq, 0), (self, 0)) # finished with fine/coarse freq correction self.connect((frame_acq, 1), (self, 1)) # frame and symbol timing self.connect((frame_acq, 2), (self, 2)) # hestimates self.connect((frame_acq, 0), gr.file_sink(gr.sizeof_gr_complex * occupied_tones, "rx-acq.dat")) self.connect((frame_acq, 1), gr.file_sink(gr.sizeof_char, "timing-acq.dat")) if logging: self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "rx-filt.dat")) self.connect(fft1, gr.file_sink(gr.sizeof_gr_complex * fft_length, "rx-fft.dat")) self.connect((frame_acq, 0), gr.file_sink(gr.sizeof_gr_complex * occupied_tones, "rx-acq.dat")) self.connect((frame_acq, 1), gr.file_sink(1, "rx-detect.datb")) self.connect(sampler, gr.file_sink(gr.sizeof_gr_complex * fft_length, "rx-sampler.dat")) self.connect(sigmix, gr.file_sink(gr.sizeof_gr_complex, "rx-sigmix.dat")) self.connect(nco, gr.file_sink(gr.sizeof_gr_complex, "rx-nco.dat"))
def __init__( self, sample_rate, ber_threshold=0, # Above which to do search ber_smoothing=0, # Alpha of BER smoother (0.01) ber_duration=0, # Length before trying next combo ber_sample_decimation=1, settling_period=0, pre_lock_duration=0, #ber_sample_skip=0 **kwargs): use_throttle = False base_duration = 1024 if sample_rate > 0: use_throttle = True base_duration *= 4 # Has to be high enough for block-delay if ber_threshold == 0: ber_threshold = 512 * 4 if ber_smoothing == 0: ber_smoothing = 0.01 if ber_duration == 0: ber_duration = base_duration * 2 # 1000ms if settling_period == 0: settling_period = base_duration * 1 # 500ms if pre_lock_duration == 0: pre_lock_duration = base_duration * 2 #1000ms print "Creating Auto-FEC:" print "\tsample_rate:\t\t", sample_rate print "\tber_threshold:\t\t", ber_threshold print "\tber_smoothing:\t\t", ber_smoothing print "\tber_duration:\t\t", ber_duration print "\tber_sample_decimation:\t", ber_sample_decimation print "\tsettling_period:\t", settling_period print "\tpre_lock_duration:\t", pre_lock_duration print "" self.sample_rate = sample_rate self.ber_threshold = ber_threshold #self.ber_smoothing = ber_smoothing self.ber_duration = ber_duration self.settling_period = settling_period self.pre_lock_duration = pre_lock_duration #self.ber_sample_skip = ber_sample_skip self.data_lock = threading.Lock() gr.hier_block2.__init__( self, "auto_fec", gr.io_signature( 1, 1, gr.sizeof_gr_complex), # Post MPSK-receiver complex input gr.io_signature3( 3, 3, gr.sizeof_char, gr.sizeof_float, gr.sizeof_float)) # Decoded packed bytes, BER metric, lock self.input_watcher = auto_fec_input_watcher(self) default_xform = self.input_watcher.xform_lock self.gr_conjugate_cc_0 = gr.conjugate_cc() self.connect((self, 0), (self.gr_conjugate_cc_0, 0)) # Input self.blks2_selector_0 = grc_blks2.selector( item_size=gr.sizeof_gr_complex * 1, num_inputs=2, num_outputs=1, input_index=default_xform.get_conjugation_index(), output_index=0, ) self.connect((self.gr_conjugate_cc_0, 0), (self.blks2_selector_0, 0)) self.connect((self, 0), (self.blks2_selector_0, 1)) # Input self.gr_multiply_const_vxx_3 = gr.multiply_const_vcc( (0.707 * (1 + 1j), )) self.connect((self.blks2_selector_0, 0), (self.gr_multiply_const_vxx_3, 0)) self.gr_multiply_const_vxx_2 = gr.multiply_const_vcc( (default_xform.get_rotation(), )) # phase_mult self.connect((self.gr_multiply_const_vxx_3, 0), (self.gr_multiply_const_vxx_2, 0)) self.gr_complex_to_float_0_0 = gr.complex_to_float(1) self.connect((self.gr_multiply_const_vxx_2, 0), (self.gr_complex_to_float_0_0, 0)) self.gr_interleave_1 = gr.interleave(gr.sizeof_float * 1) self.connect((self.gr_complex_to_float_0_0, 1), (self.gr_interleave_1, 1)) self.connect((self.gr_complex_to_float_0_0, 0), (self.gr_interleave_1, 0)) self.gr_multiply_const_vxx_0 = gr.multiply_const_vff((1, )) # invert self.connect((self.gr_interleave_1, 0), (self.gr_multiply_const_vxx_0, 0)) self.baz_delay_2 = baz.delay( gr.sizeof_float * 1, default_xform.get_puncture_delay()) # delay_puncture self.connect((self.gr_multiply_const_vxx_0, 0), (self.baz_delay_2, 0)) self.depuncture_ff_0 = baz.depuncture_ff( (_puncture_matrices[self.input_watcher.puncture_matrix][1] )) # puncture_matrix self.connect((self.baz_delay_2, 0), (self.depuncture_ff_0, 0)) self.baz_delay_1 = baz.delay( gr.sizeof_float * 1, default_xform.get_viterbi_delay()) # delay_viterbi self.connect((self.depuncture_ff_0, 0), (self.baz_delay_1, 0)) self.swap_ff_0 = baz.swap_ff( default_xform.get_viterbi_swap()) # swap_viterbi self.connect((self.baz_delay_1, 0), (self.swap_ff_0, 0)) self.gr_decode_ccsds_27_fb_0 = gr.decode_ccsds_27_fb() if use_throttle: print "==> Using throttle at sample rate:", self.sample_rate self.gr_throttle_0 = gr.throttle(gr.sizeof_float, self.sample_rate) self.connect((self.swap_ff_0, 0), (self.gr_throttle_0, 0)) self.connect((self.gr_throttle_0, 0), (self.gr_decode_ccsds_27_fb_0, 0)) else: self.connect((self.swap_ff_0, 0), (self.gr_decode_ccsds_27_fb_0, 0)) self.connect((self.gr_decode_ccsds_27_fb_0, 0), (self, 0)) # Output bytes self.gr_add_const_vxx_1 = gr.add_const_vff((-4096, )) self.connect((self.gr_decode_ccsds_27_fb_0, 1), (self.gr_add_const_vxx_1, 0)) self.gr_multiply_const_vxx_1 = gr.multiply_const_vff((-1, )) self.connect((self.gr_add_const_vxx_1, 0), (self.gr_multiply_const_vxx_1, 0)) self.connect((self.gr_multiply_const_vxx_1, 0), (self, 1)) # Output BER self.gr_single_pole_iir_filter_xx_0 = gr.single_pole_iir_filter_ff( ber_smoothing, 1) self.connect((self.gr_multiply_const_vxx_1, 0), (self.gr_single_pole_iir_filter_xx_0, 0)) self.gr_keep_one_in_n_0 = blocks.keep_one_in_n(gr.sizeof_float, ber_sample_decimation) self.connect((self.gr_single_pole_iir_filter_xx_0, 0), (self.gr_keep_one_in_n_0, 0)) self.const_source_x_0 = gr.sig_source_f(0, gr.GR_CONST_WAVE, 0, 0, 0) # Last param is const value if use_throttle: lock_throttle_rate = self.sample_rate // 16 print "==> Using lock throttle rate:", lock_throttle_rate self.gr_throttle_1 = gr.throttle(gr.sizeof_float, lock_throttle_rate) self.connect((self.const_source_x_0, 0), (self.gr_throttle_1, 0)) self.connect((self.gr_throttle_1, 0), (self, 2)) else: self.connect((self.const_source_x_0, 0), (self, 2)) self.msg_q = gr.msg_queue( 2 * 256 ) # message queue that holds at most 2 messages, increase to speed up process self.msg_sink = gr.message_sink( gr.sizeof_float, self.msg_q, dont_block=0) # Block to speed up process self.connect((self.gr_keep_one_in_n_0, 0), self.msg_sink) self.input_watcher.start()
def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, threshold, options, logging=False): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param fft_length: total number of subcarriers @type fft_length: int @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) @type cp_length: int @param occupied_tones: number of subcarriers used for data @type occupied_tones: int @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer @type snr: float @param ks: known symbols used as preambles to each packet @type ks: list of lists @param logging: turn file logging on or off @type logging: bool """ gr.hier_block2.__init__( self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature #gr.io_signature2(2, 2, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char)) # Output signature apurv-- gr.io_signature3(3, 3, gr.sizeof_gr_complex * occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex * occupied_tones )) # apurv++, goes into frame sink for hestimates #gr.io_signature4(4, 4, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_gr_complex*fft_length)) # apurv++, goes into frame sink for hestimates bw = (float(occupied_tones) / float(fft_length)) / 2.0 tb = bw * 0.08 chan_coeffs = gr.firdes.low_pass( 1.0, # gain 1.0, # sampling rate bw + tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) win = [1 for i in range(fft_length)] zeros_on_left = int(math.ceil((fft_length - occupied_tones) / 2.0)) ks0 = fft_length * [ 0, ] ks0[zeros_on_left:zeros_on_left + occupied_tones] = ks[0] ks0 = fft.ifftshift(ks0) ks0time = fft.ifft(ks0) # ADD SCALING FACTOR ks0time = ks0time.tolist() nco_sensitivity = -2.0 / fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pn(fft_length, cp_length, ks0time, threshold, options.threshold_type, options.threshold_gap, logging) # apurv++ # Set up blocks self.nco = gr.frequency_modulator_fc( nco_sensitivity ) # generate a signal proportional to frequency error of sync block self.sigmix = gr.multiply_cc() self.sampler = digital_swig.ofdm_sampler( fft_length, fft_length + cp_length, len(ks) + 1, 100 ) # 1 for the extra preamble which ofdm_rx doesn't know about (check frame_sink) self.fft_demod = gr.fft_vcc(fft_length, True, win, True) self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition( occupied_tones, fft_length, cp_length, ks) if options.verbose: self._print_verbage(options) # apurv++ modified to allow collected time domain data to artifically pass through the rx chain # # to replay the input manually, use this # #self.connect(self, gr.null_sink(gr.sizeof_gr_complex)) #self.connect(gr.file_source(gr.sizeof_gr_complex, "input.dat"), self.chan_filt) ############# input -> chan_filt ############## self.connect(self, self.chan_filt) use_chan_filt = options.use_chan_filt correct_freq_offset = 0 if use_chan_filt == 1: ##### chan_filt -> SYNC, chan_filt -> SIGMIX #### self.connect(self.chan_filt, self.ofdm_sync) if correct_freq_offset == 1: # enable if frequency offset correction is required # self.connect(self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sigmix, 0)) # apurv++ follow freq offset else: self.connect(self.chan_filt, (self.sampler, 0)) ###self.connect(self.chan_filt, gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sampler, 0)) ## extra delay #self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) elif use_chan_filt == 2: #### alternative: chan_filt-> NULL, file_source -> SYNC, file_source -> SIGMIX #### self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex)) if correct_freq_offset == 1: self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), self.ofdm_sync) self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), gr.delay(gr.sizeof_gr_complex, (fft_length)), (self.sigmix, 0)) else: self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), self.ofdm_sync) self.connect( gr.file_source(gr.sizeof_gr_complex, "chan_filt.dat"), (self.sampler, 0)) else: # chan_filt->NULL # self.connect(self.chan_filt, gr.null_sink(gr.sizeof_gr_complex)) method = options.method if method == -1: ################## for offline analysis, dump sampler input till the frame_sink, using io_signature4 ################# if correct_freq_offset == 1: # enable if frequency offset correction is required # self.connect((self.ofdm_sync, 0), self.nco, (self.sigmix, 1)) # freq offset (0'ed :/) self.connect(self.sigmix, (self.sampler, 0)) # corrected output (0'ed FF) self.connect((self.ofdm_sync, 1), gr.delay(gr.sizeof_char, fft_length), (self.sampler, 1)) # timing signal else: # disable frequency offset correction completely # self.connect((self.ofdm_sync, 0), gr.null_sink(gr.sizeof_float)) self.connect((self.ofdm_sync, 1), (self.sampler, 1)) # timing signal, #self.connect((self.ofdm_sync,0), (self.sampler, 2)) ##added ##self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length+cp_length), (self.sampler, 1)) # timing signal, ##extra delay # route received time domain to sink (all-the-way) for offline analysis # self.connect((self.sampler, 0), (self.ofdm_frame_acq, 2)) #self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "sampler_timing.dat")) elif method == 0: # NORMAL functioning # if correct_freq_offset == 1: self.connect( (self.ofdm_sync, 0), self.nco, (self.sigmix, 1) ) # use sync freq. offset output to derotate input signal self.connect( self.sigmix, (self.sampler, 0)) # sample off timing signal detected in sync alg self.connect((self.ofdm_sync, 1), gr.delay(gr.sizeof_char, fft_length), (self.sampler, 1)) # delay? else: self.connect((self.ofdm_sync, 1), (self.sampler, 1)) #self.connect((self.sampler, 2), (self.ofdm_frame_acq, 2)) ####################################################################### use_default = options.use_default if use_default == 0: #(set method == 0) # sampler-> NULL, replay trace->fft_demod, ofdm_frame_acq (time domain) # #self.connect((self.sampler, 0), gr.null_sink(gr.sizeof_gr_complex*fft_length)) #self.connect((self.sampler, 1), gr.null_sink(gr.sizeof_char*fft_length)) self.connect( gr.file_source(gr.sizeof_gr_complex * fft_length, "symbols_src.dat"), self.fft_demod) self.connect( gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"), (self.ofdm_frame_acq, 1)) self.connect(self.fft_demod, (self.ofdm_frame_acq, 0)) elif use_default == 1: #(set method == -1) # normal functioning # self.connect( (self.sampler, 0), self.fft_demod) # send derotated sampled signal to FFT self.connect((self.sampler, 1), (self.ofdm_frame_acq, 1)) # send timing signal to signal frame start self.connect(self.fft_demod, (self.ofdm_frame_acq, 0)) elif use_default == 2: # replay directly to ofdm_frame_acq (frequency domain) # self.connect( gr.file_source(gr.sizeof_gr_complex * fft_length, "symbols_src.dat"), (self.ofdm_frame_acq, 0)) self.connect( gr.file_source(gr.sizeof_char * fft_length, "timing_src.dat"), (self.ofdm_frame_acq, 1)) ########################### some logging start ############################## #self.connect((self.ofdm_sync,1), gr.delay(gr.sizeof_char, fft_length), gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) #self.connect((self.sampler, 0), gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat")) #self.connect((self.sampler, 1), gr.file_sink(gr.sizeof_char*fft_length, "ofdm_timing_sampler_c.dat")) ############################ some logging end ############################### self.connect((self.ofdm_frame_acq, 0), (self, 0)) # finished with fine/coarse freq correction, self.connect((self.ofdm_frame_acq, 1), (self, 1)) # frame and symbol timing, and equalization self.connect((self.ofdm_frame_acq, 2), (self, 2)) # equalizer: hestimates #self.connect((self.ofdm_frame_acq,3), (self,3)) # ref sampler above # apurv++ ends # #self.connect(self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_receiver-frame_acq_c.dat")) #self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) # apurv++ log the fine frequency offset corrected symbols # #self.connect((self.ofdm_frame_acq, 1), gr.file_sink(gr.sizeof_char, "ofdm_timing_frame_acq_c.dat")) #self.connect((self.ofdm_frame_acq, 2), gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_hestimates_c.dat")) #self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-fft_out_c.dat")) #self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) #self.connect(self, gr.file_sink(gr.sizeof_gr_complex, "ofdm_input_c.dat")) # apurv++ end # if logging: self.connect( self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) self.connect( self.fft_demod, gr.file_sink(gr.sizeof_gr_complex * fft_length, "ofdm_receiver-fft_out_c.dat")) self.connect( self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex * occupied_tones, "ofdm_receiver-frame_acq_c.dat")) self.connect((self.ofdm_frame_acq, 1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) self.connect( self.sampler, gr.file_sink(gr.sizeof_gr_complex * fft_length, "ofdm_receiver-sampler_c.dat")) #self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat")) self.connect( self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat"))
def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, vcsthresh, vcsbackoff, logging=False, use_coding=0): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param fft_length: total number of subcarriers @type fft_length: int @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) @type cp_length: int @param occupied_tones: number of subcarriers used for data @type occupied_tones: int @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer @type snr: float @param ks: known symbols used as preambles to each packet @type ks: list of lists @param logging: turn file logging on or off @type logging: bool """ gr.hier_block2.__init__(self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3(3, 3, gr.sizeof_gr_complex*occupied_tones, gr.sizeof_char, gr.sizeof_float)) # Output signature bw = (float(occupied_tones) / float(fft_length)) / 2.0 tb = bw*0.08 chan_coeffs = gr.firdes.low_pass (1.0, # gain 1.0, # sampling rate bw+tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) win = [1 for i in range(fft_length)] zeros_on_left = int(math.ceil((fft_length - occupied_tones)/2.0)) ks0 = fft_length*[0,] ks0[zeros_on_left : zeros_on_left + occupied_tones] = ks[0] ks0 = fft.ifftshift(ks0) ks0time = fft.ifft(ks0) # ADD SCALING FACTOR ks0time = ks0time.tolist() SYNC = "pn" if SYNC == "ml": nco_sensitivity = -1.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_ml(fft_length, cp_length, snr, ks0time, logging) elif SYNC == "pn": nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = digital_ll.ofdm_sync_pn(fft_length, cp_length, vcsthresh, logging) elif SYNC == "pnac": nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pnac(fft_length, cp_length, ks0time, logging) # for testing only; do not user over the air # remove filter and filter delay for this elif SYNC == "fixed": self.chan_filt = gr.multiply_const_cc(1.0) nsymbols = 18 # enter the number of symbols per packet freq_offset = 0.0 # if you use a frequency offset, enter it here nco_sensitivity = -2.0/fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_fixed(fft_length, cp_length, nsymbols, freq_offset, logging) # Set up blocks self.nco = gr.frequency_modulator_fc(nco_sensitivity) # generate a signal proportional to frequency error of sync block self.sigmix = gr.multiply_cc() self.sampler = digital_swig.ofdm_sampler(fft_length, fft_length+cp_length) self.fft_demod = gr.fft_vcc(fft_length, True, win, True) if vcsbackoff: preamble_sense_time = vcsbackoff else: if use_coding: preamble_sense_time = fft_length*50 else: preamble_sense_time = fft_length*25 #preamble_sense_taps = [1.0 for i in range(preamble_sense_time)] #self.preamble_sense_avg = gr.fir_filter_fff(1,preamble_sense_taps) self.preamble_sense_avg = digital_ll.downcounter(preamble_sense_time) self.char2float = gr.char_to_float() #self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition(occupied_tones, # fft_length, # cp_length, ks[0]) print 'Using newest ofdm frame acquisition function' self.ofdm_frame_acq = digital_ll.digital_ll_ofdm_frame_acquisition(occupied_tones, fft_length, cp_length, ks[0]) self.connect(self, self.chan_filt) # filter the input channel self.connect(self.chan_filt, self.ofdm_sync) # into the synchronization alg. self.connect((self.ofdm_sync,0), self.nco, (self.sigmix,1)) # use sync freq. offset output to derotate input signal self.connect(self.chan_filt, (self.sigmix,0)) # signal to be derotated self.connect(self.sigmix, (self.sampler,0)) # sample off timing signal detected in sync alg self.connect((self.ofdm_sync,1), (self.sampler,1)) # timing signal to sample at self.connect((self.sampler,0), self.fft_demod) # send derotated sampled signal to FFT self.connect(self.fft_demod, (self.ofdm_frame_acq,0)) # find frame start and equalize signal self.connect((self.sampler,1), (self.ofdm_frame_acq,1)) # send timing signal to signal frame start self.connect((self.ofdm_sync,1), self.char2float) # the timing signal held high by filter for some time self.connect(self.char2float, self.preamble_sense_avg) self.connect(self.preamble_sense_avg, (self,2)) # virtual carrier sense signal self.connect((self.ofdm_frame_acq,0), (self,0)) # finished with fine/coarse freq correction, self.connect((self.ofdm_frame_acq,1), (self,1)) # frame and symbol timing, and equalization if logging: self.connect(self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) self.connect(self.fft_demod, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-fft_out_c.dat")) self.connect(self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex*occupied_tones, "ofdm_receiver-frame_acq_c.dat")) self.connect((self.ofdm_frame_acq,1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) self.connect(self.sampler, gr.file_sink(gr.sizeof_gr_complex*fft_length, "ofdm_receiver-sampler_c.dat")) self.connect(self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat")) self.connect(self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat")) self.connect(self.preamble_sense_avg, gr.file_sink(gr.sizeof_float, "ofdm_receiver-vcs.dat"))
def __init__(self, fft_length, cp_length, half_sync, logging=False): gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3(3, 3, # Output signature gr.sizeof_gr_complex, # delayed input gr.sizeof_float, # fine frequency offset gr.sizeof_char # timing indicator )) if half_sync: period = fft_length/2 window = fft_length/2 else: # full symbol period = fft_length + cp_length window = fft_length # makes the plateau cp_length long # Calculate the frequency offset from the correlation of the preamble x_corr = gr.multiply_cc() self.connect(self, gr.conjugate_cc(), (x_corr, 0)) self.connect(self, gr.delay(gr.sizeof_gr_complex, period), (x_corr, 1)) P_d = gr.moving_average_cc(window, 1.0) self.connect(x_corr, P_d) P_d_angle = gr.complex_to_arg() self.connect(P_d, P_d_angle) # Get the power of the input signal to normalize the output of the correlation R_d = gr.moving_average_ff(window, 1.0) self.connect(self, gr.complex_to_mag_squared(), R_d) R_d_squared = gr.multiply_ff() # this is retarded self.connect(R_d, (R_d_squared, 0)) self.connect(R_d, (R_d_squared, 1)) M_d = gr.divide_ff() self.connect(P_d, gr.complex_to_mag_squared(), (M_d, 0)) self.connect(R_d_squared, (M_d, 1)) # Now we need to detect peak of M_d # NOTE: replaced fir_filter with moving_average for clarity # the peak is up to cp_length long, but noisy, so average it out #matched_filter_taps = [1.0/cp_length for i in range(cp_length)] #matched_filter = gr.fir_filter_fff(1, matched_filter_taps) matched_filter = gr.moving_average_ff(cp_length, 1.0/cp_length) # NOTE: the look_ahead parameter doesn't do anything # these parameters are kind of magic, increase 1 and 2 (==) to be more tolerant #peak_detect = raw.peak_detector_fb(0.55, 0.55, 30, 0.001) peak_detect = raw.peak_detector_fb(0.25, 0.25, 30, 0.001) # NOTE: gr.peak_detector_fb is broken! #peak_detect = gr.peak_detector_fb(0.55, 0.55, 30, 0.001) #peak_detect = gr.peak_detector_fb(0.45, 0.45, 30, 0.001) #peak_detect = gr.peak_detector_fb(0.30, 0.30, 30, 0.001) # offset by -1 self.connect(M_d, matched_filter, gr.add_const_ff(-1), peak_detect) # peak_detect indicates the time M_d is highest, which is the end of the symbol. # We should try to sample in the middle of the plateau!! # FIXME until we figure out how to do this, just offset by cp_length/2 offset = 6 #cp_length/2 # nco(t) = P_d_angle(t-offset) sampled at peak_detect(t) # modulate input(t - fft_length) by nco(t) # signal to sample input(t) at t-offset # # We can't delay by < 0 so instead: # input is delayed by fft_length # P_d_angle is delayed by offset # signal to sample is delayed by fft_length - offset # phi = gr.sample_and_hold_ff() self.connect(peak_detect, (phi,1)) self.connect(P_d_angle, gr.delay(gr.sizeof_float, offset), (phi,0)) #self.connect(P_d_angle, matched_filter2, (phi,0)) # why isn't this better?!? # FIXME: we add fft_length delay so that the preamble is nco corrected too # BUT is this buffering worth it? consider implementing sync as a proper block # delay the input signal to follow the frequency offset signal self.connect(self, gr.delay(gr.sizeof_gr_complex, (fft_length+offset)), (self,0)) self.connect(phi, (self,1)) self.connect(peak_detect, (self,2)) if logging: self.connect(matched_filter, gr.file_sink(gr.sizeof_float, "sync-mf.dat")) self.connect(M_d, gr.file_sink(gr.sizeof_float, "sync-M.dat")) self.connect(P_d_angle, gr.file_sink(gr.sizeof_float, "sync-angle.dat")) self.connect(peak_detect, gr.file_sink(gr.sizeof_char, "sync-peaks.datb")) self.connect(phi, gr.file_sink(gr.sizeof_float, "sync-phi.dat"))
def __init__(self, fft_length, cp_length, occupied_tones, snr, ks, vcsthresh, vcsbackoff, logging=False, use_coding=0): """ Hierarchical block for receiving OFDM symbols. The input is the complex modulated signal at baseband. Synchronized packets are sent back to the demodulator. @param fft_length: total number of subcarriers @type fft_length: int @param cp_length: length of cyclic prefix as specified in subcarriers (<= fft_length) @type cp_length: int @param occupied_tones: number of subcarriers used for data @type occupied_tones: int @param snr: estimated signal to noise ratio used to guide cyclic prefix synchronizer @type snr: float @param ks: known symbols used as preambles to each packet @type ks: list of lists @param logging: turn file logging on or off @type logging: bool """ gr.hier_block2.__init__( self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3(3, 3, gr.sizeof_gr_complex * occupied_tones, gr.sizeof_char, gr.sizeof_float)) # Output signature bw = (float(occupied_tones) / float(fft_length)) / 2.0 tb = bw * 0.08 chan_coeffs = gr.firdes.low_pass( 1.0, # gain 1.0, # sampling rate bw + tb, # midpoint of trans. band tb, # width of trans. band gr.firdes.WIN_HAMMING) # filter type self.chan_filt = gr.fft_filter_ccc(1, chan_coeffs) win = [1 for i in range(fft_length)] zeros_on_left = int(math.ceil((fft_length - occupied_tones) / 2.0)) ks0 = fft_length * [ 0, ] ks0[zeros_on_left:zeros_on_left + occupied_tones] = ks[0] ks0 = fft.ifftshift(ks0) ks0time = fft.ifft(ks0) # ADD SCALING FACTOR ks0time = ks0time.tolist() SYNC = "pn" if SYNC == "ml": nco_sensitivity = -1.0 / fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_ml(fft_length, cp_length, snr, ks0time, logging) elif SYNC == "pn": nco_sensitivity = -2.0 / fft_length # correct for fine frequency self.ofdm_sync = digital_ll.ofdm_sync_pn(fft_length, cp_length, vcsthresh, logging) elif SYNC == "pnac": nco_sensitivity = -2.0 / fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_pnac(fft_length, cp_length, ks0time, logging) # for testing only; do not user over the air # remove filter and filter delay for this elif SYNC == "fixed": self.chan_filt = gr.multiply_const_cc(1.0) nsymbols = 18 # enter the number of symbols per packet freq_offset = 0.0 # if you use a frequency offset, enter it here nco_sensitivity = -2.0 / fft_length # correct for fine frequency self.ofdm_sync = ofdm_sync_fixed(fft_length, cp_length, nsymbols, freq_offset, logging) # Set up blocks self.nco = gr.frequency_modulator_fc( nco_sensitivity ) # generate a signal proportional to frequency error of sync block self.sigmix = gr.multiply_cc() self.sampler = digital_swig.ofdm_sampler(fft_length, fft_length + cp_length) self.fft_demod = gr.fft_vcc(fft_length, True, win, True) if vcsbackoff: preamble_sense_time = vcsbackoff else: if use_coding: preamble_sense_time = fft_length * 50 else: preamble_sense_time = fft_length * 25 #preamble_sense_taps = [1.0 for i in range(preamble_sense_time)] #self.preamble_sense_avg = gr.fir_filter_fff(1,preamble_sense_taps) self.preamble_sense_avg = digital_ll.downcounter(preamble_sense_time) self.char2float = gr.char_to_float() #self.ofdm_frame_acq = digital_swig.ofdm_frame_acquisition(occupied_tones, # fft_length, # cp_length, ks[0]) print 'Using newest ofdm frame acquisition function' self.ofdm_frame_acq = digital_ll.digital_ll_ofdm_frame_acquisition( occupied_tones, fft_length, cp_length, ks[0]) self.connect(self, self.chan_filt) # filter the input channel self.connect(self.chan_filt, self.ofdm_sync) # into the synchronization alg. self.connect( (self.ofdm_sync, 0), self.nco, (self.sigmix, 1)) # use sync freq. offset output to derotate input signal self.connect(self.chan_filt, (self.sigmix, 0)) # signal to be derotated self.connect( self.sigmix, (self.sampler, 0)) # sample off timing signal detected in sync alg self.connect((self.ofdm_sync, 1), (self.sampler, 1)) # timing signal to sample at self.connect((self.sampler, 0), self.fft_demod) # send derotated sampled signal to FFT self.connect( self.fft_demod, (self.ofdm_frame_acq, 0)) # find frame start and equalize signal self.connect((self.sampler, 1), (self.ofdm_frame_acq, 1)) # send timing signal to signal frame start self.connect((self.ofdm_sync, 1), self.char2float ) # the timing signal held high by filter for some time self.connect(self.char2float, self.preamble_sense_avg) self.connect(self.preamble_sense_avg, (self, 2)) # virtual carrier sense signal self.connect((self.ofdm_frame_acq, 0), (self, 0)) # finished with fine/coarse freq correction, self.connect((self.ofdm_frame_acq, 1), (self, 1)) # frame and symbol timing, and equalization if logging: self.connect( self.chan_filt, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-chan_filt_c.dat")) self.connect( self.fft_demod, gr.file_sink(gr.sizeof_gr_complex * fft_length, "ofdm_receiver-fft_out_c.dat")) self.connect( self.ofdm_frame_acq, gr.file_sink(gr.sizeof_gr_complex * occupied_tones, "ofdm_receiver-frame_acq_c.dat")) self.connect((self.ofdm_frame_acq, 1), gr.file_sink(1, "ofdm_receiver-found_corr_b.dat")) self.connect( self.sampler, gr.file_sink(gr.sizeof_gr_complex * fft_length, "ofdm_receiver-sampler_c.dat")) self.connect( self.sigmix, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-sigmix_c.dat")) self.connect( self.nco, gr.file_sink(gr.sizeof_gr_complex, "ofdm_receiver-nco_c.dat")) self.connect( self.preamble_sense_avg, gr.file_sink(gr.sizeof_float, "ofdm_receiver-vcs.dat"))
def __init__(self, N, sample_rate, search_bw=1, threshold=10, threshold_mtm=0.2, tune_freq=0, alpha_avg=1, test_duration=1, period=3600, stats=False, output=False, rate=10, subject_channels=[], valve_callback=None): gr.hier_block2.__init__( self, "coherence_detector", gr.io_signature3(3, 3, gr.sizeof_float * N, gr.sizeof_float * N, gr.sizeof_float * N), gr.io_signature(0, 0, 0)) self.N = N #lenght of the fft for spectral analysis self.sample_rate = sample_rate self.search_bw = search_bw #search bandwidth within each channel self.threshold = threshold #threshold comparison self.threshold_mtm = threshold_mtm self.tune_freq = tune_freq #center frequency self.alpha_avg = alpha_avg #averaging factor for noise level between consecutive measurements self.output = output self.subject_channels = subject_channels self.subject_channels_outcome = [0.1] * len(subject_channels) self.rate = rate self.valve_callback = valve_callback #data queue to share data between theads self.q0 = Queue.Queue() #gnuradio msg queues self.msgq = gr.msg_queue(2) self.msgq1 = gr.msg_queue(2) self.msgq2 = gr.msg_queue(2) #######BLOCKS##### self.sink = blocks.message_sink(gr.sizeof_float * self.N, self.msgq, True) self.sink1 = blocks.message_sink(gr.sizeof_float * self.N, self.msgq1, True) self.sink2 = blocks.message_sink(gr.sizeof_float * self.N, self.msgq2, True) #####CONNECTIONS#### self.connect((self, 0), self.sink) self.connect((self, 1), self.sink1) self.connect((self, 2), self.sink2) self._watcher = watcher( self.msgq, self.msgq1, self.msgq2, self.tune_freq, self.threshold, self.threshold_mtm, self.search_bw, self.N, self.sample_rate, self.q0, self.subject_channels, self.set_subject_channels_outcome, self.rate, self.valve_callback) if self.output != False: self._output_data = output_data(self.q0, self.sample_rate, self.tune_freq, self.N, self.search_bw, self.output, self.subject_channels, self.get_subject_channels_outcome)
def __init__(self, fft_length, cp_length, preambles, sample_rate=10e6, mode='RAW', logging=False): gr.hier_block2.__init__( self, "ofdm_sync_fir", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature3( 3, 3, # Output signature gr.sizeof_gr_complex, # delayed input gr.sizeof_float, # fine frequency offset gr.sizeof_char # timing indicator )) period = 16 window = 48 symbol_length = fft_length + cp_length # Calculate the frequency offset from the correlation of the preamble x_corr = blocks.multiply_cc() P_d = blocks.moving_average_cc(window, 1.0) #P_d = gnuradio.filter.fir_filter_ccf(1, [1.0]*window) P_d_angle = blocks.complex_to_arg() period_delay = blocks.delay(gr.sizeof_gr_complex, period) conj = blocks.conjugate_cc() self.connect(self, period_delay, (x_corr, 0)) self.connect(self, conj, (x_corr, 1)) self.connect(x_corr, P_d, P_d_angle) # Get the power of the input signal to normalize the output of the correlation R_d = blocks.moving_average_ff(window, 1.0) #R_d = gnuradio.filter.fir_filter_fff(1, [1.0]*window) self.connect(self, blocks.complex_to_mag_squared(), R_d) ## using volk divide #M_d = blocks.divide_ff() M_d = raw.divide_ff() self.connect(P_d, blocks.complex_to_mag(), (M_d, 0)) self.connect(R_d, (M_d, 1)) peak_detect = raw.peak_detector2_fb(0.75) # NOTE: Trainning symbol format: STS STS LTS1 LTS2 # because of the moving_average with parameter "window", M_d outputs pulse which indicates the last sample of STS, but need to be adjusted # Cross correlation operation generate peaks only at the last sample of LTS # so, in order to reduce the cross-corr computation, # we delay the peak flag with some delay(less than symbol_length to get some tolerance) # the threshold setted in peak_detector will intruduce some samples if mode == 'PNC': autocorr_flag_delay = symbol_length * 2 - cp_length elif mode == 'RAW': autocorr_flag_delay = symbol_length autoflag_delay = blocks.delay(gr.sizeof_float, autocorr_flag_delay) self.connect(self, (peak_detect, 0)) self.connect(M_d, autoflag_delay, (peak_detect, 1)) if sample_rate > 15e6: period_delay.set_min_output_buffer(96000) conj.set_min_output_buffer(96000) x_corr.set_min_output_buffer(96000) P_d.set_min_output_buffer(96000) P_d_angle.set_min_output_buffer(96000) M_d.set_min_output_buffer(96000) peak_detect.set_min_output_buffer(96000) autoflag_delay.set_min_output_buffer(96000) if False: self.connect( M_d, blocks.file_sink(gr.sizeof_float, 'logs/rx-sync-M_d.dat')) self.connect( autoflag_delay, blocks.file_sink(gr.sizeof_float, 'logs/rx-autoflag_delay.dat')) self.connect( peak_detect, blocks.file_sink(gr.sizeof_char, 'logs/rx-sync-peak.datb')) self.connect((peak_detect, 1), blocks.file_sink(gr.sizeof_float, 'logs/rx-sync-peak.datf')) ########################################################## # peak_delay and offset are for cutting CP # FIXME: until we figure out how to do this, just offset by 6 or cp_length/2 #symbol_length = fft_length+cp_length peak_delay = cp_length offset = 8 #cp_length/2 self.offset = offset # regenerate peak using cross-correlation # * RAW mode: use raw_generate_peak2 block to filter peaks # * PNC mode: use raw_generate_peak3 block to identify the proper peak for two users # PNC mode delays all input peak_delay samples # * cp_length: delay for cross-correlation since auto-correlation is not so accurate # * 3 symbol_length: delay for identifying mode for PNC # * -offset: for cp cut if mode == 'PNC': # The block structure is # signal -> 3*symbol_length+cp_length-offset -> (self,0) [signal] # signal -> cp_length delay -> auto -> (peak,0) -> (self,2) [peak] # signal -> cp_length delay -> (peak,1) -> (self,1) [cfo] # cfo -> cp_length delay -> (peak,2) # # we let cross's signal go faster to identify the beginning # we use cross's peak output to find cfo, so the delay of cfo should = cross delay # we use raw_regenerate_peak to do cross-corr, and symbols energy after STS to identify mode # so the signal is further delayed by three symbols more self.cfo_cross_delay = blocks.delay(gr.sizeof_float, peak_delay) LOOK_AHEAD_NSYM = 0 peak_cross_range = cp_length * 2 #self.connect(autoflag_delay, blocks.file_sink(gr.sizeof_float, 'logs/rx-autoflag_delay.dat')) peak = raw.regenerate_peak3(fft_length, fft_length + cp_length, LOOK_AHEAD_NSYM, peak_cross_range, preambles, False) self.connect(peak_detect, (peak, 0)) self.connect(self, (peak, 1)) self.connect(P_d_angle, self.cfo_cross_delay, (peak, 2)) self.signal_delay = blocks.delay( gr.sizeof_gr_complex, autocorr_flag_delay + cp_length + peak_cross_range - offset) self.connect(self, self.signal_delay, (self, 0)) # signal output self.connect((peak, 1), (self, 1)) # cfo output self.connect((peak, 0), (self, 2)) # peak output #self.connect((peak,1), blocks.null_sink(gr.sizeof_float)) #self.connect((peak,0), blocks.null_sink(gr.sizeof_char)) #self.connect(blocks.null_source(gr.sizeof_float), (self,1)) #self.connect(blocks.null_source(gr.sizeof_char), (self,2)) if logging: self.connect( self.cfo_cross_delay, blocks.file_sink(gr.sizeof_float, 'logs/input-cfo.dat')) #self.connect(cross, blocks.file_sink(gr.sizeof_float, 'logs/cross.dat')) self.connect( self.signal_cross_delay, blocks.file_sink(gr.sizeof_gr_complex, 'logs/cross-signal.dat')) if mode == 'RAW': LOOK_AHEAD_NSYM = 0 peak = raw.regenerate_peak2(fft_length, fft_length+cp_length, \ LOOK_AHEAD_NSYM, preambles, False) # do LTS cross correlation will set a flag at the last sample of LTS # signal delay is the same as the path(cross signal), but delay to pos(in 1st cp of LTS) # so as the sample cut the data from LTS to end; self.signal_delay = blocks.delay( gr.sizeof_gr_complex, symbol_length - offset) #self.cross_delay + symbol_length - offset if sample_rate > 15e6: self.signal_delay.set_min_output_buffer(96000) #peak.set_max_noutput_items(2048) #peak.set_max_noutput_items(96000) self.cfo_delay = autocorr_flag_delay + peak_delay self.connect(peak_detect, (peak, 0)) self.connect(P_d_angle, blocks.delay(gr.sizeof_float, self.cfo_delay), (peak, 1)) self.connect(self, (peak, 2)) self.connect(self, self.signal_delay, (self, 0)) # signal output self.connect((peak, 1), (self, 1)) # cfo output self.connect((peak, 0), (self, 2)) # raw peak output self.peak = peak
def __init__(self, options, noutputs = 2): """ @param options: parsed raw.ofdm_params """ self.params = ofdm_params(options) params = self.params if noutputs == 2: output_signature = gr.io_signature2(2, 2, gr.sizeof_gr_complex*params.data_tones, gr.sizeof_char ) elif noutputs == 3: output_signature = gr.io_signature3(3, 3, gr.sizeof_gr_complex*params.data_tones, gr.sizeof_char, gr.sizeof_float ) elif noutputs == 4: output_signature = gr.io_signature4(4, 4, gr.sizeof_gr_complex*params.data_tones, gr.sizeof_char, gr.sizeof_float, gr.sizeof_float ) else: # error raise Exception("unsupported number of outputs") gr.hier_block2.__init__(self, "ofdm_demod", gr.io_signature(1, 1, gr.sizeof_gr_complex), output_signature ) self.ofdm_recv = ofdm_receiver(params, options.log) # FIXME: magic parameters phgain = 0.4 frgain = phgain*phgain / 4.0 eqgain = 0.05 self.ofdm_demod = raw.ofdm_demapper(params.carriers, phgain, frgain, eqgain) # the studios can't handle the whole ofdm in one thread #ofdm_recv = raw.wrap_sts(self.ofdm_recv) ofdm_recv = self.ofdm_recv self.connect(self, ofdm_recv) self.connect((ofdm_recv,0), (self.ofdm_demod,0)) self.connect((ofdm_recv,1), (self.ofdm_demod,1)) self.connect(self.ofdm_demod, (self,0)) self.connect((ofdm_recv,1), (self,1)) if noutputs > 2: # average noise power per (pilot) subcarrier self.connect((self.ofdm_demod,1), (self,2)) if noutputs > 3: # average signal power per subcarrier self.connect((ofdm_recv,0), gr.vector_to_stream(gr.sizeof_float, params.occupied_tones), gr.integrate_ff(params.occupied_tones), gr.multiply_ff(1.0/params.occupied_tones), (self,3)) if options.log: self.connect((self.ofdm_demod, 2), gr.file_sink(gr.sizeof_gr_complex*params.occupied_tones, 'rx-eq.dat')) self.connect((self.ofdm_demod, 1), gr.file_sink(gr.sizeof_float, 'rx-noise.dat')) self.connect((self.ofdm_demod, 0), gr.file_sink(gr.sizeof_gr_complex*params.data_tones, 'rx-demap.dat'))