def test_complex_to_arg (self): pi = math.pi input_data = (0, pi/6, pi/4, pi/2, 3*pi/4, 7*pi/8, -pi/6, -pi/4, -pi/2, -3*pi/4, -7*pi/8) expected_result = (0.0, # 0 0.52382522821426392, # pi/6 0.78539806604385376, # pi/4 1.5707963705062866, # pi/2 2.3561947345733643, # 3pi/4 2.7491819858551025, # 7pi/8 -0.52382522821426392, # -pi/6 -0.78539806604385376, # -pi/4 -1.5707963705062866, # -pi/2 -2.3561947345733643, # -3pi/4 -2.7491819858551025) # -7pi/8 src_data = tuple ([math.cos (x) + math.sin (x) * 1j for x in input_data]) src = gr.vector_source_c (src_data) op = gr.complex_to_arg () dst = gr.vector_sink_f () self.tb.connect (src, op) self.tb.connect (op, dst) self.tb.run () actual_result = dst.data () self.assertFloatTuplesAlmostEqual (expected_result, actual_result, 5)
def __init__(self, sps, channel_decim, channel_taps, options, usrp_rate, channel_rate, lo_freq): gr.hier_block2.__init__(self, "rx_channel_cqpsk", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_float)) chan = gr.freq_xlating_fir_filter_ccf(int(channel_decim), channel_taps, lo_freq, usrp_rate) agc = gr.feedforward_agc_cc(16, 1.0) gain_omega = 0.125 * options.gain_mu * options.gain_mu alpha = options.costas_alpha beta = 0.125 * alpha * alpha fmin = -0.025 fmax = 0.025 clock = repeater.gardner_costas_cc(sps, options.gain_mu, gain_omega, alpha, beta, fmax, fmin) # Perform Differential decoding on the constellation diffdec = gr.diff_phasor_cc() # take angle of the difference (in radians) to_float = gr.complex_to_arg() # convert from radians such that signal is in -3/-1/+1/+3 rescale = gr.multiply_const_ff( (1 / (pi / 4)) ) self.connect (self, chan, agc, clock, diffdec, to_float, rescale, self)
def __init__(self, sps, channel_decim, channel_taps, options, usrp_rate, channel_rate, lo_freq): gr.hier_block2.__init__(self, "rx_channel_cqpsk", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_float)) chan = gr.freq_xlating_fir_filter_ccf(int(channel_decim), channel_taps, lo_freq, usrp_rate) agc = gr.feedforward_agc_cc(16, 1.0) gain_omega = 0.125 * options.gain_mu * options.gain_mu alpha = options.costas_alpha beta = 0.125 * alpha * alpha fmin = -0.025 fmax = 0.025 clock = repeater.gardner_costas_cc(sps, options.gain_mu, gain_omega, alpha, beta, fmax, fmin) # Perform Differential decoding on the constellation diffdec = gr.diff_phasor_cc() # take angle of the difference (in radians) to_float = gr.complex_to_arg() # convert from radians such that signal is in -3/-1/+1/+3 rescale = gr.multiply_const_ff((1 / (pi / 4))) self.connect(self, chan, agc, clock, diffdec, to_float, rescale, self)
def test_complex_to_arg(self): pi = math.pi input_data = (0, pi / 6, pi / 4, pi / 2, 3 * pi / 4, 7 * pi / 8, -pi / 6, -pi / 4, -pi / 2, -3 * pi / 4, -7 * pi / 8) expected_result = ( 0.0, # 0 0.52382522821426392, # pi/6 0.78539806604385376, # pi/4 1.5707963705062866, # pi/2 2.3561947345733643, # 3pi/4 2.7491819858551025, # 7pi/8 -0.52382522821426392, # -pi/6 -0.78539806604385376, # -pi/4 -1.5707963705062866, # -pi/2 -2.3561947345733643, # -3pi/4 -2.7491819858551025) # -7pi/8 src_data = tuple([math.cos(x) + math.sin(x) * 1j for x in input_data]) src = gr.vector_source_c(src_data) op = gr.complex_to_arg() dst = gr.vector_sink_f() self.tb.connect(src, op) self.tb.connect(op, dst) self.tb.run() actual_result = dst.data() self.assertFloatTuplesAlmostEqual(expected_result, actual_result, 3)
def __init__(self): gr.top_block.__init__(self) ################################################## # Variables ################################################## self.signal_freq = signal_freq = 5000 self.samp_rate = samp_rate = 48000 self.bw = bw = 200 ################################################## # Blocks ################################################## self.gr_probe_ref = gr.probe_signal_f() self.gr_probe_mag = gr.probe_signal_f() self.gr_probe_arg = gr.probe_signal_f() self.gr_nlog10_ff_ref = gr.nlog10_ff(1, 1, 0) self.gr_nlog10_ff_0 = gr.nlog10_ff(1, 1, 0) self.gr_divide_xx_0 = gr.divide_cc(1) self.gr_complex_to_mag_ref = gr.complex_to_mag(1) self.gr_complex_to_mag_0 = gr.complex_to_mag(1) self.gr_complex_to_arg_0 = gr.complex_to_arg(1) self.band_pass_filter_0_0 = gr.fir_filter_fcc( 1, firdes.complex_band_pass( 1, samp_rate, signal_freq - bw / 2, signal_freq + bw / 2, 100, firdes.WIN_BLACKMAN, 6.76 ), ) self.band_pass_filter_0 = gr.fir_filter_fcc( 1, firdes.complex_band_pass( 1, samp_rate, signal_freq - bw / 2, signal_freq + bw / 2, 100, firdes.WIN_BLACKMAN, 6.76 ), ) self.audio_source_0 = audio.source(samp_rate, "", True) ################################################## # Connections ################################################## self.connect((self.band_pass_filter_0_0, 0), (self.gr_complex_to_mag_0, 0)) self.connect((self.gr_complex_to_mag_0, 0), (self.gr_nlog10_ff_0, 0)) self.connect((self.gr_divide_xx_0, 0), (self.gr_complex_to_arg_0, 0)) self.connect((self.band_pass_filter_0_0, 0), (self.gr_divide_xx_0, 0)) self.connect((self.band_pass_filter_0, 0), (self.gr_divide_xx_0, 1)) self.connect((self.audio_source_0, 1), (self.band_pass_filter_0_0, 0)) self.connect((self.audio_source_0, 0), (self.band_pass_filter_0, 0)) self.connect((self.gr_nlog10_ff_0, 0), (self.gr_probe_mag, 0)) self.connect((self.gr_complex_to_arg_0, 0), (self.gr_probe_arg, 0)) self.connect((self.band_pass_filter_0, 0), (self.gr_complex_to_mag_ref, 0)) self.connect((self.gr_complex_to_mag_ref, 0), (self.gr_nlog10_ff_ref, 0)) self.connect((self.gr_nlog10_ff_ref, 0), (self.gr_probe_ref, 0))
def do_check_phase_shift(self, type, name): sps = 2 L = 1 in_bits = (1,) * 20 src = gr.vector_source_b(in_bits, False) cpm = digital_swig.cpmmod_bc(type, 0.5, sps, L) arg = gr.complex_to_arg() sink = gr.vector_sink_f() self.tb.connect(src, cpm, arg, sink) self.tb.run() symbol_phases = numpy.array(sink.data()[sps*L-1::sps]) phase_diff = numpy.mod(numpy.subtract(symbol_phases[1:], symbol_phases[:-1]), (2*numpy.pi,) * (len(symbol_phases)-1)) self.assertFloatTuplesAlmostEqual(tuple(phase_diff), (0.5 * numpy.pi,) * len(phase_diff), 5, msg="Phase shift was not correct for CPM method " + name)
def test_001_lgmsk(self): sps = 2 L = 5 bt = 0.3 in_bits = (1,) * 20 src = gr.vector_source_b(in_bits, False) gmsk = digital_swig.gmskmod_bc(sps, bt, L) arg = gr.complex_to_arg() sink = gr.vector_sink_f() self.tb.connect(src, gmsk, arg, sink) self.tb.run() symbol_phases = numpy.array(sink.data()[sps*L-1::sps]) phase_diff = numpy.mod(numpy.subtract(symbol_phases[1:], symbol_phases[:-1]), (2*numpy.pi,) * (len(symbol_phases)-1)) self.assertFloatTuplesAlmostEqual(tuple(phase_diff), (0.5 * numpy.pi,) * len(phase_diff), 5, msg="Phase shift was not correct for GMSK")
def do_check_phase_shift(self, type, name): sps = 2 L = 1 in_bits = (1, ) * 20 src = gr.vector_source_b(in_bits, False) cpm = digital_swig.cpmmod_bc(type, 0.5, sps, L) arg = gr.complex_to_arg() sink = gr.vector_sink_f() self.tb.connect(src, cpm, arg, sink) self.tb.run() symbol_phases = numpy.array(sink.data()[sps * L - 1::sps]) phase_diff = numpy.mod( numpy.subtract(symbol_phases[1:], symbol_phases[:-1]), (2 * numpy.pi, ) * (len(symbol_phases) - 1)) self.assertFloatTuplesAlmostEqual( tuple(phase_diff), (0.5 * numpy.pi, ) * len(phase_diff), 5, msg="Phase shift was not correct for CPM method " + name)
def test_001_lgmsk(self): sps = 2 L = 5 bt = 0.3 in_bits = (1, ) * 20 src = gr.vector_source_b(in_bits, False) gmsk = digital_swig.gmskmod_bc(sps, bt, L) arg = gr.complex_to_arg() sink = gr.vector_sink_f() self.tb.connect(src, gmsk, arg, sink) self.tb.run() symbol_phases = numpy.array(sink.data()[sps * L - 1::sps]) phase_diff = numpy.mod( numpy.subtract(symbol_phases[1:], symbol_phases[:-1]), (2 * numpy.pi, ) * (len(symbol_phases) - 1)) self.assertFloatTuplesAlmostEqual( tuple(phase_diff), (0.5 * numpy.pi, ) * len(phase_diff), 5, msg="Phase shift was not correct for GMSK")
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, kstime, logging=False): """ OFDM synchronization using PN Correlation and initial cross-correlation: F. Tufvesson, O. Edfors, and M. Faulkner, "Time and Frequency Synchronization for OFDM using PN-Sequency Preambles," IEEE Proc. VTC, 1999, pp. 2203-2207. This implementation is meant to be a more robust version of the Schmidl and Cox receiver design. By correlating against the preamble and using that as the input to the time-delayed correlation, this circuit produces a very clean timing signal at the end of the preamble. The timing is more accurate and does not have the problem associated with determining the timing from the plateau structure in the Schmidl and Cox. This implementation appears to require that the signal is received with a normalized power or signal scalling factor to reduce ambiguities intorduced from partial correlation of the cyclic prefix and the peak detection. A better peak detection block might fix this. Also, the cross-correlation falls apart as the frequency offset gets larger and completely fails when an integer offset is introduced. Another thing to look at. """ gr.hier_block2.__init__(self, "ofdm_sync_pnac", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) symbol_length = fft_length + cp_length # PN Sync with cross-correlation input # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime[0:fft_length//2]] kstime.reverse() self.crosscorr_filter = filter.fir_filter_ccc(1, kstime) # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); # Create a moving sum filter for the input self.mag = gr.complex_to_mag_squared() movingsum_taps = (fft_length//1)*[1.0,] self.power = filter.fir_filter_fff(1,movingsum_taps) # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.compare = gr.sub_ff() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.threshold = gr.threshold_ff(0,0,0) # threshold detection might need to be tweaked self.peaks = gr.float_to_char() self.connect(self, self.input) # Cross-correlate input signal with known preamble self.connect(self.input, self.crosscorr_filter) # use the output of the cross-correlation as input time-shifted correlation self.connect(self.crosscorr_filter, self.delay) self.connect(self.crosscorr_filter, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.c2mag) self.connect(self.corr, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # Get the power of the input signal to compare against the correlation self.connect(self.crosscorr_filter, self.mag, self.power) # Compare the power to the correlator output to determine timing peak # When the peak occurs, it peaks above zero, so the thresholder detects this self.connect(self.c2mag, (self.compare,0)) self.connect(self.power, (self.compare,1)) self.connect(self.compare, self.threshold) self.connect(self.threshold, self.peaks, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.peaks, (self,1)) if logging: self.connect(self.compare, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-compare_f.dat")) self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-theta_f.dat")) self.connect(self.power, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-inputpower_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-epsilon_f.dat")) self.connect(self.threshold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-threshold_f.dat")) self.connect(self.peaks, gr.file_sink(gr.sizeof_char, "ofdm_sync_pnac-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pnac-input_c.dat"))
def __init__(self, fft_length, cp_length, snr, kstime, logging): ''' Maximum Likelihood OFDM synchronizer: J. van de Beek, M. Sandell, and P. O. Borjesson, "ML Estimation of Time and Frequency Offset in OFDM Systems," IEEE Trans. Signal Processing, vol. 45, no. 7, pp. 1800-1805, 1997. ''' gr.hier_block2.__init__(self, "ofdm_sync_ml", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) SNR = 10.0**(snr/10.0) rho = SNR / (SNR + 1.0) symbol_length = fft_length + cp_length # ML Sync # Energy Detection from ML Sync self.connect(self, self.input) # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length) self.connect(self.input, self.delay) # magnitude squared blocks self.magsqrd1 = gr.complex_to_mag_squared() self.magsqrd2 = gr.complex_to_mag_squared() self.adder = gr.add_ff() moving_sum_taps = [rho/2 for i in range(cp_length)] self.moving_sum_filter = gr.fir_filter_fff(1,moving_sum_taps) self.connect(self.input,self.magsqrd1) self.connect(self.delay,self.magsqrd2) self.connect(self.magsqrd1,(self.adder,0)) self.connect(self.magsqrd2,(self.adder,1)) self.connect(self.adder,self.moving_sum_filter) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.mixer = gr.multiply_cc(); movingsum2_taps = [1.0 for i in range(cp_length)] self.movingsum2 = gr.fir_filter_ccf(1,movingsum2_taps) # Correlator data handler self.c2mag = gr.complex_to_mag() self.angle = gr.complex_to_arg() self.connect(self.input,(self.mixer,1)) self.connect(self.delay,self.conjg,(self.mixer,0)) self.connect(self.mixer,self.movingsum2,self.c2mag) self.connect(self.movingsum2,self.angle) # ML Sync output arg, need to find maximum point of this self.diff = gr.sub_ff() self.connect(self.c2mag,(self.diff,0)) self.connect(self.moving_sum_filter,(self.diff,1)) #ML measurements input to sampler block and detect self.f2c = gr.float_to_complex() self.pk_detect = gr.peak_detector_fb(0.2, 0.25, 30, 0.0005) self.sample_and_hold = gr.sample_and_hold_ff() # use the sync loop values to set the sampler and the NCO # self.diff = theta # self.angle = epsilon self.connect(self.diff, self.pk_detect) # The DPLL corrects for timing differences between CP correlations use_dpll = 0 if use_dpll: self.dpll = gr.dpll_bb(float(symbol_length),0.01) self.connect(self.pk_detect, self.dpll) self.connect(self.dpll, (self.sample_and_hold,1)) else: self.connect(self.pk_detect, (self.sample_and_hold,1)) self.connect(self.angle, (self.sample_and_hold,0)) ################################ # correlate against known symbol # This gives us the same timing signal as the PN sync block only on the preamble # we don't use the signal generated from the CP correlation because we don't want # to readjust the timing in the middle of the packet or we ruin the equalizer settings. kstime = [k.conjugate() for k in kstime] kstime.reverse() self.kscorr = gr.fir_filter_ccc(1, kstime) self.corrmag = gr.complex_to_mag_squared() self.div = gr.divide_ff() # The output signature of the correlation has a few spikes because the rest of the # system uses the repeated preamble symbol. It needs to work that generically if # anyone wants to use this against a WiMAX-like signal since it, too, repeats. # The output theta of the correlator above is multiplied with this correlation to # identify the proper peak and remove other products in this cross-correlation self.threshold_factor = 0.1 self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0) self.f2b = gr.float_to_char() self.b2f = gr.char_to_float() self.mul = gr.multiply_ff() # Normalize the power of the corr output by the energy. This is not really needed # and could be removed for performance, but it makes for a cleaner signal. # if this is removed, the threshold value needs adjustment. self.connect(self.input, self.kscorr, self.corrmag, (self.div,0)) self.connect(self.moving_sum_filter, (self.div,1)) self.connect(self.div, (self.mul,0)) self.connect(self.pk_detect, self.b2f, (self.mul,1)) self.connect(self.mul, self.slice) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.slice, self.f2b, (self,1)) if logging: self.connect(self.moving_sum_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-energy_f.dat")) self.connect(self.diff, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-epsilon_f.dat")) self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-corrmag_f.dat")) self.connect(self.kscorr, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-kscorr_c.dat")) self.connect(self.div, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-div_f.dat")) self.connect(self.mul, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-mul_f.dat")) self.connect(self.slice, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-slice_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-peaks_b.dat")) if use_dpll: self.connect(self.dpll, gr.file_sink(gr.sizeof_char, "ofdm_sync_ml-dpll_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_ml-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_ml-input_c.dat"))
def __init__(self, mode, debug=False): """ OFDM time and coarse frequency synchronisation for DAB @param mode DAB mode (1-4) @param debug if True: write data streams out to files """ if mode<1 or mode>4: raise ValueError, "Invalid DAB mode: "+str(mode)+" (modes 1-4 exist)" # get the correct DAB parameters dp = parameters.dab_parameters(mode) rp = parameters.receiver_parameters(mode) gr.hier_block2.__init__(self,"ofdm_sync_dab", gr.io_signature(1, 1, gr.sizeof_gr_complex), # input signature gr.io_signature2(2, 2, gr.sizeof_gr_complex, gr.sizeof_char)) # output signature # workaround for a problem that prevents connecting more than one block directly (see trac ticket #161) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) # # null-symbol detection # # (outsourced to detect_zero.py) self.ns_detect = detect_null.detect_null(dp.ns_length, debug) self.connect(self.input, self.ns_detect) # # fine frequency synchronisation # # the code for fine frequency synchronisation is adapted from # ofdm_sync_ml.py; it abuses the cyclic prefix to find the fine # frequency error, as suggested in "ML Estimation of Timing and # Frequency Offset in OFDM Systems", by Jan-Jaap van de Beek, # Magnus Sandell, Per Ola Börjesson, see # http://www.sm.luth.se/csee/sp/research/report/bsb96r.html self.ffs_delay = gr.delay(gr.sizeof_gr_complex, dp.fft_length) self.ffs_conj = gr.conjugate_cc() self.ffs_mult = gr.multiply_cc() # self.ffs_moving_sum = gr.fir_filter_ccf(1, [1]*dp.cp_length) self.ffs_moving_sum = dab_swig.moving_sum_cc(dp.cp_length) self.ffs_angle = gr.complex_to_arg() self.ffs_angle_scale = gr.multiply_const_ff(1./dp.fft_length) self.ffs_delay_sample_and_hold = gr.delay(gr.sizeof_char, dp.symbol_length) # sample the value at the end of the symbol .. self.ffs_sample_and_hold = gr.sample_and_hold_ff() self.ffs_delay_input_for_correction = gr.delay(gr.sizeof_gr_complex, dp.symbol_length) # by delaying the input, we can use the ff offset estimation from the first symbol to correct the first symbol itself self.ffs_nco = gr.frequency_modulator_fc(1) # ffs_sample_and_hold directly outputs phase error per sample self.ffs_mixer = gr.multiply_cc() # calculate fine frequency error self.connect(self.input, self.ffs_conj, self.ffs_mult) self.connect(self.input, self.ffs_delay, (self.ffs_mult, 1)) self.connect(self.ffs_mult, self.ffs_moving_sum, self.ffs_angle) # only use the value from the first half of the first symbol self.connect(self.ffs_angle, self.ffs_angle_scale, (self.ffs_sample_and_hold, 0)) self.connect(self.ns_detect, self.ffs_delay_sample_and_hold, (self.ffs_sample_and_hold, 1)) # do the correction self.connect(self.ffs_sample_and_hold, self.ffs_nco, (self.ffs_mixer, 0)) self.connect(self.input, self.ffs_delay_input_for_correction, (self.ffs_mixer, 1)) # output - corrected signal and start of DAB frames self.connect(self.ffs_mixer, (self, 0)) self.connect(self.ffs_delay_sample_and_hold, (self, 1)) if debug: self.connect(self.ffs_angle, gr.file_sink(gr.sizeof_float, "debug/ofdm_sync_dab_ffs_angle.dat")) self.connect(self.ffs_sample_and_hold, gr.multiply_const_ff(1./(dp.T*2*pi)), gr.file_sink(gr.sizeof_float, "debug/ofdm_sync_dab_fine_freq_err_f.dat")) self.connect(self.ffs_mixer, gr.file_sink(gr.sizeof_gr_complex, "debug/ofdm_sync_dab_fine_freq_corrected_c.dat"))
def __init__(self, fft_length, cp_length, kstime, threshold, threshold_type, threshold_gap, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__( self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length / 2) # Correlation from ML Sync self.conjg = gr.conjugate_cc() self.corr = gr.multiply_cc() # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length // 2)] self.moving_sum_filter = gr.fir_filter_ccf(1, moving_sum_taps) else: moving_sum_taps = [ complex(1.0, 0.0) for i in range(fft_length // 2) ] self.moving_sum_filter = gr.fft_filter_ccc(1, moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() movingsum2_taps = [1.0 for i in range(fft_length // 2)] #movingsum2_taps = [0.5 for i in range(fft_length*4)] #apurv - implementing Veljo's suggestion, when pause b/w packets if 1: self.inputmovingsum = gr.fir_filter_fff(1, movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1, movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.sub1 = gr.add_const_ff(-1) self.pk_detect = gr.peak_detector_fb( 0.20, 0.20, 30, 0.001 ) #apurv - implementing Veljo's suggestion, when pause b/w packets self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr, 0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr, 1)) self.connect(self.corr, self.moving_sum_filter) #self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold, 0)) # apurv-- #self.connect(self.angle, gr.delay(gr.sizeof_float, offset), (self.sample_and_hold, 0)) #apurv++ cross_correlate = 1 if cross_correlate == 1: # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) # get the magnitude # self.corrmag = gr.complex_to_mag_squared() self.f2b = gr.float_to_char() self.threshold_factor = threshold #0.0012 #0.012 #0.0015 if 0: self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0, fft_length) else: #thresholds = [self.threshold_factor, 9e-5] self.slice = gr.threshold_ff(threshold, threshold, 0, fft_length, threshold_type, threshold_gap) self.connect(self.input, self.crosscorr_filter, self.corrmag, self.slice, self.f2b) # some debug dump # self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_corrmag.dat")) #self.connect(self.f2b, gr.file_sink(gr.sizeof_char, "ofdm_f2b.dat")) self.connect(self.f2b, (self.sample_and_hold, 1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self, 0)) #self.connect(self.pk_detect, (self,1)) #removed #self.connect(self.f2b, gr.delay(gr.sizeof_char, 1), (self, 1)) self.connect(self.f2b, (self, 1)) if logging: self.connect( self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect( self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect( self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect( self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect( self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect( self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
def __init__(self, dab_params, rx_params, verbose=False, debug=False): """ Hierarchical block for OFDM demodulation @param dab_params DAB parameter object (dab.parameters.dab_parameters) @param rx_params RX parameter object (dab.parameters.receiver_parameters) @param debug enables debug output to files @param verbose whether to produce verbose messages """ self.dp = dp = dab_params self.rp = rp = rx_params self.verbose = verbose if self.rp.softbits: gr.hier_block2.__init__(self,"ofdm_demod", gr.io_signature (1, 1, gr.sizeof_gr_complex), # input signature gr.io_signature2(2, 2, gr.sizeof_float*self.dp.num_carriers*2, gr.sizeof_char)) # output signature else: gr.hier_block2.__init__(self,"ofdm_demod", gr.io_signature (1, 1, gr.sizeof_gr_complex), # input signature gr.io_signature2(2, 2, gr.sizeof_char*self.dp.num_carriers/4, gr.sizeof_char)) # output signature # workaround for a problem that prevents connecting more than one block directly (see trac ticket #161) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) # input filtering if self.rp.input_fft_filter: if verbose: print "--> RX filter enabled" lowpass_taps = gr.firdes_low_pass(1.0, # gain dp.sample_rate, # sampling rate rp.filt_bw, # cutoff frequency rp.filt_tb, # width of transition band gr.firdes.WIN_HAMMING) # Hamming window self.fft_filter = gr.fft_filter_ccc(1, lowpass_taps) # correct sample rate offset, if enabled if self.rp.autocorrect_sample_rate: if verbose: print "--> dynamic sample rate correction enabled" self.rate_detect_ns = detect_null.detect_null(dp.ns_length, False) self.rate_estimator = dab_swig.estimate_sample_rate_bf(dp.sample_rate, dp.frame_length) self.rate_prober = gr.probe_signal_f() self.connect(self.input, self.rate_detect_ns, self.rate_estimator, self.rate_prober) # self.resample = gr.fractional_interpolator_cc(0, 1) self.resample = dab_swig.fractional_interpolator_triggered_update_cc(0,1) self.connect(self.rate_detect_ns, (self.resample,1)) self.updater = Timer(0.1,self.update_correction) # self.updater = threading.Thread(target=self.update_correction) self.run_interpolater_update_thread = True self.updater.setDaemon(True) self.updater.start() else: self.run_interpolater_update_thread = False if self.rp.sample_rate_correction_factor != 1: if verbose: print "--> static sample rate correction enabled" self.resample = gr.fractional_interpolator_cc(0, self.rp.sample_rate_correction_factor) # timing and fine frequency synchronisation self.sync = ofdm_sync_dab2.ofdm_sync_dab(self.dp, self.rp, debug) # ofdm symbol sampler self.sampler = dab_swig.ofdm_sampler(dp.fft_length, dp.cp_length, dp.symbols_per_frame, rp.cp_gap) # fft for symbol vectors self.fft = gr.fft_vcc(dp.fft_length, True, [], True) # coarse frequency synchronisation self.cfs = dab_swig.ofdm_coarse_frequency_correct(dp.fft_length, dp.num_carriers, dp.cp_length) # diff phasor self.phase_diff = dab_swig.diff_phasor_vcc(dp.num_carriers) # remove pilot symbol self.remove_pilot = dab_swig.ofdm_remove_first_symbol_vcc(dp.num_carriers) # magnitude equalisation if self.rp.equalize_magnitude: if verbose: print "--> magnitude equalization enabled" self.equalizer = dab_swig.magnitude_equalizer_vcc(dp.num_carriers, rp.symbols_for_magnitude_equalization) # frequency deinterleaving self.deinterleave = dab_swig.frequency_interleaver_vcc(dp.frequency_deinterleaving_sequence_array) # symbol demapping self.demapper = dab_swig.qpsk_demapper_vcb(dp.num_carriers) # # connect everything # if self.rp.autocorrect_sample_rate or self.rp.sample_rate_correction_factor != 1: self.connect(self.input, self.resample) self.input2 = self.resample else: self.input2 = self.input if self.rp.input_fft_filter: self.connect(self.input2, self.fft_filter, self.sync) else: self.connect(self.input2, self.sync) # data stream self.connect((self.sync, 0), (self.sampler, 0), self.fft, (self.cfs, 0), self.phase_diff, (self.remove_pilot,0)) if self.rp.equalize_magnitude: self.connect((self.remove_pilot,0), (self.equalizer,0), self.deinterleave) else: self.connect((self.remove_pilot,0), self.deinterleave) if self.rp.softbits: if verbose: print "--> using soft bits" self.softbit_interleaver = dab_swig.complex_to_interleaved_float_vcf(self.dp.num_carriers) self.connect(self.deinterleave, self.softbit_interleaver, (self,0)) else: self.connect(self.deinterleave, self.demapper, (self,0)) # control stream self.connect((self.sync, 1), (self.sampler, 1), (self.cfs, 1), (self.remove_pilot,1)) if self.rp.equalize_magnitude: self.connect((self.remove_pilot,1), (self.equalizer,1), (self,1)) else: self.connect((self.remove_pilot,1), (self,1)) # calculate an estimate of the SNR self.phase_var_decim = gr.keep_one_in_n(gr.sizeof_gr_complex*self.dp.num_carriers, self.rp.phase_var_estimate_downsample) self.phase_var_arg = gr.complex_to_arg(dp.num_carriers) self.phase_var_v2s = gr.vector_to_stream(gr.sizeof_float, dp.num_carriers) self.phase_var_mod = dab_swig.modulo_ff(pi/2) self.phase_var_avg_mod = gr.iir_filter_ffd([rp.phase_var_estimate_alpha], [0,1-rp.phase_var_estimate_alpha]) self.phase_var_sub_avg = gr.sub_ff() self.phase_var_sqr = gr.multiply_ff() self.phase_var_avg = gr.iir_filter_ffd([rp.phase_var_estimate_alpha], [0,1-rp.phase_var_estimate_alpha]) self.probe_phase_var = gr.probe_signal_f() self.connect((self.remove_pilot,0), self.phase_var_decim, self.phase_var_arg, self.phase_var_v2s, self.phase_var_mod, (self.phase_var_sub_avg,0), (self.phase_var_sqr,0)) self.connect(self.phase_var_mod, self.phase_var_avg_mod, (self.phase_var_sub_avg,1)) self.connect(self.phase_var_sub_avg, (self.phase_var_sqr,1)) self.connect(self.phase_var_sqr, self.phase_var_avg, self.probe_phase_var) # measure processing rate self.measure_rate = dab_swig.measure_processing_rate(gr.sizeof_gr_complex, 2000000) self.connect(self.input, self.measure_rate) # debugging if debug: self.connect(self.fft, gr.file_sink(gr.sizeof_gr_complex*dp.fft_length, "debug/ofdm_after_fft.dat")) self.connect((self.cfs,0), gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_after_cfs.dat")) self.connect(self.phase_diff, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_diff_phasor.dat")) self.connect((self.remove_pilot,0), gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_pilot_removed.dat")) self.connect((self.remove_pilot,1), gr.file_sink(gr.sizeof_char, "debug/ofdm_after_cfs_trigger.dat")) self.connect(self.deinterleave, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_deinterleaved.dat")) if self.rp.equalize_magnitude: self.connect(self.equalizer, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_equalizer.dat")) if self.rp.softbits: self.connect(self.softbit_interleaver, gr.file_sink(gr.sizeof_float*dp.num_carriers*2, "debug/softbits.dat"))
def __init__(self, fft_length, cp_length, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) else: moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)] self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() movingsum2_taps = [1.0 for i in range(fft_length//2)] if 1: self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect #self.sub1 = gr.add_const_ff(-1) self.sub1 = gr.add_const_ff(0) self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) #self.pk_detect = gr.peak_detector2_fb(9) self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # Get the power of the input signal to normalize the output of the correlation self.connect(self.input, self.inputmag2, self.inputmovingsum) self.connect(self.inputmovingsum, (self.square,0)) self.connect(self.inputmovingsum, (self.square,1)) self.connect(self.square, (self.normalize,1)) self.connect(self.c2mag, (self.normalize,0)) # Create a moving sum filter for the corr output matched_filter_taps = [1.0/cp_length for i in range(cp_length)] self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps) self.connect(self.normalize, self.matched_filter) self.connect(self.matched_filter, self.sub1, self.pk_detect) #self.connect(self.matched_filter, self.pk_detect) self.connect(self.pk_detect, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.pk_detect, (self,1)) if logging: self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
def __init__(self): gr.top_block.__init__(self) parser = OptionParser(option_class=eng_option) parser.add_option("-1", "--one-channel", action="store_true", default=False, help="software synthesized Q channel") parser.add_option("-c", "--calibration", type="eng_float", default=0, help="freq offset") parser.add_option("-d", "--debug", action="store_true", default=False, help="allow time at init to attach gdb") parser.add_option("-C", "--costas-alpha", type="eng_float", default=0.125, help="Costas alpha") parser.add_option("-g", "--gain", type="eng_float", default=1.0) parser.add_option("-i", "--input-file", type="string", default="in.dat", help="specify the input file") parser.add_option("-I", "--imbe", action="store_true", default=False, help="output IMBE codewords") parser.add_option("-L", "--low-pass", type="eng_float", default=6.5e3, help="low pass cut-off", metavar="Hz") parser.add_option("-o", "--output-file", type="string", default="out.dat", help="specify the output file") parser.add_option("-p", "--polarity", action="store_true", default=False, help="use reversed polarity") parser.add_option("-s", "--sample-rate", type="int", default=96000, help="input sample rate") parser.add_option("-t", "--tone-detect", action="store_true", default=False, help="use experimental tone detect algorithm") parser.add_option("-v", "--verbose", action="store_true", default=False, help="additional output") (options, args) = parser.parse_args() sample_rate = options.sample_rate symbol_rate = 4800 samples_per_symbol = sample_rate // symbol_rate IN = gr.file_source(gr.sizeof_gr_complex, options.input_file) if options.one_channel: C2F = gr.complex_to_float() F2C = gr.float_to_complex() # osc./mixer for mixing signal down to approx. zero IF LO = gr.sig_source_c (sample_rate, gr.GR_COS_WAVE, options.calibration, 1.0, 0) MIXER = gr.multiply_cc() # get signal into normalized range (-1.0 - +1.0) # FIXME: add AGC AMP = gr.multiply_const_cc(options.gain) lpf_taps = gr.firdes.low_pass(1.0, sample_rate, options.low_pass, options.low_pass * 0.1, gr.firdes.WIN_HANN) decim_amt = 1 if options.tone_detect: if sample_rate != 96000: print "warning, only 96K has been tested." print "other rates may require theta to be reviewed/adjusted." step_size = 7.5e-8 theta = -4 # optimum timing sampling point cic_length = 48 DEMOD = repeater.tdetect_cc(samples_per_symbol, step_size, theta, cic_length) else: # decim by 2 to get 48k rate samples_per_symbol /= 2 # for DECIM sample_rate /= 2 # for DECIM decim_amt = 2 # create Gardner/Costas loop # the loop will not work if the sample levels aren't normalized (above) timing_error_gain = 0.025 # loop error gain gain_omega = 0.25 * timing_error_gain * timing_error_gain alpha = options.costas_alpha beta = 0.125 * alpha * alpha fmin = -0.025 # fmin and fmax are in radians/s fmax = 0.025 DEMOD = repeater.gardner_costas_cc(samples_per_symbol, timing_error_gain, gain_omega, alpha, beta, fmax, fmin) DECIM = gr.fir_filter_ccf (decim_amt, lpf_taps) # probably too much phase noise etc to attempt coherent demodulation # so we use differential DIFF = gr.diff_phasor_cc() # take angle of the phase difference (in radians) TOFLOAT = gr.complex_to_arg() # convert from radians such that signal is in [-3, -1, +1, +3] RESCALE = gr.multiply_const_ff(1 / (pi / 4.0)) # optional polarity reversal (should be unnec. - now autodetected) p = 1.0 if options.polarity: p = -1.0 POLARITY = gr.multiply_const_ff(p) # hard decision at specified points levels = [-2.0, 0.0, 2.0, 4.0 ] SLICER = repeater.fsk4_slicer_fb(levels) # assemble received frames and route to Wireshark via UDP hostname = "127.0.0.1" port = 23456 debug = 0 if options.verbose: debug = 255 do_imbe = False if options.imbe: do_imbe = True do_output = True # enable block's output stream do_msgq = False # msgq output not yet implemented msgq = gr.msg_queue(2) DECODER = repeater.p25_frame_assembler(hostname, port, debug, do_imbe, do_output, do_msgq, msgq) OUT = gr.file_sink(gr.sizeof_char, options.output_file) if options.one_channel: self.connect(IN, C2F, F2C, (MIXER, 0)) else: self.connect(IN, (MIXER, 0)) self.connect(LO, (MIXER, 1)) self.connect(MIXER, AMP, DECIM, DEMOD, DIFF, TOFLOAT, RESCALE, POLARITY, SLICER, DECODER, OUT) if options.debug: print 'Ready for GDB to attach (pid = %d)' % (os.getpid(),) raw_input("Press 'Enter' to continue...")
def __init__(self, freq_corr=0, avg_frames=1, decim=16, N_id_2=0, N_id_1=134): grc_wxgui.top_block_gui.__init__(self, title="Sss Corr6 Gui") _icon_path = "/usr/share/icons/hicolor/32x32/apps/gnuradio-grc.png" self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) ################################################## # Parameters ################################################## self.freq_corr = freq_corr self.avg_frames = avg_frames self.decim = decim self.N_id_2 = N_id_2 self.N_id_1 = N_id_1 ################################################## # Variables ################################################## self.vec_half_frame = vec_half_frame = 30720 * 5 / decim self.symbol_start = symbol_start = 144 / decim self.slot_0_10 = slot_0_10 = 1 self.samp_rate = samp_rate = 30720e3 / decim self.rot = rot = 0 self.noise_level = noise_level = 0 self.fft_size = fft_size = 2048 / decim self.N_re = N_re = 62 ################################################## # Blocks ################################################## _rot_sizer = wx.BoxSizer(wx.VERTICAL) self._rot_text_box = forms.text_box( parent=self.GetWin(), sizer=_rot_sizer, value=self.rot, callback=self.set_rot, label='rot', converter=forms.float_converter(), proportion=0, ) self._rot_slider = forms.slider( parent=self.GetWin(), sizer=_rot_sizer, value=self.rot, callback=self.set_rot, minimum=0, maximum=1, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_rot_sizer) self.notebook_0 = self.notebook_0 = wx.Notebook(self.GetWin(), style=wx.NB_TOP) self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "SSS ML") self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "SSS rerot") self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "SSS equ") self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "SSS in") self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "PSS equ") self.Add(self.notebook_0) _noise_level_sizer = wx.BoxSizer(wx.VERTICAL) self._noise_level_text_box = forms.text_box( parent=self.GetWin(), sizer=_noise_level_sizer, value=self.noise_level, callback=self.set_noise_level, label='noise_level', converter=forms.float_converter(), proportion=0, ) self._noise_level_slider = forms.slider( parent=self.GetWin(), sizer=_noise_level_sizer, value=self.noise_level, callback=self.set_noise_level, minimum=0, maximum=10, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_noise_level_sizer) self.wxgui_scopesink2_0_1_0_0_0_0 = scopesink2.scope_sink_f( self.notebook_0.GetPage(4).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=2, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(4).Add(self.wxgui_scopesink2_0_1_0_0_0_0.win) self.wxgui_scopesink2_0_1_0_0_0 = scopesink2.scope_sink_c( self.notebook_0.GetPage(3).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=1, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(3).Add(self.wxgui_scopesink2_0_1_0_0_0.win) self.wxgui_scopesink2_0_1_0_0 = scopesink2.scope_sink_c( self.notebook_0.GetPage(2).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=1, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(2).Add(self.wxgui_scopesink2_0_1_0_0.win) self.wxgui_scopesink2_0_1_0 = scopesink2.scope_sink_c( self.notebook_0.GetPage(1).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=2, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(1).Add(self.wxgui_scopesink2_0_1_0.win) self.wxgui_scopesink2_0_1 = scopesink2.scope_sink_f( self.notebook_0.GetPage(0).GetWin(), title="Scope Plot", sample_rate=100 / avg_frames, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=1, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(0).Add(self.wxgui_scopesink2_0_1.win) _symbol_start_sizer = wx.BoxSizer(wx.VERTICAL) self._symbol_start_text_box = forms.text_box( parent=self.GetWin(), sizer=_symbol_start_sizer, value=self.symbol_start, callback=self.set_symbol_start, label='symbol_start', converter=forms.int_converter(), proportion=0, ) self._symbol_start_slider = forms.slider( parent=self.GetWin(), sizer=_symbol_start_sizer, value=self.symbol_start, callback=self.set_symbol_start, minimum=0, maximum=144 / decim, num_steps=144 / decim, style=wx.SL_HORIZONTAL, cast=int, proportion=1, ) self.Add(_symbol_start_sizer) self.sss_ml_fd2_0 = sss_ml_fd2(avg_frames=avg_frames, ) self.sss_fd_vec_2 = gr.vector_source_c( (gen_sss_fd(N_id_1, N_id_2, N_re).get_sss(slot_0_10 == 0)), True, N_re) self.sss_fd_vec_1 = gr.vector_source_c( (gen_sss_fd(N_id_1, N_id_2, N_re).get_sss(slot_0_10 != 0)), True, N_re) self.sss_equ2_0 = sss_equ2() self.sss_derot_0 = sss_derot() self.pss_fd_vec = gr.vector_source_c( (gen_pss_fd(N_id_2, N_re, False).get_data()), True, N_re) self.gr_vector_to_stream_0_0_2_0 = gr.vector_to_stream( gr.sizeof_gr_complex * 1, N_re) self.gr_vector_to_stream_0_0_2 = gr.vector_to_stream( gr.sizeof_gr_complex * 1, N_re) self.gr_vector_to_stream_0_0_1_0_0 = gr.vector_to_stream( gr.sizeof_gr_complex * 1, N_re) self.gr_vector_to_stream_0_0_1_0 = gr.vector_to_stream( gr.sizeof_gr_complex * 1, N_re) self.gr_vector_to_stream_0_0_1 = gr.vector_to_stream( gr.sizeof_gr_complex * 1, N_re) self.gr_vector_to_stream_0_0 = gr.vector_to_stream( gr.sizeof_gr_complex * 1, N_re) self.gr_vector_to_stream_0 = gr.vector_to_stream( gr.sizeof_gr_complex * 1, fft_size) self.gr_throttle_0 = gr.throttle(gr.sizeof_gr_complex * 1, samp_rate) self.gr_stream_to_vector_0_0 = gr.stream_to_vector( gr.sizeof_gr_complex * 1, N_re) self.gr_stream_to_vector_0 = gr.stream_to_vector( gr.sizeof_gr_complex * 1, fft_size) self.gr_stream_mux_0 = gr.stream_mux(gr.sizeof_gr_complex * 1, (N_re / 2, N_re / 2)) self.gr_null_source_0 = gr.null_source(gr.sizeof_gr_complex * 1) self.gr_noise_source_x_0 = gr.noise_source_c(gr.GR_GAUSSIAN, noise_level, 0) self.gr_multiply_xx_1_0 = gr.multiply_vcc(N_re) self.gr_multiply_const_vxx_0 = gr.multiply_const_vcc( (0.005 * exp(rot * 2 * numpy.pi * 1j), )) self.gr_keep_m_in_n_0_0 = gr.keep_m_in_n(gr.sizeof_gr_complex, N_re / 2, fft_size, (fft_size - N_re) / 2 - 1) self.gr_keep_m_in_n_0 = gr.keep_m_in_n(gr.sizeof_gr_complex, N_re / 2, fft_size, (fft_size) / 2) self.gr_interleave_1 = gr.interleave(gr.sizeof_gr_complex * N_re) self.gr_file_source_0 = gr.file_source( gr.sizeof_gr_complex * 1, "/home/user/git/gr-lte/gr-lte/test/octave/foo_sss_td_in.cfile", True) self.gr_fft_vxx_0 = gr.fft_vcc(fft_size, True, (window.blackmanharris(1024)), True, 1) self.gr_deinterleave_0 = gr.deinterleave(gr.sizeof_gr_complex * N_re) self.gr_complex_to_arg_1 = gr.complex_to_arg(1) self.gr_complex_to_arg_0 = gr.complex_to_arg(1) self.gr_channel_model_0 = gr.channel_model( noise_voltage=0.005 * noise_level, frequency_offset=0.0, epsilon=1, taps=(0.005 * exp(rot * 2 * numpy.pi * 1j), ), noise_seed=0, ) self.gr_add_xx_0 = gr.add_vcc(1) self.blks2_selector_0_0 = grc_blks2.selector( item_size=gr.sizeof_gr_complex * 1, num_inputs=2, num_outputs=1, input_index=0, output_index=0, ) self.blks2_selector_0 = grc_blks2.selector( item_size=gr.sizeof_gr_complex * 1, num_inputs=3, num_outputs=2, input_index=0, output_index=0, ) ################################################## # Connections ################################################## self.connect((self.gr_noise_source_x_0, 0), (self.gr_add_xx_0, 1)) self.connect((self.gr_add_xx_0, 0), (self.gr_multiply_const_vxx_0, 0)) self.connect((self.blks2_selector_0, 0), (self.gr_channel_model_0, 0)) self.connect((self.blks2_selector_0, 1), (self.gr_add_xx_0, 0)) self.connect((self.gr_channel_model_0, 0), (self.blks2_selector_0_0, 0)) self.connect((self.gr_multiply_const_vxx_0, 0), (self.blks2_selector_0_0, 1)) self.connect((self.gr_file_source_0, 0), (self.blks2_selector_0, 0)) self.connect((self.blks2_selector_0_0, 0), (self.gr_throttle_0, 0)) self.connect((self.gr_fft_vxx_0, 0), (self.gr_vector_to_stream_0, 0)) self.connect((self.gr_stream_to_vector_0_0, 0), (self.gr_deinterleave_0, 0)) self.connect((self.gr_stream_to_vector_0, 0), (self.gr_fft_vxx_0, 0)) self.connect((self.gr_keep_m_in_n_0_0, 0), (self.gr_stream_mux_0, 0)) self.connect((self.gr_keep_m_in_n_0, 0), (self.gr_stream_mux_0, 1)) self.connect((self.gr_stream_mux_0, 0), (self.gr_stream_to_vector_0_0, 0)) self.connect((self.gr_throttle_0, 0), (self.gr_stream_to_vector_0, 0)) self.connect((self.gr_null_source_0, 0), (self.blks2_selector_0, 1)) self.connect((self.gr_null_source_0, 0), (self.blks2_selector_0, 2)) self.connect((self.gr_vector_to_stream_0_0, 0), (self.wxgui_scopesink2_0_1_0, 0)) self.connect((self.sss_fd_vec_1, 0), (self.gr_interleave_1, 0)) self.connect((self.sss_fd_vec_2, 0), (self.gr_interleave_1, 1)) self.connect((self.gr_deinterleave_0, 1), (self.sss_equ2_0, 1)) self.connect((self.sss_derot_0, 0), (self.sss_ml_fd2_0, 0)) self.connect((self.gr_interleave_1, 0), (self.sss_derot_0, 1)) self.connect((self.sss_equ2_0, 0), (self.sss_derot_0, 0)) self.connect((self.gr_interleave_1, 0), (self.sss_ml_fd2_0, 1)) self.connect((self.gr_vector_to_stream_0, 0), (self.gr_keep_m_in_n_0_0, 0)) self.connect((self.gr_vector_to_stream_0, 0), (self.gr_keep_m_in_n_0, 0)) self.connect((self.sss_ml_fd2_0, 0), (self.wxgui_scopesink2_0_1, 0)) self.connect((self.sss_derot_0, 0), (self.gr_vector_to_stream_0_0, 0)) self.connect((self.sss_equ2_0, 0), (self.gr_vector_to_stream_0_0_2, 0)) self.connect((self.gr_vector_to_stream_0_0_2, 0), (self.wxgui_scopesink2_0_1_0_0, 0)) self.connect((self.gr_deinterleave_0, 0), (self.gr_vector_to_stream_0_0_2_0, 0)) self.connect((self.gr_vector_to_stream_0_0_2_0, 0), (self.wxgui_scopesink2_0_1_0_0_0, 0)) self.connect((self.gr_deinterleave_0, 1), (self.gr_multiply_xx_1_0, 0)) self.connect((self.sss_equ2_0, 1), (self.gr_multiply_xx_1_0, 1)) self.connect((self.gr_multiply_xx_1_0, 0), (self.gr_vector_to_stream_0_0_1, 0)) self.connect((self.pss_fd_vec, 0), (self.gr_vector_to_stream_0_0_1_0, 0)) self.connect((self.gr_vector_to_stream_0_0_1_0, 0), (self.gr_complex_to_arg_1, 0)) self.connect((self.gr_complex_to_arg_1, 0), (self.wxgui_scopesink2_0_1_0_0_0_0, 1)) self.connect((self.gr_vector_to_stream_0_0_1, 0), (self.gr_complex_to_arg_0, 0)) self.connect((self.gr_complex_to_arg_0, 0), (self.wxgui_scopesink2_0_1_0_0_0_0, 0)) self.connect((self.gr_interleave_1, 0), (self.gr_vector_to_stream_0_0_1_0_0, 0)) self.connect((self.gr_vector_to_stream_0_0_1_0_0, 0), (self.wxgui_scopesink2_0_1_0, 1)) self.connect((self.gr_deinterleave_0, 0), (self.sss_equ2_0, 0)) self.connect((self.pss_fd_vec, 0), (self.sss_equ2_0, 2))
def __init__(self, fft_length, cp_length, kstime, logging=False): """ OFDM synchronization using PN Correlation and initial cross-correlation: F. Tufvesson, O. Edfors, and M. Faulkner, "Time and Frequency Synchronization for OFDM using PN-Sequency Preambles," IEEE Proc. VTC, 1999, pp. 2203-2207. This implementation is meant to be a more robust version of the Schmidl and Cox receiver design. By correlating against the preamble and using that as the input to the time-delayed correlation, this circuit produces a very clean timing signal at the end of the preamble. The timing is more accurate and does not have the problem associated with determining the timing from the plateau structure in the Schmidl and Cox. This implementation appears to require that the signal is received with a normalized power or signal scalling factor to reduce ambiguities intorduced from partial correlation of the cyclic prefix and the peak detection. A better peak detection block might fix this. Also, the cross-correlation falls apart as the frequency offset gets larger and completely fails when an integer offset is introduced. Another thing to look at. """ gr.hier_block2.__init__( self, "ofdm_sync_pnac", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) symbol_length = fft_length + cp_length # PN Sync with cross-correlation input # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime[0:fft_length // 2]] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length / 2) # Correlation from ML Sync self.conjg = gr.conjugate_cc() self.corr = gr.multiply_cc() # Create a moving sum filter for the input self.mag = gr.complex_to_mag_squared() movingsum_taps = (fft_length // 1) * [ 1.0, ] self.power = gr.fir_filter_fff(1, movingsum_taps) # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.compare = gr.sub_ff() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.threshold = gr.threshold_ff( 0, 0, 0) # threshold detection might need to be tweaked self.peaks = gr.float_to_char() self.connect(self, self.input) # Cross-correlate input signal with known preamble self.connect(self.input, self.crosscorr_filter) # use the output of the cross-correlation as input time-shifted correlation self.connect(self.crosscorr_filter, self.delay) self.connect(self.crosscorr_filter, (self.corr, 0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr, 1)) self.connect(self.corr, self.c2mag) self.connect(self.corr, self.angle) self.connect(self.angle, (self.sample_and_hold, 0)) # Get the power of the input signal to compare against the correlation self.connect(self.crosscorr_filter, self.mag, self.power) # Compare the power to the correlator output to determine timing peak # When the peak occurs, it peaks above zero, so the thresholder detects this self.connect(self.c2mag, (self.compare, 0)) self.connect(self.power, (self.compare, 1)) self.connect(self.compare, self.threshold) self.connect(self.threshold, self.peaks, (self.sample_and_hold, 1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self, 0)) self.connect(self.peaks, (self, 1)) if logging: self.connect( self.compare, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-compare_f.dat")) self.connect( self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-theta_f.dat")) self.connect( self.power, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-inputpower_f.dat")) self.connect( self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-epsilon_f.dat")) self.connect( self.threshold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-threshold_f.dat")) self.connect( self.peaks, gr.file_sink(gr.sizeof_char, "ofdm_sync_pnac-peaks_b.dat")) self.connect( self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pnac-sample_and_hold_f.dat")) self.connect( self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pnac-input_c.dat"))
def __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, kstime, threshold, threshold_type, threshold_gap, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) else: moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)] self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() movingsum2_taps = [1.0 for i in range(fft_length//2)] #movingsum2_taps = [0.5 for i in range(fft_length*4)] #apurv - implementing Veljo's suggestion, when pause b/w packets if 1: self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.sub1 = gr.add_const_ff(-1) self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) #apurv - implementing Veljo's suggestion, when pause b/w packets self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) #self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # apurv-- #self.connect(self.angle, gr.delay(gr.sizeof_float, offset), (self.sample_and_hold, 0)) #apurv++ cross_correlate = 1 if cross_correlate==1: # cross-correlate with the known symbol kstime = [k.conjugate() for k in kstime] kstime.reverse() self.crosscorr_filter = gr.fir_filter_ccc(1, kstime) # get the magnitude # self.corrmag = gr.complex_to_mag_squared() self.f2b = gr.float_to_char() self.threshold_factor = threshold #0.0012 #0.012 #0.0015 if 0: self.slice = gr.threshold_ff(self.threshold_factor, self.threshold_factor, 0, fft_length) else: #thresholds = [self.threshold_factor, 9e-5] self.slice = gr.threshold_ff(threshold, threshold, 0, fft_length, threshold_type, threshold_gap) self.connect(self.input, self.crosscorr_filter, self.corrmag, self.slice, self.f2b) # some debug dump # self.connect(self.corrmag, gr.file_sink(gr.sizeof_float, "ofdm_corrmag.dat")) #self.connect(self.f2b, gr.file_sink(gr.sizeof_char, "ofdm_f2b.dat")) self.connect(self.f2b, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) #self.connect(self.pk_detect, (self,1)) #removed #self.connect(self.f2b, gr.delay(gr.sizeof_char, 1), (self, 1)) self.connect(self.f2b, (self, 1)) if logging: self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
def __init__(self): gr.top_block.__init__(self) parser = OptionParser(option_class=eng_option) parser.add_option("-1", "--one-channel", action="store_true", default=False, help="software synthesized Q channel") parser.add_option("-a", "--agc", action="store_true", default=False, help="automatic gain control (overrides --gain)") parser.add_option("-c", "--calibration", type="eng_float", default=0, help="freq offset") parser.add_option("-d", "--debug", action="store_true", default=False, help="allow time at init to attach gdb") parser.add_option("-C", "--costas-alpha", type="eng_float", default=0.125, help="Costas alpha") parser.add_option("-g", "--gain", type="eng_float", default=1.0) parser.add_option("-i", "--input-file", type="string", default="in.dat", help="specify the input file") parser.add_option("-I", "--imbe", action="store_true", default=False, help="output IMBE codewords") parser.add_option("-L", "--low-pass", type="eng_float", default=6.5e3, help="low pass cut-off", metavar="Hz") parser.add_option("-o", "--output-file", type="string", default="out.dat", help="specify the output file") parser.add_option("-p", "--polarity", action="store_true", default=False, help="use reversed polarity") parser.add_option("-r", "--raw-symbols", type="string", default=None, help="dump decoded symbols to file") parser.add_option("-s", "--sample-rate", type="int", default=96000, help="input sample rate") parser.add_option("-t", "--tone-detect", action="store_true", default=False, help="use experimental tone detect algorithm") parser.add_option("-v", "--verbose", action="store_true", default=False, help="additional output") parser.add_option("-6", "--k6k", action="store_true", default=False, help="use 6K symbol rate") (options, args) = parser.parse_args() sample_rate = options.sample_rate if options.k6k: symbol_rate = 6000 else: symbol_rate = 4800 samples_per_symbol = sample_rate // symbol_rate IN = gr.file_source(gr.sizeof_gr_complex, options.input_file) if options.one_channel: C2F = gr.complex_to_float() F2C = gr.float_to_complex() # osc./mixer for mixing signal down to approx. zero IF LO = gr.sig_source_c(sample_rate, gr.GR_COS_WAVE, options.calibration, 1.0, 0) MIXER = gr.multiply_cc() # get signal into normalized range (-1.0 - +1.0) if options.agc: AMP = gr.feedforward_agc_cc(16, 1.0) else: AMP = gr.multiply_const_cc(options.gain) lpf_taps = gr.firdes.low_pass(1.0, sample_rate, options.low_pass, options.low_pass * 0.1, gr.firdes.WIN_HANN) decim_amt = 1 if options.tone_detect: if sample_rate != 96000: print "warning, only 96K has been tested." print "other rates may require theta to be reviewed/adjusted." step_size = 7.5e-8 theta = -4 # optimum timing sampling point cic_length = 48 DEMOD = repeater.tdetect_cc(samples_per_symbol, step_size, theta, cic_length) else: # decim by 2 to get 48k rate samples_per_symbol /= 2 # for DECIM sample_rate /= 2 # for DECIM decim_amt = 2 # create Gardner/Costas loop # the loop will not work if the sample levels aren't normalized (above) timing_error_gain = 0.025 # loop error gain gain_omega = 0.25 * timing_error_gain * timing_error_gain alpha = options.costas_alpha beta = 0.125 * alpha * alpha fmin = -0.025 # fmin and fmax are in radians/s fmax = 0.025 DEMOD = repeater.gardner_costas_cc(samples_per_symbol, timing_error_gain, gain_omega, alpha, beta, fmax, fmin) DECIM = gr.fir_filter_ccf(decim_amt, lpf_taps) # probably too much phase noise etc to attempt coherent demodulation # so we use differential DIFF = gr.diff_phasor_cc() # take angle of the phase difference (in radians) TOFLOAT = gr.complex_to_arg() # convert from radians such that signal is in [-3, -1, +1, +3] RESCALE = gr.multiply_const_ff(1 / (pi / 4.0)) # optional polarity reversal (should be unnec. - now autodetected) p = 1.0 if options.polarity: p = -1.0 POLARITY = gr.multiply_const_ff(p) # hard decision at specified points levels = [-2.0, 0.0, 2.0, 4.0] SLICER = repeater.fsk4_slicer_fb(levels) # assemble received frames and route to Wireshark via UDP hostname = "127.0.0.1" port = 23456 debug = 0 if options.verbose: debug = 255 do_imbe = False if options.imbe: do_imbe = True do_output = True # enable block's output stream do_msgq = False # msgq output not yet implemented msgq = gr.msg_queue(2) DECODER = repeater.p25_frame_assembler(hostname, port, debug, do_imbe, do_output, do_msgq, msgq) OUT = gr.file_sink(gr.sizeof_char, options.output_file) if options.one_channel: self.connect(IN, C2F, F2C, (MIXER, 0)) else: self.connect(IN, (MIXER, 0)) self.connect(LO, (MIXER, 1)) self.connect(MIXER, AMP, DECIM, DEMOD, DIFF, TOFLOAT, RESCALE, POLARITY, SLICER, DECODER, OUT) if options.raw_symbols: SINKC = gr.file_sink(gr.sizeof_char, options.raw_symbols) self.connect(SLICER, SINKC) if options.debug: print 'Ready for GDB to attach (pid = %d)' % (os.getpid(), ) raw_input("Press 'Enter' to continue...")
def __init__(self, samples_per_symbol=_def_samples_per_symbol, excess_bw=_def_excess_bw, costas_alpha=_def_costas_alpha, gain_mu=_def_gain_mu, mu=_def_mu, omega_relative_limit=_def_omega_relative_limit, gray_code=_def_gray_code, verbose=_def_verbose, log=_def_log): """ Hierarchical block for RRC-filtered CQPSK demodulation The input is the complex modulated signal at baseband. The output is a stream of floats in [ -3 / -1 / +1 / +3 ] @param samples_per_symbol: samples per symbol >= 2 @type samples_per_symbol: float @param excess_bw: Root-raised cosine filter excess bandwidth @type excess_bw: float @param costas_alpha: loop filter gain @type costas_alphas: float @param gain_mu: for M&M block @type gain_mu: float @param mu: for M&M block @type mu: float @param omega_relative_limit: for M&M block @type omega_relative_limit: float @param gray_code: Tell modulator to Gray code the bits @type gray_code: bool @param verbose: Print information about modulator? @type verbose: bool @param debug: Print modualtion data to files? @type debug: bool """ gr.hier_block2.__init__(self, "cqpsk_demod", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature self._samples_per_symbol = samples_per_symbol self._excess_bw = excess_bw self._costas_alpha = costas_alpha self._mm_gain_mu = gain_mu self._mm_mu = mu self._mm_omega_relative_limit = omega_relative_limit self._gray_code = gray_code if samples_per_symbol < 2: raise TypeError, "sbp must be >= 2, is %d" % samples_per_symbol arity = pow(2,self.bits_per_symbol()) # Automatic gain control scale = (1.0/16384.0) self.pre_scaler = gr.multiply_const_cc(scale) # scale the signal from full-range to +-1 #self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100) self.agc = gr.feedforward_agc_cc(16, 2.0) # RRC data filter ntaps = 11 * samples_per_symbol self.rrc_taps = gr.firdes.root_raised_cosine( 1.0, # gain self._samples_per_symbol, # sampling rate 1.0, # symbol rate self._excess_bw, # excess bandwidth (roll-off factor) ntaps) self.rrc_filter=gr.interp_fir_filter_ccf(1, self.rrc_taps) if not self._mm_gain_mu: sbs_to_mm = {2: 0.050, 3: 0.075, 4: 0.11, 5: 0.125, 6: 0.15, 7: 0.15} self._mm_gain_mu = sbs_to_mm[samples_per_symbol] self._mm_omega = self._samples_per_symbol self._mm_gain_omega = .25 * self._mm_gain_mu * self._mm_gain_mu self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha fmin = -0.025 fmax = 0.025 if not _def_has_gr_digital: self.receiver=gr.mpsk_receiver_cc(arity, pi/4.0, self._costas_alpha, self._costas_beta, fmin, fmax, self._mm_mu, self._mm_gain_mu, self._mm_omega, self._mm_gain_omega, self._mm_omega_relative_limit) else: self.receiver=digital.mpsk_receiver_cc(arity, pi/4.0, 2*pi/150, fmin, fmax, self._mm_mu, self._mm_gain_mu, self._mm_omega, self._mm_gain_omega, self._mm_omega_relative_limit) self.receiver.set_alpha(self._costas_alpha) self.receiver.set_beta(self._costas_beta) # Perform Differential decoding on the constellation self.diffdec = gr.diff_phasor_cc() # take angle of the difference (in radians) self.to_float = gr.complex_to_arg() # convert from radians such that signal is in -3/-1/+1/+3 self.rescale = gr.multiply_const_ff( 1 / (pi / 4) ) if verbose: self._print_verbage() if log: self._setup_logging() # Connect & Initialize base class self.connect(self, self.pre_scaler, self.agc, self.rrc_filter, self.receiver, self.diffdec, self.to_float, self.rescale, self)
def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="Audio Fft 2") ################################################## # Variables ################################################## self.samp_rate = samp_rate = 48000 ################################################## # Blocks ################################################## self.wxgui_numbersink2_0_0 = numbersink2.number_sink_f( self.GetWin(), unit="deg", minval=-90, maxval=90, factor=90/3.14159, decimal_places=10, ref_level=0, sample_rate=samp_rate, number_rate=15, average=False, avg_alpha=None, label="Angle", peak_hold=False, show_gauge=True, ) self.Add(self.wxgui_numbersink2_0_0.win) self.wxgui_numbersink2_0 = numbersink2.number_sink_f( self.GetWin(), unit="mag", minval=-100, maxval=0, factor=20, decimal_places=10, ref_level=0, sample_rate=samp_rate, number_rate=15, average=False, avg_alpha=None, label="Magnitude", peak_hold=False, show_gauge=True, ) self.Add(self.wxgui_numbersink2_0.win) self.wxgui_fftsink2_0_0 = fftsink2.fft_sink_c( self.GetWin(), baseband_freq=0, y_per_div=10, y_divs=10, ref_level=0, ref_scale=2.0, sample_rate=samp_rate, fft_size=1024, fft_rate=15, average=False, avg_alpha=None, title="Reflect", peak_hold=False, win=window.hamming, ) self.Add(self.wxgui_fftsink2_0_0.win) self.wxgui_fftsink2_0 = fftsink2.fft_sink_c( self.GetWin(), baseband_freq=0, y_per_div=10, y_divs=10, ref_level=0, ref_scale=2.0, sample_rate=samp_rate, fft_size=1024, fft_rate=15, average=False, avg_alpha=None, title="Reference", peak_hold=False, win=window.hamming, ) self.Add(self.wxgui_fftsink2_0.win) self.gr_nlog10_ff_0 = gr.nlog10_ff(1, 1, 0) self.gr_divide_xx_0 = gr.divide_cc(1) self.gr_complex_to_mag_0 = gr.complex_to_mag(1) self.gr_complex_to_arg_0 = gr.complex_to_arg(1) self.band_pass_filter_0_0 = gr.fir_filter_fcc(1, firdes.complex_band_pass( 1, samp_rate, 4.8e3, 5.2e3, 100, firdes.WIN_HAMMING, 6.76)) self.band_pass_filter_0 = gr.fir_filter_fcc(1, firdes.complex_band_pass( 1, samp_rate, 4.8e3, 5.2e3, 100, firdes.WIN_HAMMING, 6.76)) self.audio_source_0 = audio.source(samp_rate, "", True) ################################################## # Connections ################################################## self.connect((self.audio_source_0, 0), (self.band_pass_filter_0, 0)) self.connect((self.band_pass_filter_0, 0), (self.wxgui_fftsink2_0, 0)) self.connect((self.audio_source_0, 1), (self.band_pass_filter_0_0, 0)) self.connect((self.band_pass_filter_0_0, 0), (self.wxgui_fftsink2_0_0, 0)) self.connect((self.band_pass_filter_0, 0), (self.gr_divide_xx_0, 0)) self.connect((self.band_pass_filter_0_0, 0), (self.gr_divide_xx_0, 1)) self.connect((self.gr_divide_xx_0, 0), (self.gr_complex_to_arg_0, 0)) self.connect((self.gr_complex_to_arg_0, 0), (self.wxgui_numbersink2_0_0, 0)) self.connect((self.gr_complex_to_mag_0, 0), (self.gr_nlog10_ff_0, 0)) self.connect((self.gr_nlog10_ff_0, 0), (self.wxgui_numbersink2_0, 0)) self.connect((self.band_pass_filter_0_0, 0), (self.gr_complex_to_mag_0, 0))
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, freq_corr=0, avg_frames=1, decim=16, N_id_2=0, N_id_1=134): grc_wxgui.top_block_gui.__init__(self, title="Sss Corr6 Gui") _icon_path = "/usr/share/icons/hicolor/32x32/apps/gnuradio-grc.png" self.SetIcon(wx.Icon(_icon_path, wx.BITMAP_TYPE_ANY)) ################################################## # Parameters ################################################## self.freq_corr = freq_corr self.avg_frames = avg_frames self.decim = decim self.N_id_2 = N_id_2 self.N_id_1 = N_id_1 ################################################## # Variables ################################################## self.vec_half_frame = vec_half_frame = 30720*5/decim self.symbol_start = symbol_start = 144/decim self.slot_0_10 = slot_0_10 = 1 self.samp_rate = samp_rate = 30720e3/decim self.rot = rot = 0 self.noise_level = noise_level = 0 self.fft_size = fft_size = 2048/decim self.N_re = N_re = 62 ################################################## # Blocks ################################################## _rot_sizer = wx.BoxSizer(wx.VERTICAL) self._rot_text_box = forms.text_box( parent=self.GetWin(), sizer=_rot_sizer, value=self.rot, callback=self.set_rot, label='rot', converter=forms.float_converter(), proportion=0, ) self._rot_slider = forms.slider( parent=self.GetWin(), sizer=_rot_sizer, value=self.rot, callback=self.set_rot, minimum=0, maximum=1, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_rot_sizer) self.notebook_0 = self.notebook_0 = wx.Notebook(self.GetWin(), style=wx.NB_TOP) self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "SSS ML") self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "SSS rerot") self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "SSS equ") self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "SSS in") self.notebook_0.AddPage(grc_wxgui.Panel(self.notebook_0), "PSS equ") self.Add(self.notebook_0) _noise_level_sizer = wx.BoxSizer(wx.VERTICAL) self._noise_level_text_box = forms.text_box( parent=self.GetWin(), sizer=_noise_level_sizer, value=self.noise_level, callback=self.set_noise_level, label='noise_level', converter=forms.float_converter(), proportion=0, ) self._noise_level_slider = forms.slider( parent=self.GetWin(), sizer=_noise_level_sizer, value=self.noise_level, callback=self.set_noise_level, minimum=0, maximum=10, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_noise_level_sizer) self.wxgui_scopesink2_0_1_0_0_0_0 = scopesink2.scope_sink_f( self.notebook_0.GetPage(4).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=2, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(4).Add(self.wxgui_scopesink2_0_1_0_0_0_0.win) self.wxgui_scopesink2_0_1_0_0_0 = scopesink2.scope_sink_c( self.notebook_0.GetPage(3).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=1, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(3).Add(self.wxgui_scopesink2_0_1_0_0_0.win) self.wxgui_scopesink2_0_1_0_0 = scopesink2.scope_sink_c( self.notebook_0.GetPage(2).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=1, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(2).Add(self.wxgui_scopesink2_0_1_0_0.win) self.wxgui_scopesink2_0_1_0 = scopesink2.scope_sink_c( self.notebook_0.GetPage(1).GetWin(), title="Scope Plot", sample_rate=samp_rate, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=2, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(1).Add(self.wxgui_scopesink2_0_1_0.win) self.wxgui_scopesink2_0_1 = scopesink2.scope_sink_f( self.notebook_0.GetPage(0).GetWin(), title="Scope Plot", sample_rate=100/avg_frames, v_scale=0, v_offset=0, t_scale=0, ac_couple=False, xy_mode=False, num_inputs=1, trig_mode=gr.gr_TRIG_MODE_AUTO, y_axis_label="Counts", ) self.notebook_0.GetPage(0).Add(self.wxgui_scopesink2_0_1.win) _symbol_start_sizer = wx.BoxSizer(wx.VERTICAL) self._symbol_start_text_box = forms.text_box( parent=self.GetWin(), sizer=_symbol_start_sizer, value=self.symbol_start, callback=self.set_symbol_start, label='symbol_start', converter=forms.int_converter(), proportion=0, ) self._symbol_start_slider = forms.slider( parent=self.GetWin(), sizer=_symbol_start_sizer, value=self.symbol_start, callback=self.set_symbol_start, minimum=0, maximum=144/decim, num_steps=144/decim, style=wx.SL_HORIZONTAL, cast=int, proportion=1, ) self.Add(_symbol_start_sizer) self.sss_ml_fd2_0 = sss_ml_fd2( avg_frames=avg_frames, ) self.sss_fd_vec_2 = gr.vector_source_c((gen_sss_fd(N_id_1,N_id_2, N_re).get_sss(slot_0_10==0)), True, N_re) self.sss_fd_vec_1 = gr.vector_source_c((gen_sss_fd(N_id_1,N_id_2, N_re).get_sss(slot_0_10!=0)), True, N_re) self.sss_equ2_0 = sss_equ2() self.sss_derot_0 = sss_derot() self.pss_fd_vec = gr.vector_source_c((gen_pss_fd(N_id_2, N_re, False).get_data()), True, N_re) self.gr_vector_to_stream_0_0_2_0 = gr.vector_to_stream(gr.sizeof_gr_complex*1, N_re) self.gr_vector_to_stream_0_0_2 = gr.vector_to_stream(gr.sizeof_gr_complex*1, N_re) self.gr_vector_to_stream_0_0_1_0_0 = gr.vector_to_stream(gr.sizeof_gr_complex*1, N_re) self.gr_vector_to_stream_0_0_1_0 = gr.vector_to_stream(gr.sizeof_gr_complex*1, N_re) self.gr_vector_to_stream_0_0_1 = gr.vector_to_stream(gr.sizeof_gr_complex*1, N_re) self.gr_vector_to_stream_0_0 = gr.vector_to_stream(gr.sizeof_gr_complex*1, N_re) self.gr_vector_to_stream_0 = gr.vector_to_stream(gr.sizeof_gr_complex*1, fft_size) self.gr_throttle_0 = gr.throttle(gr.sizeof_gr_complex*1, samp_rate) self.gr_stream_to_vector_0_0 = gr.stream_to_vector(gr.sizeof_gr_complex*1, N_re) self.gr_stream_to_vector_0 = gr.stream_to_vector(gr.sizeof_gr_complex*1, fft_size) self.gr_stream_mux_0 = gr.stream_mux(gr.sizeof_gr_complex*1, (N_re/2, N_re/2)) self.gr_null_source_0 = gr.null_source(gr.sizeof_gr_complex*1) self.gr_noise_source_x_0 = gr.noise_source_c(gr.GR_GAUSSIAN, noise_level, 0) self.gr_multiply_xx_1_0 = gr.multiply_vcc(N_re) self.gr_multiply_const_vxx_0 = gr.multiply_const_vcc((0.005*exp(rot*2*numpy.pi*1j), )) self.gr_keep_m_in_n_0_0 = gr.keep_m_in_n(gr.sizeof_gr_complex, N_re/2, fft_size, (fft_size-N_re)/2-1) self.gr_keep_m_in_n_0 = gr.keep_m_in_n(gr.sizeof_gr_complex, N_re/2, fft_size, (fft_size)/2) self.gr_interleave_1 = gr.interleave(gr.sizeof_gr_complex*N_re) self.gr_file_source_0 = gr.file_source(gr.sizeof_gr_complex*1, "/home/user/git/gr-lte/gr-lte/test/octave/foo_sss_td_in.cfile", True) self.gr_fft_vxx_0 = gr.fft_vcc(fft_size, True, (window.blackmanharris(1024)), True, 1) self.gr_deinterleave_0 = gr.deinterleave(gr.sizeof_gr_complex*N_re) self.gr_complex_to_arg_1 = gr.complex_to_arg(1) self.gr_complex_to_arg_0 = gr.complex_to_arg(1) self.gr_channel_model_0 = gr.channel_model( noise_voltage=0.005*noise_level, frequency_offset=0.0, epsilon=1, taps=(0.005*exp(rot*2*numpy.pi*1j), ), noise_seed=0, ) self.gr_add_xx_0 = gr.add_vcc(1) self.blks2_selector_0_0 = grc_blks2.selector( item_size=gr.sizeof_gr_complex*1, num_inputs=2, num_outputs=1, input_index=0, output_index=0, ) self.blks2_selector_0 = grc_blks2.selector( item_size=gr.sizeof_gr_complex*1, num_inputs=3, num_outputs=2, input_index=0, output_index=0, ) ################################################## # Connections ################################################## self.connect((self.gr_noise_source_x_0, 0), (self.gr_add_xx_0, 1)) self.connect((self.gr_add_xx_0, 0), (self.gr_multiply_const_vxx_0, 0)) self.connect((self.blks2_selector_0, 0), (self.gr_channel_model_0, 0)) self.connect((self.blks2_selector_0, 1), (self.gr_add_xx_0, 0)) self.connect((self.gr_channel_model_0, 0), (self.blks2_selector_0_0, 0)) self.connect((self.gr_multiply_const_vxx_0, 0), (self.blks2_selector_0_0, 1)) self.connect((self.gr_file_source_0, 0), (self.blks2_selector_0, 0)) self.connect((self.blks2_selector_0_0, 0), (self.gr_throttle_0, 0)) self.connect((self.gr_fft_vxx_0, 0), (self.gr_vector_to_stream_0, 0)) self.connect((self.gr_stream_to_vector_0_0, 0), (self.gr_deinterleave_0, 0)) self.connect((self.gr_stream_to_vector_0, 0), (self.gr_fft_vxx_0, 0)) self.connect((self.gr_keep_m_in_n_0_0, 0), (self.gr_stream_mux_0, 0)) self.connect((self.gr_keep_m_in_n_0, 0), (self.gr_stream_mux_0, 1)) self.connect((self.gr_stream_mux_0, 0), (self.gr_stream_to_vector_0_0, 0)) self.connect((self.gr_throttle_0, 0), (self.gr_stream_to_vector_0, 0)) self.connect((self.gr_null_source_0, 0), (self.blks2_selector_0, 1)) self.connect((self.gr_null_source_0, 0), (self.blks2_selector_0, 2)) self.connect((self.gr_vector_to_stream_0_0, 0), (self.wxgui_scopesink2_0_1_0, 0)) self.connect((self.sss_fd_vec_1, 0), (self.gr_interleave_1, 0)) self.connect((self.sss_fd_vec_2, 0), (self.gr_interleave_1, 1)) self.connect((self.gr_deinterleave_0, 1), (self.sss_equ2_0, 1)) self.connect((self.sss_derot_0, 0), (self.sss_ml_fd2_0, 0)) self.connect((self.gr_interleave_1, 0), (self.sss_derot_0, 1)) self.connect((self.sss_equ2_0, 0), (self.sss_derot_0, 0)) self.connect((self.gr_interleave_1, 0), (self.sss_ml_fd2_0, 1)) self.connect((self.gr_vector_to_stream_0, 0), (self.gr_keep_m_in_n_0_0, 0)) self.connect((self.gr_vector_to_stream_0, 0), (self.gr_keep_m_in_n_0, 0)) self.connect((self.sss_ml_fd2_0, 0), (self.wxgui_scopesink2_0_1, 0)) self.connect((self.sss_derot_0, 0), (self.gr_vector_to_stream_0_0, 0)) self.connect((self.sss_equ2_0, 0), (self.gr_vector_to_stream_0_0_2, 0)) self.connect((self.gr_vector_to_stream_0_0_2, 0), (self.wxgui_scopesink2_0_1_0_0, 0)) self.connect((self.gr_deinterleave_0, 0), (self.gr_vector_to_stream_0_0_2_0, 0)) self.connect((self.gr_vector_to_stream_0_0_2_0, 0), (self.wxgui_scopesink2_0_1_0_0_0, 0)) self.connect((self.gr_deinterleave_0, 1), (self.gr_multiply_xx_1_0, 0)) self.connect((self.sss_equ2_0, 1), (self.gr_multiply_xx_1_0, 1)) self.connect((self.gr_multiply_xx_1_0, 0), (self.gr_vector_to_stream_0_0_1, 0)) self.connect((self.pss_fd_vec, 0), (self.gr_vector_to_stream_0_0_1_0, 0)) self.connect((self.gr_vector_to_stream_0_0_1_0, 0), (self.gr_complex_to_arg_1, 0)) self.connect((self.gr_complex_to_arg_1, 0), (self.wxgui_scopesink2_0_1_0_0_0_0, 1)) self.connect((self.gr_vector_to_stream_0_0_1, 0), (self.gr_complex_to_arg_0, 0)) self.connect((self.gr_complex_to_arg_0, 0), (self.wxgui_scopesink2_0_1_0_0_0_0, 0)) self.connect((self.gr_interleave_1, 0), (self.gr_vector_to_stream_0_0_1_0_0, 0)) self.connect((self.gr_vector_to_stream_0_0_1_0_0, 0), (self.wxgui_scopesink2_0_1_0, 1)) self.connect((self.gr_deinterleave_0, 0), (self.sss_equ2_0, 0)) self.connect((self.pss_fd_vec, 0), (self.sss_equ2_0, 2))
def __init__(self, options): gr.hier_block2.__init__(self, "fbmc_receive_path", gr.io_signature(1,1,gr.sizeof_gr_complex), gr.io_signature(0,0,0)) print "This is FBMC receive path 1x1" common_options.defaults(options) config = self.config = station_configuration() config.data_subcarriers = dsubc = options.subcarriers config.cp_length = 0 config.frame_data_blocks = options.data_blocks config._verbose = options.verbose #TODO: update config.fft_length = options.fft_length config.dc_null = options.dc_null config.training_data = default_block_header(dsubc, config.fft_length,config.dc_null,options) config.coding = options.coding config.ber_window = options.ber_window config.periodic_parts = 8 config.frame_id_blocks = 1 # FIXME self._options = copy.copy(options) #FIXME: do we need this? config.fbmc = options.fbmc config.block_length = config.fft_length + config.cp_length config.frame_data_part = config.frame_data_blocks + config.frame_id_blocks config.frame_length = config.training_data.fbmc_no_preambles + 2*config.frame_data_part config.postpro_frame_length = config.frame_data_part + \ config.training_data.no_pilotsyms config.subcarriers = dsubc + \ config.training_data.pilot_subcarriers config.virtual_subcarriers = config.fft_length - config.subcarriers - config.dc_null total_subc = config.subcarriers # check some bounds if config.fft_length < config.subcarriers: raise SystemError, "Subcarrier number must be less than FFT length" if config.fft_length < config.cp_length: raise SystemError, "Cyclic prefix length must be less than FFT length" #self.input = gr.kludge_copy(gr.sizeof_gr_complex) #self.connect( self, self.input ) self.input = self self.ideal = options.ideal self.ideal2 = options.ideal2 ## Inner receiver ## Timing & Frequency Synchronization ## Channel estimation + Equalization ## Phase Tracking for sampling clock frequency offset correction inner_receiver = self.inner_receiver = fbmc_inner_receiver( options, options.log ) self.connect( self.input, inner_receiver ) ofdm_blocks = ( inner_receiver, 2 ) frame_start = ( inner_receiver, 1 ) disp_ctf = ( inner_receiver, 0 ) #self.snr_est_preamble = ( inner_receiver, 3 ) #terminate_stream(self,snr_est_preamble) disp_cfo = ( inner_receiver, 3 ) if self.ideal is False and self.ideal2 is False: self.zmq_probe_freqoff = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5557") self.connect(disp_cfo, self.zmq_probe_freqoff) else: self.connect(disp_cfo, blocks.null_sink(gr.sizeof_float)) # for ID decoder used_id_bits = config.used_id_bits = 8 #TODO: constant in source code! rep_id_bits = config.rep_id_bits = dsubc/used_id_bits #BPSK if options.log: print "rep_id_bits %d" % (rep_id_bits) if dsubc % used_id_bits <> 0: raise SystemError,"Data subcarriers need to be multiple of 10" ## Workaround to avoid periodic structure seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] ## NOTE!!! BIG HACK!!! ## first preamble ain't equalized .... ## for Milan's SNR estimator ## Outer Receiver ## Make new inner receiver compatible with old outer receiver ## FIXME: renew outer receiver self.ctf = disp_ctf #frame_sampler = ofdm_frame_sampler(options) frame_sampler = fbmc_frame_sampler(options) self.connect( ofdm_blocks, frame_sampler) self.connect( frame_start, (frame_sampler,1) ) # # ft = [0] * config.frame_length # ft[0] = 1 # # # The next block ensures that only complete frames find their way into # # the old outer receiver. The dynamic frame start trigger is hence # # replaced with a static one, fixed to the frame length. # # frame_sampler = ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, # config.frame_length ) # self.symbol_output = blocks.vector_to_stream( gr.sizeof_gr_complex * total_subc, # config.frame_length ) # delayed_frame_start = blocks.delay( gr.sizeof_char, config.frame_length - 1 ) # damn_static_frame_trigger = blocks.vector_source_b( ft, True ) # # if options.enable_erasure_decision: # frame_gate = vector_sampler( # gr.sizeof_gr_complex * total_subc * config.frame_length, 1 ) # self.connect( ofdm_blocks, frame_sampler, frame_gate, # self.symbol_output ) # else: # self.connect( ofdm_blocks, frame_sampler, self.symbol_output ) # # self.connect( frame_start, delayed_frame_start, ( frame_sampler, 1 ) ) if options.enable_erasure_decision: frame_gate = frame_sampler.frame_gate self.symbol_output = frame_sampler orig_frame_start = frame_start frame_start = (frame_sampler,1) self.frame_trigger = frame_start #terminate_stream(self, self.frame_trigger) ## Pilot block filter pb_filt = self._pilot_block_filter = fbmc_pilot_block_filter() self.connect(self.symbol_output,pb_filt) self.connect(self.frame_trigger,(pb_filt,1)) self.frame_data_trigger = (pb_filt,1) #self.symbol_output = pb_filt #if options.log: #log_to_file(self, pb_filt, "data/pb_filt_out.compl") if config.fbmc: pda_in = pb_filt else: ## Pilot subcarrier filter ps_filt = self._pilot_subcarrier_filter = pilot_subcarrier_filter() self.connect(self.symbol_output,ps_filt) if options.log: log_to_file(self, ps_filt, "data/ps_filt_out.compl") pda_in = ps_filt ## Workaround to avoid periodic structure # for ID decoder seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] if not options.enable_erasure_decision: ## ID Block Filter # Filter ID block, skip data blocks id_bfilt = self._id_block_filter = vector_sampler( gr.sizeof_gr_complex * dsubc, 1 ) if not config.frame_id_blocks == 1: raise SystemExit, "# ID Blocks > 1 not supported" self.connect( pda_in , id_bfilt ) self.connect( self.frame_data_trigger, ( id_bfilt, 1 ) ) # trigger #log_to_file( self, id_bfilt, "data/id_bfilt.compl" ) ## ID Demapper and Decoder, soft decision self.id_dec = self._id_decoder = ofdm.coded_bpsk_soft_decoder( dsubc, used_id_bits, whitener_pn ) self.connect( id_bfilt, self.id_dec ) print "Using coded BPSK soft decoder for ID detection" else: # options.enable_erasure_decision: id_bfilt = self._id_block_filter = vector_sampler( gr.sizeof_gr_complex * total_subc, config.frame_id_blocks ) id_bfilt_trig_delay = 0 for x in range( config.frame_length ): if x in config.training_data.pilotsym_pos: id_bfilt_trig_delay += 1 else: break print "Position of ID block within complete frame: %d" %(id_bfilt_trig_delay) assert( id_bfilt_trig_delay > 0 ) # else not supported id_bfilt_trig = blocks.delay( gr.sizeof_char, id_bfilt_trig_delay ) self.connect( ofdm_blocks, id_bfilt ) self.connect( orig_frame_start, id_bfilt_trig, ( id_bfilt, 1 ) ) self.id_dec = self._id_decoder = ofdm.coded_bpsk_soft_decoder( total_subc, used_id_bits, whitener_pn, config.training_data.shifted_pilot_tones ) self.connect( id_bfilt, self.id_dec ) print "Using coded BPSK soft decoder for ID detection" # The threshold block either returns 1.0 if the llr-value from the # id decoder is below the threshold, else 0.0. Hence we convert this # into chars, 0 and 1, and use it as trigger for the sampler. min_llr = ( self.id_dec, 1 ) erasure_threshold = gr.threshold_ff( 10.0, 10.0, 0 ) # FIXME is it the optimal threshold? erasure_dec = gr.float_to_char() id_gate = vector_sampler( gr.sizeof_short, 1 ) ctf_gate = vector_sampler( gr.sizeof_float * total_subc, 1 ) self.connect( self.id_dec , id_gate ) self.connect( self.ctf, ctf_gate ) self.connect( min_llr, erasure_threshold, erasure_dec ) self.connect( erasure_dec, ( frame_gate, 1 ) ) self.connect( erasure_dec, ( id_gate, 1 ) ) self.connect( erasure_dec, ( ctf_gate, 1 ) ) self.id_dec = self._id_decoder = id_gate self.ctf = ctf_gate print "Erasure decision for IDs is enabled" if options.log: id_dec_f = gr.short_to_float() self.connect(self.id_dec,id_dec_f) log_to_file(self, id_dec_f, "data/id_dec_out.float") if options.log: log_to_file(self, id_bfilt, "data/id_blockfilter_out.compl") # TODO: refactor names if options.log: map_src_f = gr.char_to_float(dsubc) self.connect(map_src,map_src_f) log_to_file(self, map_src_f, "data/map_src_out.float") ## Allocation Control if options.static_allocation: #DEBUG if options.coding: mode = 1 # Coding mode 1-9 bitspermode = [0.5,1,1.5,2,3,4,4.5,5,6] # Information bits per mode bitcount_vec = [(int)(config.data_subcarriers*config.frame_data_blocks*bitspermode[mode-1])] bitloading = mode else: bitloading = 1 bitcount_vec = [config.data_subcarriers*config.frame_data_blocks*bitloading] #bitcount_vec = [config.data_subcarriers*config.frame_data_blocks] self.bitcount_src = blocks.vector_source_i(bitcount_vec,True,1) # 0s for ID block, then data #bitloading_vec = [0]*dsubc+[0]*(dsubc/2)+[2]*(dsubc/2) bitloading_vec = [0]*dsubc+[bitloading]*dsubc bitloading_src = blocks.vector_source_b(bitloading_vec,True,dsubc) power_vec = [1]*config.data_subcarriers power_src = blocks.vector_source_f(power_vec,True,dsubc) else: self.allocation_buffer = ofdm.allocation_buffer(config.data_subcarriers, config.frame_data_blocks, "tcp://"+options.tx_hostname+":3333",config.coding) self.bitcount_src = (self.allocation_buffer,0) bitloading_src = (self.allocation_buffer,1) power_src = (self.allocation_buffer,2) self.connect(self.id_dec, self.allocation_buffer) if options.benchmarking: self.allocation_buffer.set_allocation([4]*config.data_subcarriers,[1]*config.data_subcarriers) if options.log: log_to_file(self, self.bitcount_src, "data/bitcount_src_rx.int") log_to_file(self, bitloading_src, "data/bitloading_src_rx.char") log_to_file(self, power_src, "data/power_src_rx.cmplx") log_to_file(self, self.id_dec, "data/id_dec_rx.short") ## Power Deallocator pda = self._power_deallocator = multiply_frame_fc(config.frame_data_part, dsubc) self.connect(pda_in,(pda,0)) self.connect(power_src,(pda,1)) ## Demodulator # if 0: # ac_vector = [0.0+0.0j]*208 # ac_vector[0] = (2*10**(-0.452)) # ac_vector[3] = (10**(-0.651)) # ac_vector[7] = (10**(-1.151)) # csi_vector_inv=abs(numpy.fft.fft(numpy.sqrt(ac_vector)))**2 # dm_csi = numpy.fft.fftshift(csi_vector_inv) # TODO dm_csi = [1]*dsubc # TODO dm_csi = blocks.vector_source_f(dm_csi,True) ## Depuncturer dp_trig = [0]*(config.frame_data_blocks/2) dp_trig[0] = 1 dp_trig = blocks.vector_source_b(dp_trig,True) # TODO if(options.coding): fo=ofdm.fsm(1,2,[91,121]) if options.interleave: int_object=trellis.interleaver(2000,666) deinterlv = trellis.permutation(int_object.K(),int_object.DEINTER(),1,gr.sizeof_float) demod = self._data_demodulator = generic_softdemapper_vcf(dsubc, config.frame_data_part, config.coding) #self.connect(dm_csi,blocks.stream_to_vector(gr.sizeof_float,dsubc),(demod,2)) if(options.ideal): self.connect(dm_csi,blocks.stream_to_vector(gr.sizeof_float,dsubc),(demod,2)) else: dm_csi_filter = self.dm_csi_filter = filter.single_pole_iir_filter_ff(0.01,dsubc) self.connect(self.ctf, self.dm_csi_filter,(demod,2)) #log_to_file(self, dm_csi_filter, "data/softs_csi.float") #self.connect(dm_trig,(demod,3)) else: demod = self._data_demodulator = generic_demapper_vcb(dsubc, config.frame_data_part) if options.benchmarking: # Do receiver benchmarking until the number of frames x symbols are collected self.connect(pda,blocks.head(gr.sizeof_gr_complex*dsubc, options.N*config.frame_data_blocks),demod) else: self.connect(pda,demod) self.connect(bitloading_src,(demod,1)) if(options.coding): ## Depuncturing if not options.nopunct: depuncturing = depuncture_ff(dsubc,0) frametrigger_bitmap_filter = blocks.vector_source_b([1,0],True) self.connect(bitloading_src,(depuncturing,1)) self.connect(dp_trig,(depuncturing,2)) ## Decoding chunkdivisor = int(numpy.ceil(config.frame_data_blocks/5.0)) print "Number of chunks at Viterbi decoder: ", chunkdivisor decoding = self._data_decoder = ofdm.viterbi_combined_fb(fo,dsubc,-1,-1,2,chunkdivisor,[-1,-1,-1,1,1,-1,1,1],ofdm.TRELLIS_EUCLIDEAN) if options.log and options.coding: log_to_file(self, decoding, "data/decoded.char") if not options.nopunct: log_to_file(self, depuncturing, "data/vit_in.float") if not options.nopunct: if options.interleave: self.connect(demod,deinterlv,depuncturing,decoding) else: self.connect(demod,depuncturing,decoding) else: self.connect(demod,decoding) self.connect(self.bitcount_src, multiply_const_ii(1./chunkdivisor), (decoding,1)) if options.scatterplot or options.scatter_plot_before_phase_tracking: if self.ideal2 is False: scatter_vec_elem = self._scatter_vec_elem = ofdm.vector_element(dsubc,40) scatter_s2v = self._scatter_s2v = blocks.stream_to_vector(gr.sizeof_gr_complex,config.frame_data_blocks) scatter_id_filt = skip(gr.sizeof_gr_complex*dsubc,config.frame_data_blocks) scatter_id_filt.skip_call(0) scatter_trig = [0]*config.frame_data_part scatter_trig[0] = 1 scatter_trig = blocks.vector_source_b(scatter_trig,True) self.connect(scatter_trig,(scatter_id_filt,1)) self.connect(scatter_vec_elem,scatter_s2v) if not options.scatter_plot_before_phase_tracking: print "Enabling Scatterplot for data subcarriers" self.connect(pda,scatter_id_filt,scatter_vec_elem) # Work on this #scatter_sink = ofdm.scatterplot_sink(dsubc) #self.connect(pda,scatter_sink) #self.connect(map_src,(scatter_sink,1)) #self.connect(dm_trig,(scatter_sink,2)) #print "Enabled scatterplot gui interface" self.zmq_probe_scatter = zeromq.pub_sink(gr.sizeof_gr_complex,config.frame_data_blocks, "tcp://*:5560") self.connect(scatter_s2v, blocks.keep_one_in_n(gr.sizeof_gr_complex*config.frame_data_blocks,20), self.zmq_probe_scatter) else: print "Enabling Scatterplot for data before phase tracking" inner_rx = inner_receiver.before_phase_tracking #scatter_sink2 = ofdm.scatterplot_sink(dsubc,"phase_tracking") op = copy.copy(options) op.enable_erasure_decision = False new_framesampler = ofdm_frame_sampler(op) self.connect( inner_rx, new_framesampler ) self.connect( orig_frame_start, (new_framesampler,1) ) new_ps_filter = pilot_subcarrier_filter() new_pb_filter = fbmc_pilot_block_filter() self.connect( (new_framesampler,1), (new_pb_filter,1) ) self.connect( new_framesampler, new_pb_filter, new_ps_filter, scatter_id_filt, scatter_vec_elem ) #self.connect( new_ps_filter, scatter_sink2 ) #self.connect( map_src, (scatter_sink2,1)) #self.connect( dm_trig, (scatter_sink2,2)) if options.log: if(options.coding): log_to_file(self, demod, "data/data_stream_out.float") else: data_f = gr.char_to_float() self.connect(demod,data_f) log_to_file(self, data_f, "data/data_stream_out.float") if options.sfo_feedback: used_id_bits = 8 rep_id_bits = config.data_subcarriers/used_id_bits seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] id_enc = ofdm.repetition_encoder_sb(used_id_bits,rep_id_bits,whitener_pn) self.connect( self.id_dec, id_enc ) id_mod = ofdm_bpsk_modulator(dsubc) self.connect( id_enc, id_mod ) id_mod_conj = gr.conjugate_cc(dsubc) self.connect( id_mod, id_mod_conj ) id_mult = blocks.multiply_vcc(dsubc) self.connect( id_bfilt, ( id_mult,0) ) self.connect( id_mod_conj, ( id_mult,1) ) # id_mult_avg = filter.single_pole_iir_filter_cc(0.01,dsubc) # self.connect( id_mult, id_mult_avg ) id_phase = gr.complex_to_arg(dsubc) self.connect( id_mult, id_phase ) log_to_file( self, id_phase, "data/id_phase.float" ) est=ofdm.LS_estimator_straight_slope(dsubc) self.connect(id_phase,est) slope=blocks.multiply_const_ff(1e6/2/3.14159265) self.connect( (est,0), slope ) log_to_file( self, slope, "data/slope.float" ) log_to_file( self, (est,1), "data/offset.float" ) # ------------------------------------------------------------------------ # # Display some information about the setup if config._verbose: self._print_verbage() ## debug logging ## if options.log: # log_to_file(self,self.ofdm_symbols,"data/unequalized_rx_ofdm_symbols.compl") # log_to_file(self,self.ofdm_symbols,"data/unequalized_rx_ofdm_symbols.float",mag=True) fftlen = 256 my_window = window.hamming(fftlen) #.blackmanharris(fftlen) rxs_sampler = vector_sampler(gr.sizeof_gr_complex,fftlen) rxs_sampler_vect = concatenate([[1],[0]*49]) rxs_trigger = blocks.vector_source_b(rxs_sampler_vect.tolist(),True) rxs_window = blocks.multiply_const_vcc(my_window) rxs_spectrum = gr.fft_vcc(fftlen,True,[],True) rxs_mag = gr.complex_to_mag(fftlen) rxs_avg = filter.single_pole_iir_filter_ff(0.01,fftlen) #rxs_logdb = blocks.nlog10_ff(20.0,fftlen,-20*log10(fftlen)) rxs_logdb = gr.kludge_copy( gr.sizeof_float * fftlen ) rxs_decimate_rate = gr.keep_one_in_n(gr.sizeof_float*fftlen,1) self.connect(rxs_trigger,(rxs_sampler,1)) self.connect(self.input,rxs_sampler,rxs_window, rxs_spectrum,rxs_mag,rxs_avg,rxs_logdb, rxs_decimate_rate) log_to_file( self, rxs_decimate_rate, "data/psd_input.float" ) #output branches self.publish_rx_performance_measure()
def __init__(self, fft_length, cp_length, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) # PN Sync # Create a delay line self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) # Correlation from ML Sync self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); # Create a moving sum filter for the corr output if 1: moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) else: moving_sum_taps = [complex(1.0,0.0) for i in range(fft_length//2)] self.moving_sum_filter = gr.fft_filter_ccc(1,moving_sum_taps) # Create a moving sum filter for the input self.inputmag2 = gr.complex_to_mag_squared() # Modified by Yong (12.06.27) #movingsum2_taps = [1.0 for i in range(fft_length//2)] movingsum2_taps = [0.5 for i in range(fft_length)] if 1: self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) else: self.inputmovingsum = gr.fft_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() # Get magnitude (peaks) and angle (phase/freq error) self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = gr.sample_and_hold_ff() #ML measurements input to sampler block and detect self.sub1 = gr.add_const_ff(-1) self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) #self.pk_detect = gr.peak_detector2_fb(9) self.connect(self, self.input) # Calculate the frequency offset from the correlation of the preamble self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) # Get the power of the input signal to normalize the output of the correlation self.connect(self.input, self.inputmag2, self.inputmovingsum) self.connect(self.inputmovingsum, (self.square,0)) self.connect(self.inputmovingsum, (self.square,1)) self.connect(self.square, (self.normalize,1)) self.connect(self.c2mag, (self.normalize,0)) # Create a moving sum filter for the corr output matched_filter_taps = [1.0/cp_length for i in range(cp_length)] self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps) self.connect(self.normalize, self.matched_filter) self.connect(self.matched_filter, self.sub1, self.pk_detect) #self.connect(self.matched_filter, self.pk_detect) self.connect(self.pk_detect, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.pk_detect, (self,1)) if logging: self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-nominator_f.dat")) self.connect(self.square, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-denominator_f.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
def __init__(self, samples_per_symbol=_def_samples_per_symbol, excess_bw=_def_excess_bw, costas_alpha=_def_costas_alpha, gain_mu=_def_gain_mu, mu=_def_mu, omega_relative_limit=_def_omega_relative_limit, gray_code=_def_gray_code, verbose=_def_verbose, log=_def_log): """ Hierarchical block for RRC-filtered CQPSK demodulation The input is the complex modulated signal at baseband. The output is a stream of floats in [ -3 / -1 / +1 / +3 ] @param samples_per_symbol: samples per symbol >= 2 @type samples_per_symbol: float @param excess_bw: Root-raised cosine filter excess bandwidth @type excess_bw: float @param costas_alpha: loop filter gain @type costas_alphas: float @param gain_mu: for M&M block @type gain_mu: float @param mu: for M&M block @type mu: float @param omega_relative_limit: for M&M block @type omega_relative_limit: float @param gray_code: Tell modulator to Gray code the bits @type gray_code: bool @param verbose: Print information about modulator? @type verbose: bool @param debug: Print modualtion data to files? @type debug: bool """ gr.hier_block2.__init__( self, "cqpsk_demod", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature(1, 1, gr.sizeof_float)) # Output signature self._samples_per_symbol = samples_per_symbol self._excess_bw = excess_bw self._costas_alpha = costas_alpha self._mm_gain_mu = gain_mu self._mm_mu = mu self._mm_omega_relative_limit = omega_relative_limit self._gray_code = gray_code if samples_per_symbol < 2: raise TypeError, "sbp must be >= 2, is %d" % samples_per_symbol arity = pow(2, self.bits_per_symbol()) # Automatic gain control scale = (1.0 / 16384.0) self.pre_scaler = gr.multiply_const_cc( scale) # scale the signal from full-range to +-1 #self.agc = gr.agc2_cc(0.6e-1, 1e-3, 1, 1, 100) self.agc = gr.feedforward_agc_cc(16, 2.0) # RRC data filter ntaps = 11 * samples_per_symbol self.rrc_taps = gr.firdes.root_raised_cosine( 1.0, # gain self._samples_per_symbol, # sampling rate 1.0, # symbol rate self._excess_bw, # excess bandwidth (roll-off factor) ntaps) self.rrc_filter = gr.interp_fir_filter_ccf(1, self.rrc_taps) if not self._mm_gain_mu: sbs_to_mm = { 2: 0.050, 3: 0.075, 4: 0.11, 5: 0.125, 6: 0.15, 7: 0.15 } self._mm_gain_mu = sbs_to_mm[samples_per_symbol] self._mm_omega = self._samples_per_symbol self._mm_gain_omega = .25 * self._mm_gain_mu * self._mm_gain_mu self._costas_beta = 0.25 * self._costas_alpha * self._costas_alpha fmin = -0.025 fmax = 0.025 self.receiver = gr.mpsk_receiver_cc( arity, pi / 4.0, self._costas_alpha, self._costas_beta, fmin, fmax, self._mm_mu, self._mm_gain_mu, self._mm_omega, self._mm_gain_omega, self._mm_omega_relative_limit) # Perform Differential decoding on the constellation self.diffdec = gr.diff_phasor_cc() # take angle of the difference (in radians) self.to_float = gr.complex_to_arg() # convert from radians such that signal is in -3/-1/+1/+3 self.rescale = gr.multiply_const_ff(1 / (pi / 4)) if verbose: self._print_verbage() if log: self._setup_logging() # Connect & Initialize base class self.connect(self, self.pre_scaler, self.agc, self.rrc_filter, self.receiver, self.diffdec, self.to_float, self.rescale, self)
def __init__(self, mode, debug=False): """ OFDM time and coarse frequency synchronisation for DAB @param mode DAB mode (1-4) @param debug if True: write data streams out to files """ if mode < 1 or mode > 4: raise ValueError, "Invalid DAB mode: " + str( mode) + " (modes 1-4 exist)" # get the correct DAB parameters dp = parameters.dab_parameters(mode) rp = parameters.receiver_parameters(mode) gr.hier_block2.__init__( self, "ofdm_sync_dab", gr.io_signature(1, 1, gr.sizeof_gr_complex), # input signature gr.io_signature2(2, 2, gr.sizeof_gr_complex, gr.sizeof_char)) # output signature # workaround for a problem that prevents connecting more than one block directly (see trac ticket #161) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) # # null-symbol detection # # (outsourced to detect_zero.py) self.ns_detect = detect_null.detect_null(dp.ns_length, debug) self.connect(self.input, self.ns_detect) # # fine frequency synchronisation # # the code for fine frequency synchronisation is adapted from # ofdm_sync_ml.py; it abuses the cyclic prefix to find the fine # frequency error, as suggested in "ML Estimation of Timing and # Frequency Offset in OFDM Systems", by Jan-Jaap van de Beek, # Magnus Sandell, Per Ola Börjesson, see # http://www.sm.luth.se/csee/sp/research/report/bsb96r.html self.ffs_delay = gr.delay(gr.sizeof_gr_complex, dp.fft_length) self.ffs_conj = gr.conjugate_cc() self.ffs_mult = gr.multiply_cc() # self.ffs_moving_sum = gr.fir_filter_ccf(1, [1]*dp.cp_length) self.ffs_moving_sum = dab_swig.moving_sum_cc(dp.cp_length) self.ffs_angle = gr.complex_to_arg() self.ffs_angle_scale = gr.multiply_const_ff(1. / dp.fft_length) self.ffs_delay_sample_and_hold = gr.delay( gr.sizeof_char, dp.symbol_length) # sample the value at the end of the symbol .. self.ffs_sample_and_hold = gr.sample_and_hold_ff() self.ffs_delay_input_for_correction = gr.delay( gr.sizeof_gr_complex, dp.symbol_length ) # by delaying the input, we can use the ff offset estimation from the first symbol to correct the first symbol itself self.ffs_nco = gr.frequency_modulator_fc( 1) # ffs_sample_and_hold directly outputs phase error per sample self.ffs_mixer = gr.multiply_cc() # calculate fine frequency error self.connect(self.input, self.ffs_conj, self.ffs_mult) self.connect(self.input, self.ffs_delay, (self.ffs_mult, 1)) self.connect(self.ffs_mult, self.ffs_moving_sum, self.ffs_angle) # only use the value from the first half of the first symbol self.connect(self.ffs_angle, self.ffs_angle_scale, (self.ffs_sample_and_hold, 0)) self.connect(self.ns_detect, self.ffs_delay_sample_and_hold, (self.ffs_sample_and_hold, 1)) # do the correction self.connect(self.ffs_sample_and_hold, self.ffs_nco, (self.ffs_mixer, 0)) self.connect(self.input, self.ffs_delay_input_for_correction, (self.ffs_mixer, 1)) # output - corrected signal and start of DAB frames self.connect(self.ffs_mixer, (self, 0)) self.connect(self.ffs_delay_sample_and_hold, (self, 1)) if debug: self.connect( self.ffs_angle, gr.file_sink(gr.sizeof_float, "debug/ofdm_sync_dab_ffs_angle.dat")) self.connect( self.ffs_sample_and_hold, gr.multiply_const_ff(1. / (dp.T * 2 * pi)), gr.file_sink(gr.sizeof_float, "debug/ofdm_sync_dab_fine_freq_err_f.dat")) self.connect( self.ffs_mixer, gr.file_sink(gr.sizeof_gr_complex, "debug/ofdm_sync_dab_fine_freq_corrected_c.dat"))
def __init__(self, dab_params, rx_params, debug=False): """ OFDM time and coarse frequency synchronisation for DAB @param mode DAB mode (1-4) @param debug if True: write data streams out to files """ dp = dab_params rp = rx_params gr.hier_block2.__init__(self,"ofdm_sync_dab", gr.io_signature(1, 1, gr.sizeof_gr_complex), # input signature gr.io_signature2(2, 2, gr.sizeof_gr_complex, gr.sizeof_char)) # output signature # workaround for a problem that prevents connecting more than one block directly (see trac ticket #161) self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) # # null-symbol detection # # (outsourced to detect_zero.py) self.ns_detect = detect_null.detect_null(dp.ns_length, debug) self.connect(self.input, self.ns_detect) # # fine frequency synchronisation # # the code for fine frequency synchronisation is adapted from # ofdm_sync_ml.py; it abuses the cyclic prefix to find the fine # frequency error, as suggested in "ML Estimation of Timing and # Frequency Offset in OFDM Systems", by Jan-Jaap van de Beek, # Magnus Sandell, Per Ola Börjesson, see # http://www.sm.luth.se/csee/sp/research/report/bsb96r.html self.ffs_delay = gr.delay(gr.sizeof_gr_complex, dp.fft_length) self.ffs_conj = gr.conjugate_cc() self.ffs_mult = gr.multiply_cc() self.ffs_moving_sum = dab_swig.moving_sum_cc(dp.cp_length) self.ffs_arg = gr.complex_to_arg() self.ffs_sample_and_average = dab_swig.ofdm_ffs_sample(dp.symbol_length, dp.fft_length, rp.symbols_for_ffs_estimation, rp.ffs_alpha, dp.sample_rate) if rp.correct_ffe: self.ffs_delay_input_for_correction = gr.delay(gr.sizeof_gr_complex, dp.symbol_length*rp.symbols_for_ffs_estimation) # by delaying the input, we can use the ff offset estimation from the first symbol to correct the first symbol itself self.ffs_delay_frame_start = gr.delay(gr.sizeof_char, dp.symbol_length*rp.symbols_for_ffs_estimation) # sample the value at the end of the symbol .. self.ffs_nco = gr.frequency_modulator_fc(1) # ffs_sample_and_hold directly outputs phase error per sample self.ffs_mixer = gr.multiply_cc() # calculate fine frequency error self.connect(self.input, self.ffs_conj, self.ffs_mult) self.connect(self.input, self.ffs_delay, (self.ffs_mult, 1)) self.connect(self.ffs_mult, self.ffs_moving_sum, self.ffs_arg, (self.ffs_sample_and_average, 0)) self.connect(self.ns_detect, (self.ffs_sample_and_average, 1)) if rp.correct_ffe: # do the correction self.connect(self.ffs_sample_and_average, self.ffs_nco, (self.ffs_mixer, 0)) self.connect(self.input, self.ffs_delay_input_for_correction, (self.ffs_mixer, 1)) # output - corrected signal and start of DAB frames self.connect(self.ffs_mixer, (self, 0)) self.connect(self.ns_detect, self.ffs_delay_frame_start, (self, 1)) else: # just patch the signal through self.connect(self.ffs_sample_and_average, gr.null_sink(gr.sizeof_float)) self.connect(self.input, (self,0)) # frame start still needed .. self.connect(self.ns_detect, (self,1)) if debug: self.connect(self.ffs_sample_and_average, gr.multiply_const_ff(1./(dp.T*2*pi)), gr.file_sink(gr.sizeof_float, "debug/ofdm_sync_dab_fine_freq_err_f.dat")) self.connect(self.ffs_mixer, gr.file_sink(gr.sizeof_gr_complex, "debug/ofdm_sync_dab_fine_freq_corrected_c.dat"))
def __init__(self, fft_length, cp_length, logging=False): """ OFDM synchronization using PN Correlation: T. M. Schmidl and D. C. Cox, "Robust Frequency and Timing Synchonization for OFDM," IEEE Trans. Communications, vol. 45, no. 12, 1997. Improved with averaging over the whole FFT and peak averaging over cp_length. """ gr.hier_block2.__init__(self, "ofdm_sync_pn", gr.io_signature(1, 1, gr.sizeof_gr_complex), # Input signature gr.io_signature2(2, 2, gr.sizeof_float, gr.sizeof_char)) # Output signature self.input = gr.add_const_cc(0) self.delay = gr.delay(gr.sizeof_gr_complex, fft_length/2) self.conjg = gr.conjugate_cc(); self.corr = gr.multiply_cc(); moving_sum_taps = [1.0 for i in range(fft_length//2)] self.moving_sum_filter = gr.fir_filter_ccf(1,moving_sum_taps) self.inputmag2 = gr.complex_to_mag_squared() #movingsum2_taps = [0.125 for i in range(fft_length*4)] movingsum2_taps = [0.5 for i in range(fft_length*4)] self.inputmovingsum = gr.fir_filter_fff(1,movingsum2_taps) self.square = gr.multiply_ff() self.normalize = gr.divide_ff() self.c2mag = gr.complex_to_mag_squared() self.angle = gr.complex_to_arg() self.sample_and_hold = flex.sample_and_hold_ff() self.sub1 = gr.add_const_ff(-1) # linklab, change peak detector parameters: use higher threshold to detect rise/fall of peak #self.pk_detect = gr.peak_detector_fb(0.20, 0.20, 30, 0.001) self.pk_detect = flex.peak_detector_fb(0.7, 0.7, 30, 0.001) # linklab #self.pk_detect = gr.peak_detector2_fb(9) self.connect(self, self.input) # Lower branch: self.connect(self.input, self.delay) self.connect(self.input, (self.corr,0)) self.connect(self.delay, self.conjg) self.connect(self.conjg, (self.corr,1)) self.connect(self.corr, self.moving_sum_filter) self.connect(self.moving_sum_filter, self.c2mag) self.connect(self.moving_sum_filter, self.angle) self.connect(self.angle, (self.sample_and_hold,0)) self.connect(self.c2mag, (self.normalize,0)) # Upper branch self.connect(self.input, self.inputmag2, self.inputmovingsum) self.connect(self.inputmovingsum, (self.square,0)) self.connect(self.inputmovingsum, (self.square,1)) self.connect(self.square, (self.normalize,1)) matched_filter_taps = [1.0/cp_length for i in range(cp_length)] self.matched_filter = gr.fir_filter_fff(1,matched_filter_taps) self.connect(self.normalize, self.matched_filter) # linklab, provide the signal power into peak detector, linklab self.connect(self.square, (self.pk_detect,1)) self.connect(self.matched_filter, self.sub1, (self.pk_detect, 0)) self.connect(self.pk_detect, (self.sample_and_hold,1)) # Set output signals # Output 0: fine frequency correction value # Output 1: timing signal self.connect(self.sample_and_hold, (self,0)) self.connect(self.pk_detect, (self,1)) if logging: self.connect(self.square, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-square.dat")) self.connect(self.c2mag, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-c2mag.dat")) self.connect(self.matched_filter, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-mf_f.dat")) self.connect(self.sub1, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sub1.dat")) self.connect(self.normalize, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-theta_f.dat")) self.connect(self.angle, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-epsilon_f.dat")) self.connect(self.pk_detect, gr.file_sink(gr.sizeof_char, "ofdm_sync_pn-peaks_b.dat")) self.connect(self.sample_and_hold, gr.file_sink(gr.sizeof_float, "ofdm_sync_pn-sample_and_hold_f.dat")) self.connect(self.input, gr.file_sink(gr.sizeof_gr_complex, "ofdm_sync_pn-input_c.dat"))
def __init__(self, app, samp_rate, inter, dec): ''' in: - app = object of type TXApp - samp_rate = sample rate in Herz - inter = interpolation factor - dec = decimation factor ''' gr.hier_block2.__init__(self, "TXGui", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(0, 0, 0)) # instance variables self.app = app self.gui = wxgui.top_block_gui("BPSK TX", size=default_gui_size) self.nb = self.__createNoteBook() self.throttle = gr.throttle(gr.sizeof_gr_complex, samp_rate) # resampler if inter == 1 and dec == 1: self.resampler = gr.multiply_const_vcc((1,)) print("i: resampler not need") else: self.resampler = blks2.rational_resampler_ccc( interpolation=inter, decimation=dec, taps=None, fractional_bw=None, ) # samples self.fft = fftsink2.fft_sink_c( self.nb.GetPage(0).GetWin(), baseband_freq=0, y_per_div=5, y_divs=10, ref_level=-40, sample_rate= inter*samp_rate/dec, fft_size=512, fft_rate=10, average=True, avg_alpha=0.1, title="FFT Plot", peak_hold=False, ) self.scope_IQ = scopesink2.scope_sink_c( self.nb.GetPage(1).GetWin(), title="Scope IQ", sample_rate = inter*samp_rate/dec, v_scale=0.001, t_scale=0.001, ac_couple=False, xy_mode=False, num_inputs=1, ) # adding the visual components to the notebook self.nb.GetPage(0).Add(self.fft.win) self.nb.GetPage(1).Add(self.scope_IQ.win) # phase self.cmp2arg = gr.complex_to_arg() self.fftPhase = fftsink2.fft_sink_f( self.nb.GetPage(2).GetWin(), baseband_freq=0, y_per_div=5, y_divs=10, ref_level=-40, sample_rate= inter*samp_rate/dec, fft_size=512, fft_rate=10, average=True, avg_alpha=0.1, title="FFT phase Plot", peak_hold=False, ) self.scopePhase = scopesink2.scope_sink_f( self.nb.GetPage(3).GetWin(), title="Scope phase", sample_rate = inter*samp_rate/dec, v_scale=0.001, t_scale=0.001, ac_couple=False, xy_mode=False, num_inputs=1, ) # adding the visual components to the notebook self.nb.GetPage(2).Add(self.fftPhase.win) self.nb.GetPage(3).Add(self.scopePhase.win) self.__makeConnections()
def __init__(self): grc_wxgui.top_block_gui.__init__(self, title="Top Block") ################################################## # Variables ################################################## self.tranwidth = tranwidth = 10000 self.tau = tau = 50e-6 self.gain = gain = 20 self.freq = freq = 100.0e6 self.decim = decim = 80 self.cutoff = cutoff = 100000 ################################################## # Blocks ################################################## self._tranwidth_text_box = forms.text_box( parent=self.GetWin(), value=self.tranwidth, callback=self.set_tranwidth, label="tranwidth", converter=forms.float_converter(), ) self.GridAdd(self._tranwidth_text_box, 1, 1, 1, 1) _tau_sizer = wx.BoxSizer(wx.VERTICAL) self._tau_text_box = forms.text_box( parent=self.GetWin(), sizer=_tau_sizer, value=self.tau, callback=self.set_tau, label="Zeitkonstante (Tau)", converter=forms.float_converter(), proportion=0, ) self._tau_slider = forms.slider( parent=self.GetWin(), sizer=_tau_sizer, value=self.tau, callback=self.set_tau, minimum=0, maximum=100e-6, num_steps=100, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_tau_sizer) _gain_sizer = wx.BoxSizer(wx.VERTICAL) self._gain_text_box = forms.text_box( parent=self.GetWin(), sizer=_gain_sizer, value=self.gain, callback=self.set_gain, label="Gain [dB]", converter=forms.float_converter(), proportion=0, ) self._gain_slider = forms.slider( parent=self.GetWin(), sizer=_gain_sizer, value=self.gain, callback=self.set_gain, minimum=0, maximum=30, num_steps=60, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.Add(_gain_sizer) _freq_sizer = wx.BoxSizer(wx.VERTICAL) self._freq_text_box = forms.text_box( parent=self.GetWin(), sizer=_freq_sizer, value=self.freq, callback=self.set_freq, label="Frequenz (UKW)", converter=forms.float_converter(), proportion=0, ) self._freq_slider = forms.slider( parent=self.GetWin(), sizer=_freq_sizer, value=self.freq, callback=self.set_freq, minimum=87.5e6, maximum=108e6, num_steps=205, style=wx.SL_HORIZONTAL, cast=float, proportion=1, ) self.GridAdd(_freq_sizer, 0, 0, 1, 1) self._decim_text_box = forms.text_box( parent=self.GetWin(), value=self.decim, callback=self.set_decim, label="Decimation", converter=forms.float_converter(), ) self.GridAdd(self._decim_text_box, 1, 0, 1, 1) self._cutoff_text_box = forms.text_box( parent=self.GetWin(), value=self.cutoff, callback=self.set_cutoff, label="Cutoff", converter=forms.float_converter(), ) self.GridAdd(self._cutoff_text_box, 0, 1, 1, 1) self.wxgui_fftsink2_0 = fftsink2.fft_sink_f( self.GetWin(), baseband_freq=0, y_per_div=10, y_divs=10, ref_level=0, ref_scale=2.0, sample_rate=64000000/decim, fft_size=1024, fft_rate=15, average=False, avg_alpha=None, title="FFT Plot", peak_hold=False, ) self.Add(self.wxgui_fftsink2_0.win) self.uhd_usrp_source_0 = uhd.usrp_source( device_addr="type=usrp1", stream_args=uhd.stream_args( cpu_format="fc32", channels=range(1), ), ) self.uhd_usrp_source_0.set_subdev_spec("B:0", 0) self.uhd_usrp_source_0.set_samp_rate(64000000/decim) self.uhd_usrp_source_0.set_center_freq(freq, 0) self.uhd_usrp_source_0.set_gain(gain, 0) self.low_pass_filter_0_0 = gr.fir_filter_fff(1, firdes.low_pass( 1, 44100, 15000, tranwidth, firdes.WIN_HAMMING, 6.76)) self.low_pass_filter_0 = gr.fir_filter_ccf(1, firdes.low_pass( 1, 64000000/decim, cutoff, tranwidth, firdes.WIN_HAMMING, 6.76)) self.gr_multiply_xx_0 = gr.multiply_vcc(1) self.gr_iir_filter_ffd_1 = gr.iir_filter_ffd(((1.0/(1+tau*2*800000), 1.0/(1+tau*2*800000))), ((1, -(1-tau*2*800000)/(1+tau*2*800000)))) self.gr_delay_0 = gr.delay(gr.sizeof_gr_complex*1, 1) self.gr_conjugate_cc_0 = gr.conjugate_cc() self.gr_complex_to_arg_0 = gr.complex_to_arg(1) self.blks2_rational_resampler_xxx_0 = blks2.rational_resampler_fff( interpolation=44100, decimation=64000000/decim, taps=None, fractional_bw=None, ) self.audio_sink_0 = audio.sink(44100, "", True) ################################################## # Connections ################################################## self.connect((self.uhd_usrp_source_0, 0), (self.low_pass_filter_0, 0)) self.connect((self.low_pass_filter_0, 0), (self.gr_delay_0, 0)) self.connect((self.low_pass_filter_0, 0), (self.gr_multiply_xx_0, 0)) self.connect((self.gr_delay_0, 0), (self.gr_conjugate_cc_0, 0)) self.connect((self.gr_conjugate_cc_0, 0), (self.gr_multiply_xx_0, 1)) self.connect((self.gr_multiply_xx_0, 0), (self.gr_complex_to_arg_0, 0)) self.connect((self.gr_complex_to_arg_0, 0), (self.blks2_rational_resampler_xxx_0, 0)) self.connect((self.blks2_rational_resampler_xxx_0, 0), (self.low_pass_filter_0_0, 0)) self.connect((self.low_pass_filter_0_0, 0), (self.gr_iir_filter_ffd_1, 0)) self.connect((self.gr_iir_filter_ffd_1, 0), (self.audio_sink_0, 0)) self.connect((self.gr_complex_to_arg_0, 0), (self.wxgui_fftsink2_0, 0))