def publish_rx_spectrum(self,fftlen): ## RX Spectrum fftlen = 256 my_window = window.hamming(fftlen) #.blackmanharris(fftlen) rxs_sampler = vector_sampler(gr.sizeof_gr_complex,fftlen) rxs_trigger = gr.vector_source_b(concatenate([[1],[0]*199]),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 = gr.single_pole_iir_filter_ff(0.01,fftlen) rxs_logdb = gr.nlog10_ff(20.0,fftlen,-20*log10(fftlen)) rxs_decimate_rate = gr.keep_one_in_n(gr.sizeof_float*fftlen,1) msgq = gr.msg_queue(5) rxs_msg_sink = gr.message_sink(gr.sizeof_float*fftlen,msgq,True) self.connect(rxs_trigger,(rxs_sampler,1)) t = self.u if self.filter is None else self.filter self.connect(t,rxs_sampler,rxs_window, rxs_spectrum,rxs_mag,rxs_avg,rxs_logdb, rxs_decimate_rate, rxs_msg_sink) self.servants.append(corba_data_buffer_servant("spectrum",fftlen,msgq)) print "RXS trigger unique id", rxs_trigger.unique_id() print "Publishing RX baseband under id: spectrum"
def build_blocks( self, vlen, cp_len, snr_db, sigpow, startup, N, LOS_power, no_taps, have_LOS ): self.fading_channel = \ static_fading_channel( LOS_power, no_taps, vlen, have_LOS ) self.freq_resp = self.fading_channel.freq_resp assert( len(self.freq_resp) == vlen ) ############################################################################ # select channel estimator to be tested her # self.uut = channel_estimator_003( vlen, cp_len ) self.uut = channel_estimator_001( vlen) ############################################################################ block = self.uut.preamble_td #print abs(sigpow) #print sum(numpy.abs(block)**2.0)/len(block) assert( abs( sigpow - sum(numpy.abs(block)**2.0)/len(block) ) < 1e-4 ) # power if cp_len > 0: block = concatenate([ block[ len(block) - cp_len : len(block) ], block ]) self.block = block self.compare = gr.add_const_vcc( -1.0 * numpy.array(self.freq_resp) ) self.mean_squared_error = vector_acc_se2( vlen, startup, N ) self.dst = gr.vector_sink_f() self.awgn_channel = awgn_channel( snr_db, sigpow ) self.block_src = gr.vector_source_c( self.block, True ) self.sampler = ofdm.vector_sampler( gr.sizeof_gr_complex, vlen ) self.perfect_trigger = perfect_block_trigger( len(self.block) ) self.fft = scaled_fft( vlen )
def supply_rx_baseband(self): ## RX Spectrum if self.__dict__.has_key('rx_baseband'): return self.rx_baseband config = self.config fftlen = config.fft_length my_window = window.hamming(fftlen) #.blackmanharris(fftlen) rxs_sampler = vector_sampler(gr.sizeof_gr_complex, fftlen) rxs_trigger = blocks.vector_source_b(concatenate([[1], [0] * 199]), 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 = gr.single_pole_iir_filter_ff(0.01, fftlen) rxs_logdb = gr.nlog10_ff(20.0, fftlen, -20 * log10(fftlen)) rxs_decimate_rate = gr.keep_one_in_n(gr.sizeof_float * fftlen, 50) t = self.u if self.filter is None else self.filter self.connect(rxs_trigger, (rxs_sampler, 1)) self.connect(t, rxs_sampler, rxs_window, rxs_spectrum, rxs_mag, rxs_avg, rxs_logdb, rxs_decimate_rate) if self._options.log: log_to_file(self, rxs_decimate_rate, "data/supply_rx.float") self.rx_baseband = rxs_decimate_rate return rxs_decimate_rate
def main(): parser = OptionParser(conflict_handler="resolve") expert_grp = parser.add_option_group("Expert") add_options(parser, expert_grp) (options, args) = parser.parse_args() fft_length = options.fft_length or 512 file = options.file or "input.compl" out = options.out or "output.compl" src = gr.file_source(gr.sizeof_gr_complex, file) sampler = ofdm.vector_sampler(gr.sizeof_gr_complex, fft_length) trig = gr.vector_source_b([1], True) fft = gr.fft_vcc(fft_length, True, [], True) mag = gr.complex_to_mag(fft_length) avg = gr.single_pole_iir_filter_ff(0.01, fft_length) nlog = gr.nlog10_ff(20, fft_length, -10 * math.log10(fft_length)) dst = gr.file_sink(gr.sizeof_float * fft_length, out) fg = gr.top_block() fg.connect(src, sampler, fft, mag, avg, nlog, dst) fg.connect(trig, (sampler, 1)) # fg.connect(src,limit, # gr.stream_to_vector(gr.sizeof_gr_complex,fft_length), # fft, # gr.multiply_const_vcc([1./fft_length]*fft_length), # gr.complex_to_mag(fft_length), # gr.nlog10_ff(10.0,fft_length), # dst) # fg.connect( src, fft, dst ) fg.run() print "done"
def main(): parser = OptionParser(conflict_handler="resolve") expert_grp = parser.add_option_group("Expert") add_options(parser, expert_grp) (options, args) = parser.parse_args () fft_length = options.fft_length or 512 file = options.file or "input.compl" out = options.out or "output.compl" src = gr.file_source(gr.sizeof_gr_complex,file) sampler = ofdm.vector_sampler( gr.sizeof_gr_complex, fft_length ) trig = gr.vector_source_b([1],True) fft = gr.fft_vcc( fft_length, True, [], True ) mag = gr.complex_to_mag( fft_length ) avg = gr.single_pole_iir_filter_ff(0.01, fft_length) nlog = gr.nlog10_ff( 20, fft_length, -10*math.log10(fft_length) ) dst = gr.file_sink( gr.sizeof_float * fft_length, out ) fg = gr.top_block() fg.connect( src, sampler, fft, mag, avg, nlog, dst ) fg.connect( trig, (sampler,1)) # fg.connect(src,limit, # gr.stream_to_vector(gr.sizeof_gr_complex,fft_length), # fft, # gr.multiply_const_vcc([1./fft_length]*fft_length), # gr.complex_to_mag(fft_length), # gr.nlog10_ff(10.0,fft_length), # dst) # fg.connect( src, fft, dst ) fg.run() print "done"
def __init__(self,subcarriers, frame_length): #config = station_configuration() total_subc = subcarriers vlen = total_subc gr.hier_block2.__init__(self,"ofdm_frame_sampler_grc", gr.io_signature2(2,2,gr.sizeof_gr_complex*vlen, gr.sizeof_char), gr.io_signature2(2,2,gr.sizeof_gr_complex*vlen, gr.sizeof_char)) ft = [0] * 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 = vector_sampler( gr.sizeof_gr_complex * total_subc, frame_length ) symbol_output = blocks.vector_to_stream( gr.sizeof_gr_complex * total_subc, frame_length ) delayed_frame_start = blocks.delay( gr.sizeof_char, frame_length - 1 ) damn_static_frame_trigger = blocks.vector_source_b( ft, True ) self.connect( self, frame_sampler, symbol_output, self ) self.connect( (self,1), delayed_frame_start, ( frame_sampler, 1 ) ) self.connect( damn_static_frame_trigger, (self,1) )
def __init__(self, vlen): gr.hier_block2.__init__(self, "coarse_frequency_offset_estimation", gr.io_signature2(2,2,gr.sizeof_gr_complex,gr.sizeof_char), gr.io_signature (1,1,gr.sizeof_float)) ## Preamble Extraction sampler = vector_sampler(gr.sizeof_gr_complex,vlen) self.connect(self,sampler) self.connect((self,1),(sampler,1)) ## Split block into two parts splitter = gr.vector_to_streams(gr.sizeof_gr_complex*vlen/2,2) self.connect(sampler,splitter) ## Multiply 2nd sub-part with conjugate of 1st sub-part conj_mult = gr.multiply_conjugate_cc(vlen/2) self.connect((splitter,1),(conj_mult,0)) self.connect((splitter,0),(conj_mult,1)) ## Sum of Products psum = vector_sum_vcc(vlen/2) self.connect((conj_mult,0),psum) ## Complex to Angle angle = complex_to_arg() self.connect(psum,angle) ## Normalize norm = gr.multiply_const_ff(1.0/math.pi) self.connect(angle,norm) ## Setup Output Connections self.connect(norm,self)
def __init__(self, fft_length): gr.hier_block2.__init__( self, "foe", gr.io_signature2(2, 2, gr.sizeof_gr_complex, gr.sizeof_char), gr.io_signature(1, 1, gr.sizeof_float)) self.input = (self, 0) self.time_sync = (self, 1) # P(d) self.nominator = schmidl_nominator(fft_length) # sample nominator sampler = vector_sampler(gr.sizeof_gr_complex, 1) self.connect(self.input, self.nominator, (sampler, 0)) self.connect(self.time_sync, (sampler, 1)) # calculate epsilon from P(d), epsilon is normalized fractional frequency offset angle = complex_to_arg() self.epsilon = gr.multiply_const_ff(1.0 / math.pi) self.connect(sampler, angle, self.epsilon, self) try: gr.hier_block.update_var_names(self, "foe", vars()) gr.hier_block.update_var_names(self, "foe", vars(self)) except: pass
def __init__(self, fft_length): gr.hier_block2.__init__(self, "foe", gr.io_signature2(2,2,gr.sizeof_gr_complex,gr.sizeof_char), gr.io_signature(1,1,gr.sizeof_float)) self.input = (self,0) self.time_sync = (self,1) # P(d) self.nominator = schmidl_nominator(fft_length) # sample nominator sampler = vector_sampler(gr.sizeof_gr_complex,1) self.connect(self.input, self.nominator, (sampler,0)) self.connect(self.time_sync, (sampler,1)) # calculate epsilon from P(d), epsilon is normalized fractional frequency offset angle = complex_to_arg() self.epsilon = gr.multiply_const_ff(1.0/math.pi) self.connect(sampler, angle, self.epsilon, self) try: gr.hier_block.update_var_names(self, "foe", vars()) gr.hier_block.update_var_names(self, "foe", vars(self)) except: pass
def __init__(self, vlen): gr.hier_block2.__init__( self, "coarse_frequency_offset_estimation", gr.io_signature2(2, 2, gr.sizeof_gr_complex, gr.sizeof_char), gr.io_signature(1, 1, gr.sizeof_float)) ## Preamble Extraction sampler = vector_sampler(gr.sizeof_gr_complex, vlen) self.connect(self, sampler) self.connect((self, 1), (sampler, 1)) ## Split block into two parts splitter = gr.vector_to_streams(gr.sizeof_gr_complex * vlen / 2, 2) self.connect(sampler, splitter) ## Multiply 2nd sub-part with conjugate of 1st sub-part conj_mult = gr.multiply_conjugate_cc(vlen / 2) self.connect((splitter, 1), (conj_mult, 0)) self.connect((splitter, 0), (conj_mult, 1)) ## Sum of Products psum = vector_sum_vcc(vlen / 2) self.connect((conj_mult, 0), psum) ## Complex to Angle angle = complex_to_arg() self.connect(psum, angle) ## Normalize norm = gr.multiply_const_ff(1.0 / math.pi) self.connect(angle, norm) ## Setup Output Connections self.connect(norm, self)
def supply_rx_baseband(self): ## RX Spectrum if self.__dict__.has_key('rx_baseband'): return self.rx_baseband config = self.config fftlen = config.fft_length my_window = window.hamming(fftlen) #.blackmanharris(fftlen) rxs_sampler = vector_sampler(gr.sizeof_gr_complex,fftlen) rxs_trigger = gr.vector_source_b(concatenate([[1],[0]*199]),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 = gr.single_pole_iir_filter_ff(0.01,fftlen) rxs_logdb = gr.nlog10_ff(20.0,fftlen,-20*log10(fftlen)) rxs_decimate_rate = gr.keep_one_in_n(gr.sizeof_float*fftlen,50) t = self.u if self.filter is None else self.filter self.connect(rxs_trigger,(rxs_sampler,1)) self.connect(t,rxs_sampler,rxs_window, rxs_spectrum,rxs_mag,rxs_avg,rxs_logdb, rxs_decimate_rate) if self._options.log: log_to_file(self, rxs_decimate_rate, "data/supply_rx.float") self.rx_baseband = rxs_decimate_rate return rxs_decimate_rate
def test_007_vector_sampler(self): data = range(1,577,1) # 1..576 trigger = numpy.concatenate([[0]*575,[1]]) blocks = 10000 expected = data[64:577] assert(len(expected)==512) expected = numpy.concatenate([expected*blocks]) assert(len(expected) == 512*blocks) src = gr.vector_source_c(data,True) src2 = gr.vector_source_b(trigger.tolist(),True) dst = gr.vector_sink_c() sampler = ofdm.vector_sampler(gr.sizeof_gr_complex,512) v2s = gr.vector_to_stream(gr.sizeof_gr_complex,512) self.fg.connect(src, (sampler,0)) self.fg.connect(src2,gr.head(gr.sizeof_char,blocks*576), (sampler,1)) self.fg.connect(sampler, v2s, dst) self.fg.run() #self.assertEqual(numpy.array(expected,numpy.Complex), numpy.array(dst.data(), numpy.Complex)) if numpy.array(expected).all() != numpy.array(dst.data()).all(): print "up" print len(expected),len(dst.data()) vec = dst.data() for i in range(min(len(expected),len(dst.data()))): if vec[i] != expected[i]: print "e at ",i
def __init__( self, vlen, cp_len ): gr.hier_block2.__init__( self, "perfect_ofdm_block_sampler", gr.io_signature( 1, 1, gr.sizeof_gr_complex ), gr.io_signature( 1, 1, gr.sizeof_gr_complex * vlen ) ) sampler = ofdm.vector_sampler( gr.sizeof_gr_complex, vlen ) perfect_trigger = perfect_block_trigger( vlen + cp_len ) self.connect( self, sampler, self ) self.connect( perfect_trigger, ( sampler, 1 ) ) self.perfect_trigger = perfect_trigger
def __init__(self,options): config = station_configuration() total_subc = config.subcarriers vlen = total_subc gr.hier_block2.__init__(self,"ofdm_frame_sampler", gr.io_signature2(2,2,gr.sizeof_gr_complex*vlen, gr.sizeof_char), gr.io_signature2(2,2,gr.sizeof_gr_complex*vlen, gr.sizeof_char)) 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 ) 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 ) #oqam_postpro = ofdm.fbmc_oqam_postprocessing_vcvc(total_subc,0,0) if options.enable_erasure_decision: self.frame_gate = vector_sampler( gr.sizeof_gr_complex * total_subc * config.frame_length, 1 ) self.connect( self, frame_sampler, self.frame_gate, symbol_output ) else: self.connect( self, frame_sampler, symbol_output, self ) self.connect( (self,1), delayed_frame_start, ( frame_sampler, 1 ) ) self.connect( damn_static_frame_trigger, (self,1) )
def publish_tm_window(self,unique_id): """ corbaname: ofdm_ti.unique_id """ raise SystemError,"Bad guy! Obey the gnuradio hierarchy ..." config = self.config msgq = gr.msg_queue(10) msg_sink = gr.message_sink(gr.sizeof_float*config.fft_length,msgq,True) sampler = vector_sampler(gr.sizeof_float,config.fft_length) self.connect(self.receiver.timing_metric,(sampler,0)) self.connect(self.receiver.time_sync,delay(gr.sizeof_char,config.fft_length/2-1),(sampler,1)) self.connect(sampler,msg_sink) self.servants.append(corba_data_buffer_servant(str(unique_id),config.fft_length,msgq))
def __init__(self, total_subcarriers, frame_length, frame_data_part, training_data): #config = station_configuration() total_subc = total_subcarriers vlen = total_subc gr.hier_block2.__init__( self, "fbmc_frame_sampler_grc", gr.io_signature2(2, 2, gr.sizeof_gr_complex * vlen, gr.sizeof_char), gr.io_signature2(2, 2, gr.sizeof_gr_complex * vlen, gr.sizeof_char)) frame_size = frame_data_part + training_data.fbmc_no_preambles / 2 print "frame_size: ", frame_size ft = [0] * frame_size 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 = vector_sampler(gr.sizeof_gr_complex * total_subc, frame_size) symbol_output = blocks.vector_to_stream( gr.sizeof_gr_complex * total_subc, frame_size) #delayed_frame_start = blocks.delay( gr.sizeof_char, config.frame_length-1 - config.training_data.fbmc_no_preambles/2 ) delayed_frame_start = blocks.delay(gr.sizeof_char, frame_length / 2 - 1) damn_static_frame_trigger = blocks.vector_source_b(ft, True) #oqam_postpro = ofdm.fbmc_oqam_postprocessing_vcvc(total_subc,0,0) self.connect(self, frame_sampler, symbol_output, self) #self.connect( (self,1), blocks.keep_m_in_n(gr.sizeof_char,frame_data_part + training_data.fbmc_no_preambles/2,2*frame_data_part + training_data.fbmc_no_preambles,0),delayed_frame_start, ( frame_sampler, 1 ) ) self.connect((self, 1), delayed_frame_start, (frame_sampler, 1)) #self.connect( self, blocks.multiply_const_vcc(([1.0]*total_subc)) ,self) #terminate_stream(self,frame_sampler) self.connect(damn_static_frame_trigger, (self, 1))
def test_008(self): vlen = 128 syms = 4 bin1 = vlen / 2 + 4 bin1_val = 1.0 cp_length = vlen / 4 expec = numpy.array(numpy.zeros(vlen), numpy.complex) expec[bin1] = bin1_val expec = concatenate([expec] * syms) epsilon = [-0.5] frame_trigger = numpy.concatenate([[1], [0] * (syms - 1)]) freq_shift = ofdm.frequency_shift_vcc(vlen, 1.0 / vlen, cp_length) fft = gr.fft_vcc(vlen, True, [], True) # natural order, dc = vlen / 2 fft_scale = gr.multiply_const_vcc([1.0 / vlen] * vlen) sampler = ofdm.vector_sampler(gr.sizeof_gr_complex, vlen) trigger_vec = concatenate([[0] * (vlen + cp_length - 1), [1]]) trigger_vec = concatenate([trigger_vec] * syms) trigger = gr.vector_source_b(trigger_vec.tolist()) src = gr.sig_source_c(vlen, gr.GR_COS_WAVE, 4.5, 1.0, 0.0) # bin vlen/2 + 4.5 dst = gr.vector_sink_c() v2s = gr.vector_to_stream(gr.sizeof_gr_complex, vlen) eps = gr.vector_source_f(epsilon) trig = gr.vector_source_b(frame_trigger.tolist()) self.fg.connect(src, (sampler, 0)) self.fg.connect(trigger, (sampler, 1)) self.fg.connect(sampler, (freq_shift, 0)) self.fg.connect(eps, (freq_shift, 1)) self.fg.connect(trig, (freq_shift, 2)) self.fg.connect(freq_shift, fft, fft_scale, v2s, dst) self.fg.run() self.assertComplexTuplesAlmostEqual2(expec, dst.data(), 1e-5, 1e-5)
def __init__(self, fft_length, L): gr.hier_block2.__init__(self, "morelli_foe", gr.io_signature2(2,2,gr.sizeof_gr_complex,gr.sizeof_char), gr.io_signature(1,1,gr.sizeof_float)) data_in = (self,0) trig_in = (self,1) est_out = (self,0) #inp = gr.kludge_copy(gr.sizeof_gr_complex) #self.connect(data_in,inp) inp = data_in sampler = vector_sampler(gr.sizeof_gr_complex,fft_length) self.connect(inp,(sampler,0)) self.connect(trig_in,(sampler,1)) est = mm_frequency_estimator(fft_length,L) self.connect(sampler,est,est_out)
def test_008 (self): vlen = 128 syms = 4 bin1 = vlen/2 + 4 bin1_val = 1.0 cp_length = vlen/4 expec = numpy.array(numpy.zeros(vlen), numpy.complex) expec[bin1] = bin1_val expec = concatenate([expec]*syms) epsilon = [-0.5] frame_trigger = numpy.concatenate([[1],[0]*(syms-1)]) freq_shift = ofdm.frequency_shift_vcc(vlen, 1.0/vlen, cp_length) fft = gr.fft_vcc(vlen, True, [], True) # natural order, dc = vlen / 2 fft_scale = gr.multiply_const_vcc([1.0/vlen]*vlen) sampler = ofdm.vector_sampler(gr.sizeof_gr_complex,vlen) trigger_vec = concatenate([[0]*(vlen+cp_length-1),[1]]) trigger_vec = concatenate([trigger_vec]*syms) trigger = gr.vector_source_b(trigger_vec.tolist()) src = gr.sig_source_c(vlen, gr.GR_COS_WAVE, 4.5, 1.0, 0.0) # bin vlen/2 + 4.5 dst = gr.vector_sink_c() v2s = gr.vector_to_stream(gr.sizeof_gr_complex, vlen) eps = gr.vector_source_f(epsilon) trig = gr.vector_source_b(frame_trigger.tolist()) self.fg.connect(src, (sampler,0)) self.fg.connect(trigger, (sampler,1)) self.fg.connect(sampler, (freq_shift,0)) self.fg.connect(eps, (freq_shift,1)) self.fg.connect(trig, (freq_shift,2)) self.fg.connect(freq_shift, fft, fft_scale, v2s, dst) self.fg.run() self.assertComplexTuplesAlmostEqual2(expec, dst.data(), 1e-5, 1e-5)
def __init__(self,options): config = station_configuration() total_subc = config.subcarriers vlen = total_subc gr.hier_block2.__init__(self,"fbmc_frame_sampler", gr.io_signature2(2,2,gr.sizeof_gr_complex*vlen, gr.sizeof_char), gr.io_signature2(2,2,gr.sizeof_gr_complex*vlen, gr.sizeof_char)) frame_size = config.frame_data_part + config.training_data.fbmc_no_preambles/2 print "frame_size: ", frame_size ft = [0] * frame_size 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, frame_size ) symbol_output = blocks.vector_to_stream( gr.sizeof_gr_complex * total_subc, frame_size ) #delayed_frame_start = blocks.delay( gr.sizeof_char, config.frame_length-1 - config.training_data.fbmc_no_preambles/2 ) delayed_frame_start = blocks.delay( gr.sizeof_char, config.frame_length/2-1) damn_static_frame_trigger = blocks.vector_source_b( ft, True ) #oqam_postpro = ofdm.fbmc_oqam_postprocessing_vcvc(total_subc,0,0) self.connect( self, frame_sampler, symbol_output ,self) #self.connect( (self,1), blocks.keep_m_in_n(gr.sizeof_char,config.frame_data_part + config.training_data.fbmc_no_preambles/2,2*config.frame_data_part + config.training_data.fbmc_no_preambles,0),delayed_frame_start, ( frame_sampler, 1 ) ) self.connect( (self,1), delayed_frame_start, ( frame_sampler, 1 ) ) #self.connect( self, blocks.multiply_const_vcc(([1.0]*total_subc)) ,self) #terminate_stream(self,frame_sampler) self.connect( damn_static_frame_trigger, (self,1) )
def __init__(self, vlen): gr.hier_block2.__init__( self, "snr_estimator", gr.io_signature2(2, 2, gr.sizeof_gr_complex, gr.sizeof_char), gr.io_signature(1, 1, gr.sizeof_float)) data_in = (self, 0) trig_in = (self, 1) snr_out = (self, 0) ## Preamble Extraction sampler = vector_sampler(gr.sizeof_gr_complex, vlen) self.connect(data_in, sampler) self.connect(trig_in, (sampler, 1)) ## Algorithm implementation estim = sc_snr_estimator(vlen) self.connect(sampler, estim) self.connect(estim, snr_out) return ## Split block into two parts splitter = gr.vector_to_streams(gr.sizeof_gr_complex * vlen / 2, 2) self.connect(sampler, splitter) ## Conjugate first half block conj = gr.conjugate_cc(vlen / 2) self.connect(splitter, conj) ## Vector multiplication of both half blocks vmult = gr.multiply_vcc(vlen / 2) self.connect(conj, vmult) self.connect((splitter, 1), (vmult, 1)) ## Sum of Products psum = vector_sum_vcc(vlen / 2) self.connect(vmult, psum) ## Magnitude of P(d) p_mag = gr.complex_to_mag() self.connect(psum, p_mag) ## Squared Magnitude of block r_magsqrd = gr.complex_to_mag_squared(vlen) self.connect(sampler, r_magsqrd) ## Sum of squared second half block r_sum = vector_sum_vff(vlen) self.connect(r_magsqrd, r_sum) ## Square Root of Metric m_sqrt = gr.divide_ff() self.connect(p_mag, (m_sqrt, 0)) self.connect(r_sum, gr.multiply_const_ff(0.5), (m_sqrt, 1)) ## Denominator of SNR estimate denom = gr.add_const_ff(1) neg_m_sqrt = gr.multiply_const_ff(-1.0) self.connect(m_sqrt, limit_vff(1, 1 - 2e-5, -1000), neg_m_sqrt, denom) ## SNR estimate snr_est = gr.divide_ff() self.connect(m_sqrt, (snr_est, 0)) self.connect(denom, (snr_est, 1)) ## Setup Output Connections self.connect(snr_est, self)
def __init__(self): gr.top_block.__init__(self, "Top Block") Qt.QWidget.__init__(self) self.setWindowTitle("Top Block") try: self.setWindowIcon(Qt.QIcon.fromTheme('gnuradio-grc')) except: pass self.top_scroll_layout = Qt.QVBoxLayout() self.setLayout(self.top_scroll_layout) self.top_scroll = Qt.QScrollArea() self.top_scroll.setFrameStyle(Qt.QFrame.NoFrame) self.top_scroll_layout.addWidget(self.top_scroll) self.top_scroll.setWidgetResizable(True) self.top_widget = Qt.QWidget() self.top_scroll.setWidget(self.top_widget) self.top_layout = Qt.QVBoxLayout(self.top_widget) self.top_grid_layout = Qt.QGridLayout() self.top_layout.addLayout(self.top_grid_layout) self.settings = Qt.QSettings("GNU Radio", "top_block") self.restoreGeometry(self.settings.value("geometry").toByteArray()) ################################################## # Variables ################################################## self.used_id_bits = used_id_bits = 8 self.subcarriers = subcarriers = 208 self.id_blocks = id_blocks = 1 self.fft_length = fft_length = 256 self.fbmc = fbmc = 1 self.estimation_preamble = estimation_preamble = 0 self.data_blocks = data_blocks = 10 self.training_data = training_data = default_block_header(subcarriers,fft_length,fbmc,estimation_preamble,[]) self.repeated_id_bits = repeated_id_bits = subcarriers/used_id_bits self.data_part = data_part = data_blocks + id_blocks self.whitener_seed = whitener_seed = seed(1) self.whitener_pn = whitener_pn = [randint(0,1) for i in range(used_id_bits*repeated_id_bits)] self.variable_function_probe_2 = variable_function_probe_2 = 0 self.variable_function_probe_1 = variable_function_probe_1 = 0 self.variable_function_probe_0 = variable_function_probe_0 = 0 self.tx_hostname = tx_hostname = "localhost" self.samp_rate = samp_rate = 4*250000 self.interleaver = interleaver = trellis.interleaver(2000,666) self.frame_length = frame_length = 2*data_part + training_data.fbmc_no_preambles self.filter_length = filter_length = 4 self.disable_freq_sync = disable_freq_sync = 1 self.coding = coding = 1 self.chunkdivisor = chunkdivisor = int(numpy.ceil(data_blocks/5.0)) self.ber_window = ber_window = 100000 self.amplitude = amplitude = 1 self.SNR = SNR = 40 ################################################## # Blocks ################################################## self._amplitude_layout = Qt.QVBoxLayout() self._amplitude_tool_bar = Qt.QToolBar(self) self._amplitude_layout.addWidget(self._amplitude_tool_bar) self._amplitude_tool_bar.addWidget(Qt.QLabel("amplitude"+": ")) class qwt_counter_pyslot(Qwt.QwtCounter): def __init__(self, parent=None): Qwt.QwtCounter.__init__(self, parent) @pyqtSlot('double') def setValue(self, value): super(Qwt.QwtCounter, self).setValue(value) self._amplitude_counter = qwt_counter_pyslot() self._amplitude_counter.setRange(0, 1, 0.02) self._amplitude_counter.setNumButtons(2) self._amplitude_counter.setValue(self.amplitude) self._amplitude_tool_bar.addWidget(self._amplitude_counter) self._amplitude_counter.valueChanged.connect(self.set_amplitude) self._amplitude_slider = Qwt.QwtSlider(None, Qt.Qt.Horizontal, Qwt.QwtSlider.BottomScale, Qwt.QwtSlider.BgSlot) self._amplitude_slider.setRange(0, 1, 0.02) self._amplitude_slider.setValue(self.amplitude) self._amplitude_slider.setMinimumWidth(200) self._amplitude_slider.valueChanged.connect(self.set_amplitude) self._amplitude_layout.addWidget(self._amplitude_slider) self.top_layout.addLayout(self._amplitude_layout) self.tx_rpc_manager_0 = tx_rpc_manager(fft_length, subcarriers, data_blocks, frame_length, 0, 0.0, samp_rate) self.tigr_transmit_control_0 = tigr_transmit_control( subcarriers=subcarriers, fft_length=fft_length, used_id_bits=used_id_bits, estimation_preamble=estimation_preamble, filter_length=filter_length, fbmc=fbmc, data_blocks=data_blocks, data_part=data_part, repeated_id_bits=repeated_id_bits, coding=coding, ) self.tigr_scatterplot_0 = tigr_scatterplot( subcarriers=subcarriers, fbmc=fbmc, fft_length=fft_length, estimation_preamble=estimation_preamble, data_blocks=data_blocks, data_part=11, frame_length=frame_length, ) self.rx_rpc_manager_0 = rx_rpc_manager() self.rms = fbmc_rms_amplifier(amplitude, subcarriers) self.zeromq_pub_sink_1 = zeromq.pub_sink(gr.sizeof_float, subcarriers, "tcp://*:5559", 100) self.zeromq_pub_sink_0 = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5557", 100) def _variable_function_probe_2_probe(): while True: val = self.rx_rpc_manager_0.add_set_scatter_subcarrier_interface(self.tigr_scatterplot_0.ofdm_vector_element_0.set_element) try: self.set_variable_function_probe_2(val) except AttributeError: pass time.sleep(1.0 / (0.000000001)) _variable_function_probe_2_thread = threading.Thread(target=_variable_function_probe_2_probe) _variable_function_probe_2_thread.daemon = True _variable_function_probe_2_thread.start() def _variable_function_probe_1_probe(): while True: val = self.tx_rpc_manager_0.add_tx_modulation_interface(self.tigr_transmit_control_0.ofdm_allocation_src_0.set_allocation) try: self.set_variable_function_probe_1(val) except AttributeError: pass time.sleep(1.0 / (0.000000001)) _variable_function_probe_1_thread = threading.Thread(target=_variable_function_probe_1_probe) _variable_function_probe_1_thread.daemon = True _variable_function_probe_1_thread.start() def _variable_function_probe_0_probe(): while True: val = self.tx_rpc_manager_0.add_tx_ampl_interface(self.rms.set_rms_amplitude) try: self.set_variable_function_probe_0(val) except AttributeError: pass time.sleep(1.0 / (0.000000001)) _variable_function_probe_0_thread = threading.Thread(target=_variable_function_probe_0_probe) _variable_function_probe_0_thread.daemon = True _variable_function_probe_0_thread.start() self.trellis_permutation_0 = trellis.permutation(interleaver.K(), (interleaver.DEINTER()), 1, gr.sizeof_float*1) self.tigr_fbmc_snr_estimator_0 = tigr_fbmc_snr_estimator( subcarriers=subcarriers, fbmc=fbmc, fft_length=fft_length, estimation_preamble=estimation_preamble, frame_length=frame_length, ) self.tigr_fbmc_inner_receiver_0 = tigr_fbmc_inner_receiver( subcarriers=subcarriers, fft_length=fft_length, data_blocks=data_blocks, estimation_preamble=estimation_preamble, filter_length=filter_length, frame_length=frame_length, disable_freq_sync=disable_freq_sync, ) self.tigr_ber_measurement_0 = tigr_ber_measurement( subcarriers=subcarriers, fbmc=fbmc, fft_length=fft_length, estimation_preamble=estimation_preamble, ber_window=ber_window, data_blocks=data_blocks, ) self.single_pole_iir_filter_xx_0 = filter.single_pole_iir_filter_ff(0.1, subcarriers) self.ofdm_viterbi_combined_fb_0 = ofdm.viterbi_combined_fb(ofdm.fsm(ofdm.fsm(1,2,[91,121])), subcarriers, -1, -1, 2, chunkdivisor, ([-1,-1,-1,1,1,-1,1,1]), ofdm.TRELLIS_EUCLIDEAN) self.ofdm_vector_sampler_0 = ofdm.vector_sampler(gr.sizeof_gr_complex*subcarriers, 1) self.ofdm_vector_padding_0 = ofdm.vector_padding(subcarriers, fft_length, -1) self.ofdm_multiply_frame_fc_0 = ofdm.multiply_frame_fc(data_part, subcarriers) self.ofdm_multiply_const_ii_0 = ofdm.multiply_const_ii(1./int(numpy.ceil(data_blocks/5.0))) self.ofdm_generic_softdemapper_vcf_0 = ofdm.generic_softdemapper_vcf(subcarriers, data_part, 1) self.ofdm_fbmc_separate_vcvc_1 = ofdm.fbmc_separate_vcvc(fft_length, 2) self.ofdm_fbmc_polyphase_network_vcvc_1 = ofdm.fbmc_polyphase_network_vcvc(fft_length, filter_length, filter_length*fft_length-1, False) self.ofdm_fbmc_polyphase_network_vcvc_0 = ofdm.fbmc_polyphase_network_vcvc(fft_length, filter_length, filter_length*fft_length-1, False) self.ofdm_fbmc_pilot_block_inserter_0 = fbmc_pilot_block_inserter(subcarriers, data_part, training_data, 5) self.ofdm_fbmc_pilot_block_filter_0 = fbmc_pilot_block_filter(subcarriers, frame_length, data_part, training_data) self.ofdm_fbmc_overlapping_parallel_to_serial_vcc_0 = ofdm.fbmc_overlapping_parallel_to_serial_vcc(fft_length) self.ofdm_fbmc_oqam_preprocessing_vcvc_0 = ofdm.fbmc_oqam_preprocessing_vcvc(subcarriers, 0, 0) self.ofdm_fbmc_frame_sampler_0 = fbmc_frame_sampler(subcarriers, frame_length, data_part, training_data) self.ofdm_fbmc_beta_multiplier_vcvc_0 = ofdm.fbmc_beta_multiplier_vcvc(fft_length, filter_length, fft_length*fft_length-1, 0) self.ofdm_dynamic_trigger_ib_0 = ofdm.dynamic_trigger_ib(0) self.ofdm_depuncture_ff_0 = ofdm.depuncture_ff(subcarriers, 0) self.ofdm_coded_bpsk_soft_decoder_0 = ofdm.coded_bpsk_soft_decoder(subcarriers, used_id_bits, (whitener_pn)) self.ofdm_allocation_buffer_0 = ofdm.allocation_buffer(subcarriers, data_blocks, "tcp://"+tx_hostname+":3333", 1) self.fft_vxx_1 = fft.fft_vcc(fft_length, False, ([]), True, 1) self.channels_channel_model_0 = channels.channel_model( noise_voltage=math.sqrt(1.0*fft_length/subcarriers)*math.sqrt(0.5)*10**(-SNR/20.0), frequency_offset=0.0/fft_length, epsilon=1, taps=((1.0 ), ), noise_seed=0, block_tags=False ) self.blocks_vector_source_x_0 = blocks.vector_source_b([1] + [0]*(data_blocks/2-1), True, 1, []) self.blocks_throttle_0 = blocks.throttle(gr.sizeof_gr_complex*1, samp_rate,True) self.blocks_keep_one_in_n_1 = blocks.keep_one_in_n(gr.sizeof_float*subcarriers, 20) self.blks2_selector_0 = grc_blks2.selector( item_size=gr.sizeof_float*1, num_inputs=2, num_outputs=1, input_index=0, output_index=0, ) ################################################## # Connections ################################################## self.connect((self.ofdm_fbmc_polyphase_network_vcvc_0, 0), (self.ofdm_fbmc_overlapping_parallel_to_serial_vcc_0, 0)) self.connect((self.ofdm_fbmc_polyphase_network_vcvc_1, 0), (self.ofdm_fbmc_overlapping_parallel_to_serial_vcc_0, 1)) self.connect((self.ofdm_fbmc_separate_vcvc_1, 1), (self.ofdm_fbmc_polyphase_network_vcvc_1, 0)) self.connect((self.ofdm_fbmc_oqam_preprocessing_vcvc_0, 0), (self.ofdm_fbmc_pilot_block_inserter_0, 0)) self.connect((self.ofdm_fbmc_pilot_block_inserter_0, 0), (self.ofdm_vector_padding_0, 0)) self.connect((self.ofdm_fbmc_beta_multiplier_vcvc_0, 0), (self.fft_vxx_1, 0)) self.connect((self.fft_vxx_1, 0), (self.ofdm_fbmc_separate_vcvc_1, 0)) self.connect((self.tigr_transmit_control_0, 0), (self.ofdm_fbmc_oqam_preprocessing_vcvc_0, 0)) self.connect((self.single_pole_iir_filter_xx_0, 0), (self.blocks_keep_one_in_n_1, 0)) self.connect((self.ofdm_fbmc_frame_sampler_0, 1), (self.ofdm_fbmc_pilot_block_filter_0, 1)) self.connect((self.ofdm_fbmc_frame_sampler_0, 0), (self.ofdm_fbmc_pilot_block_filter_0, 0)) self.connect((self.ofdm_fbmc_pilot_block_filter_0, 1), (self.ofdm_vector_sampler_0, 1)) self.connect((self.ofdm_vector_sampler_0, 0), (self.ofdm_coded_bpsk_soft_decoder_0, 0)) self.connect((self.ofdm_coded_bpsk_soft_decoder_0, 0), (self.ofdm_allocation_buffer_0, 0)) self.connect((self.ofdm_allocation_buffer_0, 1), (self.ofdm_generic_softdemapper_vcf_0, 1)) self.connect((self.single_pole_iir_filter_xx_0, 0), (self.ofdm_generic_softdemapper_vcf_0, 2)) self.connect((self.ofdm_generic_softdemapper_vcf_0, 0), (self.trellis_permutation_0, 0)) self.connect((self.ofdm_depuncture_ff_0, 0), (self.ofdm_viterbi_combined_fb_0, 0)) self.connect((self.ofdm_allocation_buffer_0, 1), (self.ofdm_depuncture_ff_0, 1)) self.connect((self.blocks_vector_source_x_0, 0), (self.ofdm_depuncture_ff_0, 2)) self.connect((self.ofdm_allocation_buffer_0, 0), (self.ofdm_multiply_const_ii_0, 0)) self.connect((self.ofdm_multiply_const_ii_0, 0), (self.ofdm_viterbi_combined_fb_0, 1)) self.connect((self.ofdm_fbmc_separate_vcvc_1, 0), (self.ofdm_fbmc_polyphase_network_vcvc_0, 0)) self.connect((self.ofdm_viterbi_combined_fb_0, 0), (self.tigr_ber_measurement_0, 2)) self.connect((self.ofdm_dynamic_trigger_ib_0, 0), (self.tigr_ber_measurement_0, 3)) self.connect((self.ofdm_fbmc_frame_sampler_0, 0), (self.tigr_fbmc_snr_estimator_0, 0)) self.connect((self.ofdm_fbmc_frame_sampler_0, 1), (self.tigr_fbmc_snr_estimator_0, 1)) self.connect((self.tigr_fbmc_inner_receiver_0, 1), (self.ofdm_fbmc_frame_sampler_0, 1)) self.connect((self.tigr_fbmc_inner_receiver_0, 2), (self.ofdm_fbmc_frame_sampler_0, 0)) self.connect((self.rms, 0), (self.blocks_throttle_0, 0)) self.connect((self.tigr_fbmc_inner_receiver_0, 3), (self.zeromq_pub_sink_0, 0)) self.connect((self.blocks_keep_one_in_n_1, 0), (self.zeromq_pub_sink_1, 0)) self.connect((self.ofdm_vector_padding_0, 0), (self.ofdm_fbmc_beta_multiplier_vcvc_0, 0)) self.connect((self.tigr_fbmc_inner_receiver_0, 0), (self.single_pole_iir_filter_xx_0, 0)) self.connect((self.ofdm_fbmc_overlapping_parallel_to_serial_vcc_0, 0), (self.rms, 0)) self.connect((self.ofdm_allocation_buffer_0, 0), (self.ofdm_dynamic_trigger_ib_0, 0)) self.connect((self.ofdm_coded_bpsk_soft_decoder_0, 0), (self.tigr_ber_measurement_0, 0)) self.connect((self.ofdm_allocation_buffer_0, 0), (self.tigr_ber_measurement_0, 1)) self.connect((self.blks2_selector_0, 0), (self.ofdm_depuncture_ff_0, 0)) self.connect((self.trellis_permutation_0, 0), (self.blks2_selector_0, 1)) self.connect((self.ofdm_generic_softdemapper_vcf_0, 0), (self.blks2_selector_0, 0)) self.connect((self.blocks_throttle_0, 0), (self.channels_channel_model_0, 0)) self.connect((self.channels_channel_model_0, 0), (self.tigr_fbmc_inner_receiver_0, 0)) self.connect((self.ofdm_fbmc_pilot_block_filter_0, 0), (self.ofdm_vector_sampler_0, 0)) self.connect((self.ofdm_allocation_buffer_0, 2), (self.ofdm_multiply_frame_fc_0, 1)) self.connect((self.ofdm_fbmc_pilot_block_filter_0, 0), (self.ofdm_multiply_frame_fc_0, 0)) self.connect((self.ofdm_multiply_frame_fc_0, 0), (self.tigr_scatterplot_0, 0)) self.connect((self.ofdm_multiply_frame_fc_0, 0), (self.ofdm_generic_softdemapper_vcf_0, 0))
def __init__(self, fft_length, block_length, block_header, range, options): gr.hier_block2.__init__(self, "integer_fo_estimator", gr.io_signature3(3,3,gr.sizeof_gr_complex,gr.sizeof_float,gr.sizeof_char), gr.io_signature2(3,3,gr.sizeof_float,gr.sizeof_char)) raise NotImplementedError,"Obsolete class" self._range = range # threshold after integer part frequency offset estimation # if peak value below threshold, assume false triggering self._thr_lo = 0.4 #0.19 # empirically found threshold. see ioe_metric.float self._thr_hi = 0.4 #0.2 # stuff to be removed after bugfix for hierblock2s self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.time_sync = gr.kludge_copy(gr.sizeof_char) self.epsilon = (self,1) self.connect((self,0),self.input) self.connect((self,2),self.time_sync) delay(gr.sizeof_char, block_header.schmidl_fine_sync[0]*block_length) # sample ofdm symbol (preamble 1 and 2) sampler_symbol1 = vector_sampler(gr.sizeof_gr_complex,fft_length) sampler_symbol2 = vector_sampler(gr.sizeof_gr_complex,fft_length) time_delay1 = delay(gr.sizeof_char,block_length*block_header.schmidl_fine_sync[1]) self.connect(self.input, (sampler_symbol1,0)) self.connect(self.input, (sampler_symbol2,0)) if block_header.schmidl_fine_sync[0] > 0: time_delay0 = delay(gr.sizeof_char,block_length*block_header.schmidl_fine_sync[0]) self.connect(self.time_sync, time_delay0, (sampler_symbol1,1)) else: self.connect(self.time_sync, (sampler_symbol1,1)) self.connect(self.time_sync, time_delay1, (sampler_symbol2,1)) # negative fractional frequency offset estimate epsilon = gr.multiply_const_ff(-1.0) self.connect(self.epsilon, epsilon) # compensate for fractional frequency offset on per symbol base # freq_shift: vector length, modulator sensitivity # freq_shift third input: reset phase accumulator # symbol/preamble 1 freq_shift_sym1 = frequency_shift_vcc(fft_length, 1.0/fft_length) self.connect(sampler_symbol1, (freq_shift_sym1,0)) self.connect(epsilon, (freq_shift_sym1,1)) self.connect(gr.vector_source_b([1], True), (freq_shift_sym1,2)) # symbol/preamble 2 freq_shift_sym2 = frequency_shift_vcc(fft_length, 1.0/fft_length) self.connect(sampler_symbol2, (freq_shift_sym2,0)) self.connect(epsilon, (freq_shift_sym2,1)) self.connect(gr.vector_source_b([1], True), (freq_shift_sym2,2)) # fourier transfrom on both preambles fft_sym1 = gr.fft_vcc(fft_length, True, [], True) # Forward + Blockshift fft_sym2 = gr.fft_vcc(fft_length, True, [], True) # Forward + Blockshift # calculate schmidl's metric for estimation of freq. offset's integer part assert(hasattr(block_header, "schmidl_fine_sync")) pre1 = block_header.pilotsym_fd[block_header.schmidl_fine_sync[0]] pre2 = block_header.pilotsym_fd[block_header.schmidl_fine_sync[1]] diff_pn = concatenate([[conjugate(math.sqrt(2)*pre2[2*i]/pre1[2*i]),0.0j] for i in arange(len(pre1)/2)]) cfo_estimator = schmidl_cfo_estimator(fft_length, len(pre1), self._range, diff_pn) self.connect(freq_shift_sym1, fft_sym1, (cfo_estimator,0)) # preamble 1 self.connect(freq_shift_sym2, fft_sym2, (cfo_estimator,1)) # preamble 2 # search for maximum and its argument in interval [-range .. +range] #arg_max = arg_max_vff(2*self._range + 1) arg_max_s = gr.argmax_fs(2*self._range+1) arg_max = gr.short_to_float() ifo_max = gr.max_ff(2*self._range + 1) # vlen ifo_estimate = gr.add_const_ff(-self._range) self.connect(cfo_estimator, arg_max_s, arg_max, ifo_estimate) self.connect(cfo_estimator, ifo_max) self.connect((arg_max_s,1),gr.null_sink(gr.sizeof_short)) # threshold maximal value ifo_threshold = gr.threshold_ff(self._thr_lo, self._thr_hi, 0.0) ifo_thr_f2b = gr.float_to_char() self.connect(ifo_max, ifo_threshold, ifo_thr_f2b) # gating the streams ifo_estimate (integer part) and epsilon (frac. part) # if the metric's peak value was above the chosen threshold, assume to have # found a new burst. peak value below threshold results in blocking the # streams self.gate = gate_ff() self.connect(ifo_thr_f2b, (self.gate,0)) # threshold stream self.connect(ifo_estimate, (self.gate,1)) self.connect(epsilon, (self.gate,2)) # peak filtering # resynchronize and suppress peaks that didn't match a preamble filtered_time_sync = peak_resync_bb(True) # replace self.connect(self.time_sync, (filtered_time_sync,0)) self.connect(ifo_thr_f2b, (filtered_time_sync,1)) # find complete estimation for frequency offset # add together fractional and integer part freq_offset = gr.add_ff() self.connect((self.gate,1), gr.multiply_const_ff(-1.0), (freq_offset,0)) # integer offset self.connect((self.gate,2), (freq_offset,1)) # frac offset # output connections self.connect(freq_offset, (self,0)) self.connect(filtered_time_sync, (self,1)) self.connect((self.gate,0), (self,2)) # used for frame trigger ######################################### # debugging if options.log: self.epsilon2_sink = gr.vector_sink_f() self.connect(epsilon, self.epsilon2_sink) self.connect(cfo_estimator, gr.file_sink(gr.sizeof_float*(self._range*2+1), "data/ioe_metric.float")) # output joint stream preamble_stream = gr.streams_to_vector(fft_length * gr.sizeof_gr_complex, 2) self.connect(fft_sym1, (preamble_stream,0)) self.connect(fft_sym2, (preamble_stream,1)) self.connect(preamble_stream, gr.file_sink(gr.sizeof_gr_complex * 2 * fft_length, "data/preambles.compl")) # output, preambles before and after correction, magnitude and complex spectrum self.connect(sampler_symbol1, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre1_bef.compl")) self.connect(sampler_symbol1, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre1_bef.float")) self.connect(sampler_symbol2, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre2_bef.compl")) self.connect(sampler_symbol2, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre2_bef.float")) self.connect(freq_shift_sym1, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length,"data/pre1.compl")) self.connect(freq_shift_sym1, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length,"data/pre1.float")) self.connect(freq_shift_sym2, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length,"data/pre2.compl")) self.connect(freq_shift_sym2, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length,"data/pre2.float")) # calculate epsilon from corrected source to check function test_cp = cyclic_prefixer(fft_length, block_length) test_eps = foe(fft_length) self.connect(freq_shift_sym1, test_cp, test_eps, gr.file_sink(gr.sizeof_float, "data/eps_after.float")) try: gr.hier_block.update_var_names(self, "ifo_estimator", vars()) gr.hier_block.update_var_names(self, "ifo_estimator", vars(self)) except: pass
def __init__(self, fft_length, block_length, block_header, range, options): gr.hier_block2.__init__( self, "integer_fo_estimator", gr.io_signature3(3, 3, gr.sizeof_gr_complex, gr.sizeof_float, gr.sizeof_char), gr.io_signature2(3, 3, gr.sizeof_float, gr.sizeof_char)) raise NotImplementedError, "Obsolete class" self._range = range # threshold after integer part frequency offset estimation # if peak value below threshold, assume false triggering self._thr_lo = 0.4 #0.19 # empirically found threshold. see ioe_metric.float self._thr_hi = 0.4 #0.2 # stuff to be removed after bugfix for hierblock2s self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.time_sync = gr.kludge_copy(gr.sizeof_char) self.epsilon = (self, 1) self.connect((self, 0), self.input) self.connect((self, 2), self.time_sync) delay(gr.sizeof_char, block_header.schmidl_fine_sync[0] * block_length) # sample ofdm symbol (preamble 1 and 2) sampler_symbol1 = vector_sampler(gr.sizeof_gr_complex, fft_length) sampler_symbol2 = vector_sampler(gr.sizeof_gr_complex, fft_length) time_delay1 = delay(gr.sizeof_char, block_length * block_header.schmidl_fine_sync[1]) self.connect(self.input, (sampler_symbol1, 0)) self.connect(self.input, (sampler_symbol2, 0)) if block_header.schmidl_fine_sync[0] > 0: time_delay0 = delay( gr.sizeof_char, block_length * block_header.schmidl_fine_sync[0]) self.connect(self.time_sync, time_delay0, (sampler_symbol1, 1)) else: self.connect(self.time_sync, (sampler_symbol1, 1)) self.connect(self.time_sync, time_delay1, (sampler_symbol2, 1)) # negative fractional frequency offset estimate epsilon = gr.multiply_const_ff(-1.0) self.connect(self.epsilon, epsilon) # compensate for fractional frequency offset on per symbol base # freq_shift: vector length, modulator sensitivity # freq_shift third input: reset phase accumulator # symbol/preamble 1 freq_shift_sym1 = frequency_shift_vcc(fft_length, 1.0 / fft_length) self.connect(sampler_symbol1, (freq_shift_sym1, 0)) self.connect(epsilon, (freq_shift_sym1, 1)) self.connect(gr.vector_source_b([1], True), (freq_shift_sym1, 2)) # symbol/preamble 2 freq_shift_sym2 = frequency_shift_vcc(fft_length, 1.0 / fft_length) self.connect(sampler_symbol2, (freq_shift_sym2, 0)) self.connect(epsilon, (freq_shift_sym2, 1)) self.connect(gr.vector_source_b([1], True), (freq_shift_sym2, 2)) # fourier transfrom on both preambles fft_sym1 = gr.fft_vcc(fft_length, True, [], True) # Forward + Blockshift fft_sym2 = gr.fft_vcc(fft_length, True, [], True) # Forward + Blockshift # calculate schmidl's metric for estimation of freq. offset's integer part assert (hasattr(block_header, "schmidl_fine_sync")) pre1 = block_header.pilotsym_fd[block_header.schmidl_fine_sync[0]] pre2 = block_header.pilotsym_fd[block_header.schmidl_fine_sync[1]] diff_pn = concatenate( [[conjugate(math.sqrt(2) * pre2[2 * i] / pre1[2 * i]), 0.0j] for i in arange(len(pre1) / 2)]) cfo_estimator = schmidl_cfo_estimator(fft_length, len(pre1), self._range, diff_pn) self.connect(freq_shift_sym1, fft_sym1, (cfo_estimator, 0)) # preamble 1 self.connect(freq_shift_sym2, fft_sym2, (cfo_estimator, 1)) # preamble 2 # search for maximum and its argument in interval [-range .. +range] #arg_max = arg_max_vff(2*self._range + 1) arg_max_s = gr.argmax_fs(2 * self._range + 1) arg_max = gr.short_to_float() ifo_max = gr.max_ff(2 * self._range + 1) # vlen ifo_estimate = gr.add_const_ff(-self._range) self.connect(cfo_estimator, arg_max_s, arg_max, ifo_estimate) self.connect(cfo_estimator, ifo_max) self.connect((arg_max_s, 1), gr.null_sink(gr.sizeof_short)) # threshold maximal value ifo_threshold = gr.threshold_ff(self._thr_lo, self._thr_hi, 0.0) ifo_thr_f2b = gr.float_to_char() self.connect(ifo_max, ifo_threshold, ifo_thr_f2b) # gating the streams ifo_estimate (integer part) and epsilon (frac. part) # if the metric's peak value was above the chosen threshold, assume to have # found a new burst. peak value below threshold results in blocking the # streams self.gate = gate_ff() self.connect(ifo_thr_f2b, (self.gate, 0)) # threshold stream self.connect(ifo_estimate, (self.gate, 1)) self.connect(epsilon, (self.gate, 2)) # peak filtering # resynchronize and suppress peaks that didn't match a preamble filtered_time_sync = peak_resync_bb(True) # replace self.connect(self.time_sync, (filtered_time_sync, 0)) self.connect(ifo_thr_f2b, (filtered_time_sync, 1)) # find complete estimation for frequency offset # add together fractional and integer part freq_offset = gr.add_ff() self.connect((self.gate, 1), gr.multiply_const_ff(-1.0), (freq_offset, 0)) # integer offset self.connect((self.gate, 2), (freq_offset, 1)) # frac offset # output connections self.connect(freq_offset, (self, 0)) self.connect(filtered_time_sync, (self, 1)) self.connect((self.gate, 0), (self, 2)) # used for frame trigger ######################################### # debugging if options.log: self.epsilon2_sink = gr.vector_sink_f() self.connect(epsilon, self.epsilon2_sink) self.connect( cfo_estimator, gr.file_sink(gr.sizeof_float * (self._range * 2 + 1), "data/ioe_metric.float")) # output joint stream preamble_stream = gr.streams_to_vector( fft_length * gr.sizeof_gr_complex, 2) self.connect(fft_sym1, (preamble_stream, 0)) self.connect(fft_sym2, (preamble_stream, 1)) self.connect( preamble_stream, gr.file_sink(gr.sizeof_gr_complex * 2 * fft_length, "data/preambles.compl")) # output, preambles before and after correction, magnitude and complex spectrum self.connect( sampler_symbol1, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre1_bef.compl")) self.connect( sampler_symbol1, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre1_bef.float")) self.connect( sampler_symbol2, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre2_bef.compl")) self.connect( sampler_symbol2, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre2_bef.float")) self.connect( freq_shift_sym1, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre1.compl")) self.connect( freq_shift_sym1, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre1.float")) self.connect( freq_shift_sym2, gr.fft_vcc(fft_length, True, [], True), gr.file_sink(gr.sizeof_gr_complex * fft_length, "data/pre2.compl")) self.connect( freq_shift_sym2, gr.fft_vcc(fft_length, True, [], True), gr.complex_to_mag(fft_length), gr.file_sink(gr.sizeof_float * fft_length, "data/pre2.float")) # calculate epsilon from corrected source to check function test_cp = cyclic_prefixer(fft_length, block_length) test_eps = foe(fft_length) self.connect(freq_shift_sym1, test_cp, test_eps, gr.file_sink(gr.sizeof_float, "data/eps_after.float")) try: gr.hier_block.update_var_names(self, "ifo_estimator", vars()) gr.hier_block.update_var_names(self, "ifo_estimator", vars(self)) except: pass
def __init__(self, vlen): gr.hier_block2.__init__(self, "snr_estimator", gr.io_signature2(2,2,gr.sizeof_gr_complex,gr.sizeof_char), gr.io_signature (1,1,gr.sizeof_float)) data_in = (self,0) trig_in = (self,1) snr_out = (self,0) ## Preamble Extraction sampler = vector_sampler(gr.sizeof_gr_complex,vlen) self.connect(data_in,sampler) self.connect(trig_in,(sampler,1)) ## Algorithm implementation estim = sc_snr_estimator(vlen) self.connect(sampler,estim) self.connect(estim,snr_out) return ## Split block into two parts splitter = gr.vector_to_streams(gr.sizeof_gr_complex*vlen/2,2) self.connect(sampler,splitter) ## Conjugate first half block conj = gr.conjugate_cc(vlen/2) self.connect(splitter,conj) ## Vector multiplication of both half blocks vmult = gr.multiply_vcc(vlen/2) self.connect(conj,vmult) self.connect((splitter,1),(vmult,1)) ## Sum of Products psum = vector_sum_vcc(vlen/2) self.connect(vmult,psum) ## Magnitude of P(d) p_mag = gr.complex_to_mag() self.connect(psum,p_mag) ## Squared Magnitude of block r_magsqrd = gr.complex_to_mag_squared(vlen) self.connect(sampler,r_magsqrd) ## Sum of squared second half block r_sum = vector_sum_vff(vlen) self.connect(r_magsqrd,r_sum) ## Square Root of Metric m_sqrt = gr.divide_ff() self.connect(p_mag,(m_sqrt,0)) self.connect(r_sum,gr.multiply_const_ff(0.5),(m_sqrt,1)) ## Denominator of SNR estimate denom = gr.add_const_ff(1) neg_m_sqrt = gr.multiply_const_ff(-1.0) self.connect(m_sqrt,limit_vff(1,1-2e-5,-1000),neg_m_sqrt,denom) ## SNR estimate snr_est = gr.divide_ff() self.connect(m_sqrt,(snr_est,0)) self.connect(denom,(snr_est,1)) ## Setup Output Connections self.connect(snr_est,self)
def __init__(self, options, log=False): ## Read configuration config = station_configuration() fft_length = config.fft_length #cp_length = config.cp_length block_header = config.training_data data_subc = config.data_subcarriers virtual_subc = config.virtual_subcarriers total_subc = config.subcarriers block_length = config.block_length frame_length = config.frame_length L = block_header.mm_periodic_parts cp_length = config.cp_length print "data_subc: ", config.data_subcarriers print "total_subc: ", config.subcarriers print "frame_lengthframe_length: ", frame_length ## Set Input/Output signature gr.hier_block2.__init__( self, "fbmc_inner_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signaturev( 4, 4, [ gr.sizeof_float * total_subc, # Normalized |CTF|^2 gr.sizeof_char, # Frame start gr.sizeof_gr_complex * total_subc, # OFDM blocks, SNR est gr.sizeof_float ])) # CFO ## Input and output ports self.input = rx_input = self out_ofdm_blocks = (self, 2) out_frame_start = (self, 1) out_disp_ctf = (self, 0) out_disp_cfo = (self, 3) #out_snr_pream = ( self, 3 ) ## pre-FFT processing ''' ## Compute autocorrelations for S&C preamble ## and cyclic prefix self._sc_metric = sc_metric = autocorrelator( fft_length/2, fft_length/2 ) self._gi_metric = gi_metric = autocorrelator( fft_length, cp_length ) self.connect( rx_input, sc_metric ) self.connect( rx_input, gi_metric ) terminate_stream(self, gi_metric) ## Sync. Output contains OFDM blocks sync = ofdm.time_sync( fft_length/2, 1) self.connect( rx_input, ( sync, 0 ) ) self.connect( sc_metric, ( sync, 1 ) ) self.connect( sc_metric, ( sync, 2 ) ) ofdm_blocks = ( sync, 0 ) frame_start = ( sync, 1 ) log_to_file( self, ( sync, 1 ), "data/fbmc_peak_detector.char" ) ''' if options.ideal is False and options.ideal2 is False: #Testing old/new metric self.tm = schmidl.recursive_timing_metric(2 * fft_length) self.connect(self.input, self.tm) #log_to_file( self, self.tm, "data/fbmc_rec_sc_metric_ofdm.float" ) timingmetric_shift = 0 #-2 #int(-cp_length * 0.8) tmfilter = filter.fft_filter_fff( 1, [2. / fft_length] * (fft_length / 2) ) # ofdm.lms_fir_ff( fft_length, 1e-3 ) #; filter.fir_filter_fff(1, [1./fft_length]*fft_length) self.connect(self.tm, tmfilter) self.tm = tmfilter #log_to_file( self, self.tm, "data/fbmc_rec_sc_metric_ofdm2.float" ) self._pd_thres = 0.6 self._pd_lookahead = fft_length # empirically chosen peak_detector = ofdm.peak_detector_02_fb(self._pd_lookahead, self._pd_thres) self.connect(self.tm, peak_detector) #log_to_file( self, peak_detector, "data/fbmc_rec_peak_detector.char" ) #frame_start = [0]*frame_length #frame_start[0] = 1 #frame_start = blocks.vector_source_b(frame_start,True) #OLD #delayed_timesync = blocks.delay(gr.sizeof_char, # (frame_length-10)*fft_length/2 - fft_length/4 -1 + timingmetric_shift) delayed_timesync = blocks.delay( gr.sizeof_char, (frame_length - 10) * fft_length / 2 - fft_length / 4 + int(2.5 * fft_length) + timingmetric_shift - 1) #delayed_timesync = blocks.delay(gr.sizeof_char, #(frame_length-10)*fft_length/2 - fft_length/4 + int(3.5*fft_length) + timingmetric_shift-1) self.connect(peak_detector, delayed_timesync) self.block_sampler = ofdm.vector_sampler( gr.sizeof_gr_complex, fft_length / 2 * frame_length) self.connect(self.input, self.block_sampler) self.connect(delayed_timesync, (self.block_sampler, 1)) #log_to_file( self, self.block_sampler, "data/fbmc_block_sampler.compl" ) vt2s = blocks.vector_to_stream(gr.sizeof_gr_complex * fft_length, frame_length / 2) self.connect(self.block_sampler, vt2s) #terminate_stream(self,ofdm_blocks) ofdm_blocks = vt2s ''' # TODO: dynamic solution vt2s = blocks.vector_to_stream(gr.sizeof_gr_complex*block_length/2, frame_length) self.connect(self.block_sampler,vt2s) terminate_stream(self,( sync, 0 )) ofdm_blocks = vt2s ''' ##stv_help = blocks.stream_to_vector(gr.sizeof_gr_complex*config.fft_length/2, 1) #stv_help = blocks.vector_to_stream(gr.sizeof_gr_complex*config.fft_length/2, 2) ##self.connect(ofdm_blocks, stv_help) ##ofdm_blocks = stv_help #ofdm_blocks = ( sync, 0 ) #frame_start = ( sync, 1 ) #log_to_file(self, frame_start, "data/frame_start.compl") #log_to_file( self, sc_metric, "data/sc_metric.float" ) #log_to_file( self, gi_metric, "data/gi_metric.float" ) #log_to_file( self, (sync,1), "data/sync.float" ) # log_to_file(self,ofdm_blocks,"data/ofdm_blocks_original.compl") frame_start = [0] * int(frame_length / 2) frame_start[0] = 1 frame_start = blocks.vector_source_b(frame_start, True) #frame_start2 = [0]*int(frame_length/2) #frame_start2[0] = 1 #frame_start2 = blocks.vector_source_b(frame_start2,True) if options.disable_time_sync or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, ofdm_blocks) terminate_stream(self, frame_start) serial_to_parallel = blocks.stream_to_vector( gr.sizeof_gr_complex, fft_length) #discard_cp = ofdm.vector_mask(block_length,cp_length,fft_length,[]) #serial_to_parallel = blocks.stream_to_vector(gr.sizeof_gr_complex,block_length) #discard_cp = ofdm.vector_mask(block_length,cp_length,fft_length,[]) #self.connect( rx_input,serial_to_parallel) #self.connect( rx_input, blocks.delay(gr.sizeof_gr_complex,0),serial_to_parallel) initial_skip = blocks.skiphead(gr.sizeof_gr_complex, 2 * fft_length) self.connect(rx_input, initial_skip) if options.ideal is False and options.ideal2 is False: self.connect(initial_skip, serial_to_parallel) ofdm_blocks = serial_to_parallel else: ofdm_blocks = initial_skip #self.connect( rx_input, serial_to_parallel, discard_cp ) frame_start = [0] * int(frame_length / 2) frame_start[0] = 1 frame_start = blocks.vector_source_b(frame_start, True) #frame_start2 = [0]*int(frame_length/2) #frame_start2[0] = 1 #frame_start2 = blocks.vector_source_b(frame_start2,True) print "Disabled time synchronization stage" print "\t\t\t\t\tframe_length = ", frame_length if options.ideal is False and options.ideal2 is False: ## Extract preamble, feed to Morelli & Mengali frequency offset estimator assert (block_header.mm_preamble_pos == 0) morelli_foe = ofdm.mm_frequency_estimator(fft_length, 2, 1, config.fbmc) sampler_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length, 1) self.connect(ofdm_blocks, (sampler_preamble, 0)) self.connect(frame_start, blocks.delay(gr.sizeof_char, 1), (sampler_preamble, 1)) self.connect(sampler_preamble, morelli_foe) freq_offset = morelli_foe print "FRAME_LENGTH: ", frame_length #log_to_file( self, sampler_preamble, "data/sampler_preamble.compl" ) #log_to_file( self, rx_input, "data/rx_input.compl" ) #log_to_file( self, ofdm_blocks, "data/rx_input.compl" ) #Extracting preamble for SNR estimation #fft_snr_est = fft_blocks.fft_vcc( fft_length, True, [], True ) #self.connect( sampler_preamble, blocks.stream_to_vector(gr.sizeof_gr_complex*fft_length/2, 2), fft_snr_est ) ## Remove virtual subcarriers #if fft_length > data_subc: #subcarrier_mask_snr_est = ofdm.vector_mask( fft_length, virtual_subc/2, # total_subc, [] ) #self.connect( fft_snr_est, subcarrier_mask_snr_est ) #fft_snr_est = subcarrier_mask_snr_est #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") ## Least Squares estimator for channel transfer function (CTF) #self.connect( fft_snr_est, out_snr_pream ) # Connecting to output ## Adaptive LMS FIR filtering of frequency offset lms_fir = ofdm.lms_fir_ff(20, 1e-3) # TODO: verify parameter choice self.connect(freq_offset, lms_fir) freq_offset = lms_fir self.connect(freq_offset, blocks.keep_one_in_n(gr.sizeof_float, 20), out_disp_cfo) else: self.connect(blocks.vector_source_f([1]), out_disp_cfo) #log_to_file(self, lms_fir, "data/lms_fir.float") if options.disable_freq_sync or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, freq_offset) freq_offset = blocks.vector_source_f([0.0], True) print "Disabled frequency synchronization stage" if options.ideal is False and options.ideal2 is False: ## Correct frequency shift, feed-forward structure frequency_shift = ofdm.frequency_shift_vcc(fft_length, -1.0 / fft_length, 0) #freq_shift = blocks.multiply_cc() #norm_freq = -0.1 / config.fft_length #freq_off = self.freq_off_src = analog.sig_source_c(1.0, analog.GR_SIN_WAVE, norm_freq, 1.0, 0.0 ) self.connect(ofdm_blocks, (frequency_shift, 0)) self.connect(freq_offset, (frequency_shift, 1)) self.connect(frame_start, blocks.delay(gr.sizeof_char, 0), (frequency_shift, 2)) #self.connect(frequency_shift,s2help) #ofdm_blocks = s2help ofdm_blocks = frequency_shift #terminate_stream(self, frequency_shift) #inner_pb_filt = self._inner_pilot_block_filter = fbmc_inner_pilot_block_filter() #self.connect(ofdm_blocks,inner_pb_filt) #self.connect(frame_start,(inner_pb_filt,1)) #self.connect((inner_pb_filt,1),blocks.null_sink(gr.sizeof_char)) #ofdm_blocks = (inner_pb_filt,0) overlap_ser_to_par = ofdm.fbmc_overlapping_serial_to_parallel_cvc( fft_length) self.separate_vcvc = ofdm.fbmc_separate_vcvc(fft_length, 2) self.polyphase_network_vcvc_1 = ofdm.fbmc_polyphase_network_vcvc( fft_length, 4, 4 * fft_length - 1, True) self.polyphase_network_vcvc_2 = ofdm.fbmc_polyphase_network_vcvc( fft_length, 4, 4 * fft_length - 1, True) self.junction_vcvc = ofdm.fbmc_junction_vcvc(fft_length, 2) self.fft_fbmc = fft_blocks.fft_vcc(fft_length, True, [], True) print "config.training_data.fbmc_no_preambles: ", config.training_data.fbmc_no_preambles #center_preamble = [1, -1j, -1, 1j] #self.preamble = preamble = [0]*total_subc + center_preamble*((int)(total_subc/len(center_preamble)))+[0]*total_subc self.preamble = preamble = config.training_data.fbmc_pilotsym_fd_list #inv_preamble = 1. / numpy.array(self.preamble) #print "self.preamble: ", len(self.preamble #print "inv_preamble: ", list(inv_preamble) #print "self.preamble", self.preamble #print "self.preamble2", self.preamble2 self.multiply_const = ofdm.multiply_const_vcc( ([1.0 / (math.sqrt(fft_length * 0.6863))] * total_subc)) self.beta_multiplier_vcvc = ofdm.fbmc_beta_multiplier_vcvc( total_subc, 4, 4 * fft_length - 1, 0) #self.skiphead = blocks.skiphead(gr.sizeof_gr_complex*total_subc, 2*4-1-1) self.skiphead = blocks.skiphead(gr.sizeof_gr_complex * total_subc, 2) self.skiphead_1 = blocks.skiphead(gr.sizeof_gr_complex * total_subc, 0) #self.remove_preamble_vcvc = ofdm.fbmc_remove_preamble_vcvc(total_subc, config.frame_data_part/2, config.training_data.fbmc_no_preambles*total_subc/2) #self.subchannel_processing_vcvc = ofdm.fbmc_subchannel_processing_vcvc(total_subc, config.frame_data_part, 1, 2, 1, 0) self.oqam_postprocessing_vcvc = ofdm.fbmc_oqam_postprocessing_vcvc( total_subc, 0, 0) #log_to_file( self, ofdm_blocks, "data/PRE_FBMC.compl" ) #log_to_file( self, self.fft_fbmc, "data/FFT_FBMC.compl" ) if options.ideal is False and options.ideal2 is False: self.subchannel_processing_vcvc = ofdm.fbmc_subchannel_processing_vcvc( total_subc, config.frame_data_part, 3, 2, 1, 0) help2 = blocks.keep_one_in_n(gr.sizeof_gr_complex * total_subc, frame_length) self.connect((self.subchannel_processing_vcvc, 1), help2) #log_to_file( self, self.subchannel_processing_vcvc, "data/fbmc_subc.compl" ) #terminate_stream(self, help2) if options.ideal is False and options.ideal2 is False: self.connect( ofdm_blocks, blocks.vector_to_stream(gr.sizeof_gr_complex, fft_length), overlap_ser_to_par) else: self.connect(ofdm_blocks, overlap_ser_to_par) self.connect(overlap_ser_to_par, self.separate_vcvc) self.connect((self.separate_vcvc, 1), (self.polyphase_network_vcvc_2, 0)) self.connect((self.separate_vcvc, 0), (self.polyphase_network_vcvc_1, 0)) self.connect((self.polyphase_network_vcvc_1, 0), (self.junction_vcvc, 0)) self.connect((self.polyphase_network_vcvc_2, 0), (self.junction_vcvc, 1)) self.connect(self.junction_vcvc, self.fft_fbmc) ofdm_blocks = self.fft_fbmc print "config.dc_null: ", config.dc_null if fft_length > data_subc: subcarrier_mask_fbmc = ofdm.vector_mask_dc_null( fft_length, virtual_subc / 2, total_subc, config.dc_null, []) self.connect(ofdm_blocks, subcarrier_mask_fbmc) ofdm_blocks = subcarrier_mask_fbmc #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") ## Least Squares estimator for channel transfer function (CTF) #log_to_file( self, subcarrier_mask, "data/OFDM_Blocks.compl" ) self.connect(ofdm_blocks, self.beta_multiplier_vcvc) ofdm_blocks = self.beta_multiplier_vcvc #self.connect(ofdm_blocks,self.multiply_const) #self.connect(self.multiply_const, (self.skiphead, 0)) self.connect(ofdm_blocks, (self.skiphead, 0)) #log_to_file( self, self.skiphead, "data/fbmc_skiphead_4.compl" ) #self.connect(ofdm_blocks, self.multiply_const) #self.connect(self.multiply_const, self.beta_multiplier_vcvc) #self.connect((self.beta_multiplier_vcvc, 0), (self.skiphead, 0)) if options.ideal or options.ideal2: self.connect((self.skiphead, 0), (self.skiphead_1, 0)) else: self.connect((self.skiphead, 0), (self.subchannel_processing_vcvc, 0)) self.connect((self.subchannel_processing_vcvc, 0), (self.skiphead_1, 0)) #log_to_file( self, self.skiphead, "data/fbmc_subc.compl" ) #self.connect((self.skiphead_1, 0),(self.remove_preamble_vcvc, 0)) #self.connect((self.remove_preamble_vcvc, 0), (self.oqam_postprocessing_vcvc, 0)) #ofdm_blocks = self.oqam_postprocessing_vcvc #log_to_file( self, self.subchannel_processing_vcvc, "data/subc_0.compl" ) #log_to_file( self, (self.subchannel_processing_vcvc,1), "data/subc_1.compl" ) self.connect((self.skiphead_1, 0), (self.oqam_postprocessing_vcvc, 0)) #self.connect((self.oqam_postprocessing_vcvc, 0), (self.remove_preamble_vcvc, 0) ) ofdm_blocks = (self.oqam_postprocessing_vcvc, 0 ) #(self.remove_preamble_vcvc, 0) #log_to_file( self, (self.oqam_postprocessing_vcvc, 0), "data/fbmc_before_remove.compl" ) #log_to_file( self, self.skiphead, "data/SKIP_HEAD_FBMC.compl" ) #log_to_file( self, self.beta_multiplier_vcvc, "data/BETA_REC_FBMC.compl" ) #log_to_file( self, self.oqam_postprocessing_vcvc, "data/REC_OUT_FBMC.compl" ) """ DISABLED OFDM CHANNEL ESTIMATION PREMBLE -> CORRECT LATER to compare FBMC and OFDM channel estimation #TAKING THE CHANNEL ESTIMATION PREAMBLE chest_pre_trigger = blocks.delay( gr.sizeof_char, 3 ) sampled_chest_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length/2, 2 ) self.connect( frame_start, chest_pre_trigger ) self.connect( chest_pre_trigger, ( sampled_chest_preamble, 1 ) ) self.connect( frequency_shift, ( sampled_chest_preamble, 0 ) ) #ofdm_blocks = sampled_chest_preamble ## FFT fft = fft_blocks.fft_vcc( fft_length, True, [], True ) self.connect( sampled_chest_preamble, fft ) ofdm_blocks_est = fft log_to_file( self, sampled_chest_preamble, "data/SAMPLED_EST_PREAMBLE.compl" ) log_to_file( self, ofdm_blocks_est, "data/FFT.compl" ) ## Remove virtual subcarriers if fft_length > data_subc: subcarrier_mask = ofdm.vector_mask( fft_length, virtual_subc/2, total_subc, [] ) self.connect( ofdm_blocks_est, subcarrier_mask ) ofdm_blocks_est = subcarrier_mask #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") ## Least Squares estimator for channel transfer function (CTF) log_to_file( self, subcarrier_mask, "data/OFDM_Blocks.compl" ) ## post-FFT processing ## extract channel estimation preamble from frame ##chest_pre_trigger = blocks.delay( gr.sizeof_char, ##1 ) ##sampled_chest_preamble = \ ## ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) ##self.connect( frame_start, chest_pre_trigger ) ##self.connect( chest_pre_trigger, ( sampled_chest_preamble, 1 ) ) ##self.connect( ofdm_blocks, ( sampled_chest_preamble, 0 ) ) ## Least Squares estimator for channel transfer function (CTF) inv_preamble_fd = numpy.array( block_header.pilotsym_fd[ block_header.channel_estimation_pilot[0] ] ) #print "Channel estimation pilot: ", inv_preamble_fd inv_preamble_fd = 1. / inv_preamble_fd LS_channel_estimator = ofdm.multiply_const_vcc( list( inv_preamble_fd ) ) self.connect( ofdm_blocks_est, LS_channel_estimator ) estimated_CTF = LS_channel_estimator terminate_stream(self,estimated_CTF) """ if options.ideal is False and options.ideal2 is False: if options.logcir: log_to_file(self, sampled_chest_preamble, "data/PREAM.compl") if not options.disable_ctf_enhancer: if options.logcir: ifft1 = fft_blocks.fft_vcc(total_subc, False, [], True) self.connect( estimated_CTF, ifft1, gr.null_sink(gr.sizeof_gr_complex * total_subc)) summ1 = ofdm.vector_sum_vcc(total_subc) c2m = gr.complex_to_mag(total_subc) self.connect(estimated_CTF, summ1, gr.null_sink(gr.sizeof_gr_complex)) self.connect(estimated_CTF, c2m, gr.null_sink(gr.sizeof_float * total_subc)) log_to_file(self, ifft1, "data/CIR1.compl") log_to_file(self, summ1, "data/CTFsumm1.compl") log_to_file(self, estimated_CTF, "data/CTF1.compl") log_to_file(self, c2m, "data/CTFmag1.float") ## MSE enhancer ctf_mse_enhancer = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect(estimated_CTF, ctf_mse_enhancer) # log_to_file( self, ctf_mse_enhancer, "data/ctf_mse_enhancer_original.compl") #ifft3 = fft_blocks.fft_vcc(total_subc,False,[],True) #null_noise = ofdm.noise_nulling(total_subc, cp_length + cp_length) #ctf_mse_enhancer = fft_blocks.fft_vcc(total_subc,True,[],True) #ctf_mse_enhancer = ofdm.vector_mask( fft_length, virtual_subc/2, # total_subc, [] ) #self.connect( estimated_CTF, ifft3,null_noise,ctf_mse_enhancer ) estimated_CTF = ctf_mse_enhancer print "Disabled CTF MSE enhancer" if options.logcir: ifft2 = fft_blocks.fft_vcc(total_subc, False, [], True) self.connect(estimated_CTF, ifft2, gr.null_sink(gr.sizeof_gr_complex * total_subc)) summ2 = ofdm.vector_sum_vcc(total_subc) c2m2 = gr.complex_to_mag(total_subc) self.connect(estimated_CTF, summ2, gr.null_sink(gr.sizeof_gr_complex)) self.connect(estimated_CTF, c2m2, gr.null_sink(gr.sizeof_float * total_subc)) log_to_file(self, ifft2, "data/CIR2.compl") log_to_file(self, summ2, "data/CTFsumm2.compl") log_to_file(self, estimated_CTF, "data/CTF2.compl") log_to_file(self, c2m2, "data/CTFmag2.float") ## Postprocess the CTF estimate ## CTF -> inverse CTF (for equalizer) ## CTF -> norm |.|^2 (for CTF display) ctf_postprocess = ofdm.fbmc_postprocess_CTF_estimate(total_subc) self.connect(help2, ctf_postprocess) #estimated_SNR = ( ctf_postprocess, 0 ) disp_CTF = (ctf_postprocess, 0) #self.connect(estimated_SNR,out_snr_pream) #log_to_file( self, estimated_SNR, "data/fbmc_SNR.float" ) #Disable measured SNR output #terminate_stream(self, estimated_SNR) #self.connect(blocks.vector_source_f([10.0],True) ,out_snr_pream) # if options.disable_equalization or options.ideal: # terminate_stream(self, inv_estimated_CTF) # inv_estimated_CTF_vec = blocks.vector_source_c([1.0/fft_length*math.sqrt(total_subc)]*total_subc,True,total_subc) # inv_estimated_CTF_str = blocks.vector_to_stream(gr.sizeof_gr_complex, total_subc) # self.inv_estimated_CTF_mul = ofdm.multiply_const_ccf( 1.0/config.rms_amplitude ) # #inv_estimated_CTF_mul.set_k(1.0/config.rms_amplitude) # inv_estimated_CTF = blocks.stream_to_vector(gr.sizeof_gr_complex, total_subc) # self.connect( inv_estimated_CTF_vec, inv_estimated_CTF_str, self.inv_estimated_CTF_mul, inv_estimated_CTF) # print "Disabled equalization stage" ''' ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) print"\t\t\t\t\tnondata_blocks=",nondata_blocks pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers,0 ) self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) self.connect( inv_estimated_CTF, ( phase_tracking, 1 ) ) self.connect( frame_start, ( phase_tracking, 2 ) ) ## if options.scatter_plot_before_phase_tracking: self.before_phase_tracking = equalizer if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ''' ## Channel Equalizer ##equalizer = ofdm.channel_equalizer( total_subc ) ##self.connect( ofdm_blocks, ( equalizer, 0 ) ) ##self.connect( inv_estimated_CTF, ( equalizer, 1 ) ) ##self.connect( frame_start, ( equalizer, 2 ) ) ##ofdm_blocks = equalizer #log_to_file(self, equalizer,"data/equalizer_siso.compl") #log_to_file(self, ofdm_blocks, "data/equalizer.compl") ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) print "\t\t\t\t\tnondata_blocks=", nondata_blocks pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers if options.scatter_plot_before_phase_tracking: self.before_phase_tracking = equalizer ## Output connections self.connect(ofdm_blocks, out_ofdm_blocks) self.connect(frame_start, out_frame_start) if options.ideal is False and options.ideal2 is False: self.connect(disp_CTF, out_disp_ctf) else: self.connect(blocks.vector_source_f([1.0] * total_subc), blocks.stream_to_vector(gr.sizeof_float, total_subc), out_disp_ctf) if log: log_to_file(self, sc_metric, "data/sc_metric.float") log_to_file(self, gi_metric, "data/gi_metric.float") log_to_file(self, morelli_foe, "data/morelli_foe.float") log_to_file(self, lms_fir, "data/lms_fir.float") log_to_file(self, sampler_preamble, "data/preamble.compl") log_to_file(self, sync, "data/sync.compl") log_to_file(self, frequency_shift, "data/frequency_shift.compl") log_to_file(self, fft, "data/fft.compl") log_to_file(self, fft, "data/fft.float", mag=True) if vars().has_key('subcarrier_mask'): log_to_file(self, subcarrier_mask, "data/subcarrier_mask.compl") log_to_file(self, ofdm_blocks, "data/ofdm_blocks_out.compl") log_to_file(self, frame_start, "data/frame_start.float", char_to_float=True) log_to_file(self, sampled_chest_preamble, "data/sampled_chest_preamble.compl") log_to_file(self, LS_channel_estimator, "data/ls_channel_estimator.compl") log_to_file(self, LS_channel_estimator, "data/ls_channel_estimator.float", mag=True) if "ctf_mse_enhancer" in locals(): log_to_file(self, ctf_mse_enhancer, "data/ctf_mse_enhancer.compl") log_to_file(self, ctf_mse_enhancer, "data/ctf_mse_enhancer.float", mag=True) log_to_file(self, (ctf_postprocess, 0), "data/inc_estimated_ctf.compl") log_to_file(self, (ctf_postprocess, 1), "data/disp_ctf.float") log_to_file(self, equalizer, "data/equalizer.compl") log_to_file(self, equalizer, "data/equalizer.float", mag=True) log_to_file(self, phase_tracking, "data/phase_tracking.compl")
def publish_rx_performance_measure(self): if self._rx_performance_measure_initialized(): return self.rx_performance_measure_initialized = True config = station_configuration() vlen = config.data_subcarriers vlen_sinr_sc = config.subcarriers # self.rx_per_sink = rpsink = rpsink_dummy() self.setup_ber_measurement() self.setup_snr_measurement() self.setup_snr_measurement_2() ber_mst = self._ber_measuring_tool if self._options.sinr_est: sinr_mst = self._sinr_measurement sinr_mst_2 = self._sinr_measurement_2 else: snr_mst = self._snr_measurement snr_mst_2 = self._snr_measurement_2 # 1. frame id # 2. channel transfer function ctf = self.filter_ctf() ctf_2 = self.filter_ctf_2() self.zmq_probe_ctf = zeromq.pub_sink(gr.sizeof_float,config.data_subcarriers, "tcp://*:5559") self.zmq_probe_ctf_2 = zeromq.pub_sink(gr.sizeof_float,config.data_subcarriers, "tcp://*:5558") self.connect(ctf, blocks.keep_one_in_n(gr.sizeof_float*config.data_subcarriers,20) ,self.zmq_probe_ctf) self.connect(ctf_2, blocks.keep_one_in_n(gr.sizeof_float*config.data_subcarriers,20) ,self.zmq_probe_ctf_2) # 3. BER ### FIXME HACK print "Normal BER measurement" trig_src = dynamic_trigger_ib(False) self.connect(self.bitcount_src,trig_src) ber_sampler = vector_sampler(gr.sizeof_float,1) self.connect(ber_mst,(ber_sampler,0)) self.connect(trig_src,(ber_sampler,1)) if self._options.log: trig_src_float = gr.char_to_float() self.connect(trig_src,trig_src_float) log_to_file(self, trig_src_float , 'data/dynamic_trigger_out.float') if self._options.sinr_est is False: self.zmq_probe_ber = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5556") self.connect(ber_sampler,blocks.keep_one_in_n(gr.sizeof_float,20) ,self.zmq_probe_ber) self.zmq_probe_snr = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5555") self.connect(snr_mst,blocks.keep_one_in_n(gr.sizeof_float,20) ,self.zmq_probe_snr) self.zmq_probe_snr_2 = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5554") self.connect(snr_mst_2,blocks.keep_one_in_n(gr.sizeof_float,20) ,self.zmq_probe_snr_2)
def __init__(self, options, log=False): ## Read configuration config = station_configuration() fft_length = config.fft_length cp_length = config.cp_length block_header = config.training_data data_subc = config.data_subcarriers virtual_subc = config.virtual_subcarriers total_subc = config.subcarriers block_length = config.block_length frame_length = config.frame_length dc_null = config.dc_null L = block_header.mm_periodic_parts ## Set Input/Output signature gr.hier_block2.__init__( self, "ofdm_inner_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signaturev( 4, 4, [ gr.sizeof_gr_complex * total_subc, # OFDM blocks gr.sizeof_char, # Frame start gr.sizeof_float * total_subc, gr.sizeof_float ])) # Normalized |CTF|^2 ## Input and output ports self.input = rx_input = self out_ofdm_blocks = (self, 0) out_frame_start = (self, 1) out_disp_ctf = (self, 2) out_disp_cfo = (self, 3) ## pre-FFT processing if options.ideal is False and options.ideal2 is False: if options.old_receiver is False: ## Compute autocorrelations for S&C preamble ## and cyclic prefix self._sc_metric = sc_metric = autocorrelator( fft_length / 2, fft_length / 2) self._gi_metric = gi_metric = autocorrelator( fft_length, cp_length) self.connect(rx_input, sc_metric) self.connect(rx_input, gi_metric) ## Sync. Output contains OFDM blocks sync = ofdm.time_sync(fft_length, cp_length) self.connect(rx_input, (sync, 0)) self.connect(sc_metric, (sync, 1)) self.connect(gi_metric, (sync, 2)) ofdm_blocks = (sync, 0) frame_start = (sync, 1) #log_to_file( self, ( sync, 1 ), "data/peak_detector.char" ) else: #Testing old/new metric self.tm = schmidl.recursive_timing_metric(fft_length) self.connect(self.input, self.tm) #log_to_file( self, self.tm, "data/rec_sc_metric_ofdm.float" ) timingmetric_shift = -2 #int(-cp_length/4)# 0#-2 #int(-cp_length * 0.8) tmfilter = filter.fft_filter_fff(1, [1. / cp_length] * cp_length) self.connect(self.tm, tmfilter) self.tm = tmfilter self._pd_thres = 0.3 self._pd_lookahead = fft_length / 2 # empirically chosen peak_detector = ofdm.peak_detector_02_fb( self._pd_lookahead, self._pd_thres) self.connect(self.tm, peak_detector) #log_to_file( self, peak_detector, "data/rec_peak_detector.char" ) frame_start = [0] * frame_length frame_start[0] = 1 frame_start = self.frame_trigger_old = blocks.vector_source_b( frame_start, True) delayed_timesync = blocks.delay( gr.sizeof_char, (frame_length - 1) * block_length + timingmetric_shift) self.connect(peak_detector, delayed_timesync) self.block_sampler = ofdm.vector_sampler( gr.sizeof_gr_complex, block_length * frame_length) self.discard_cp = ofdm.vector_mask(block_length, cp_length, fft_length, []) self.connect(self.input, self.block_sampler) self.connect(delayed_timesync, (self.block_sampler, 1)) # TODO: dynamic solution vt2s = blocks.vector_to_stream( gr.sizeof_gr_complex * block_length, frame_length) self.connect(self.block_sampler, vt2s, self.discard_cp) #terminate_stream(self,ofdm_blocks) ofdm_blocks = self.discard_cp # else: # serial_to_parallel = blocks.stream_to_vector(gr.sizeof_gr_complex,block_length) # discard_cp = ofdm.vector_mask(block_length,cp_length,fft_length,[]) # ofdm_blocks = discard_cp # self.connect( rx_input, serial_to_parallel, discard_cp ) # frame_start = [0]*frame_length # frame_start[0] = 1 # frame_start = blocks.vector_source_b(frame_start,True) # # print "Disabled time synchronization stage" ## Compute autocorrelations for S&C preamble ## and cyclic prefix #log_to_file( self, sc_metric, "data/sc_metric_ofdm.float" ) #log_to_file(self, frame_start, "data/frame_start.compl") # log_to_file(self,ofdm_blocks,"data/ofdm_blocks_original.compl") if options.disable_time_sync or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, ofdm_blocks) terminate_stream(self, frame_start) serial_to_parallel = blocks.stream_to_vector( gr.sizeof_gr_complex, block_length) discard_cp = ofdm.vector_mask_dc_null(block_length, cp_length, fft_length, dc_null, []) ofdm_blocks = discard_cp self.connect(rx_input, serial_to_parallel, discard_cp) frame_start = [0] * frame_length frame_start[0] = 1 frame_start = blocks.vector_source_b(frame_start, True) print "Disabled time synchronization stage" print "\t\t\t\t\tframe_length = ", frame_length if options.ideal is False and options.ideal2 is False: ## Extract preamble, feed to Morelli & Mengali frequency offset estimator assert (block_header.mm_preamble_pos == 0) morelli_foe = ofdm.mm_frequency_estimator(fft_length, L, 1, 0) sampler_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length, 1) self.connect(ofdm_blocks, (sampler_preamble, 0)) self.connect(frame_start, (sampler_preamble, 1)) self.connect(sampler_preamble, morelli_foe) freq_offset = morelli_foe ## Adaptive LMS FIR filtering of frequency offset lms_fir = ofdm.lms_fir_ff(20, 1e-3) # TODO: verify parameter choice self.connect(freq_offset, lms_fir) freq_offset = lms_fir #self.zmq_probe_freqoff = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5557") self.connect(lms_fir, blocks.keep_one_in_n(gr.sizeof_float, 20), out_disp_cfo) else: self.connect(blocks.vector_source_f([1]), out_disp_cfo) #log_to_file(self, lms_fir, "data/lms_fir.float") if options.disable_freq_sync or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, freq_offset) freq_offset = blocks.vector_source_f([0.0], True) print "Disabled frequency synchronization stage" if options.ideal is False and options.ideal2 is False: ## Correct frequency shift, feed-forward structure frequency_shift = ofdm.frequency_shift_vcc(fft_length, -1.0 / fft_length, cp_length) self.connect(ofdm_blocks, (frequency_shift, 0)) self.connect(freq_offset, (frequency_shift, 1)) self.connect(frame_start, (frequency_shift, 2)) ofdm_blocks = frequency_shift ## FFT fft = fft_blocks.fft_vcc(fft_length, True, [], True) self.connect(ofdm_blocks, fft) ofdm_blocks = fft #log_to_file( self, fft, "data/compen.float" ) ## Remove virtual subcarriers if fft_length > data_subc: subcarrier_mask = ofdm.vector_mask_dc_null(fft_length, virtual_subc / 2, total_subc, dc_null, []) self.connect(ofdm_blocks, subcarrier_mask) ofdm_blocks = subcarrier_mask #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") ## Least Squares estimator for channel transfer function (CTF) if options.logcir: log_to_file(self, ofdm_blocks, "data/OFDM_Blocks.compl") inv_preamble_fd = numpy.array(block_header.pilotsym_fd[ block_header.channel_estimation_pilot[0]]) inv_preamble_fd = numpy.concatenate([ inv_preamble_fd[:total_subc / 2], inv_preamble_fd[total_subc / 2 + dc_null:] ]) #print "Channel estimation pilot: ", inv_preamble_fd inv_preamble_fd = 1. / inv_preamble_fd LS_channel_estimator0 = ofdm.multiply_const_vcc( list(inv_preamble_fd)) self.connect(ofdm_blocks, LS_channel_estimator0, gr.null_sink(gr.sizeof_gr_complex * total_subc)) log_to_file(self, LS_channel_estimator0, "data/OFDM_Blocks_eq.compl") ## post-FFT processing ## extract channel estimation preamble from frame if options.ideal is False and options.ideal2 is False: chest_pre_trigger = blocks.delay(gr.sizeof_char, 1) sampled_chest_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1) self.connect(frame_start, chest_pre_trigger) self.connect(chest_pre_trigger, (sampled_chest_preamble, 1)) self.connect(ofdm_blocks, (sampled_chest_preamble, 0)) ## Least Squares estimator for channel transfer function (CTF) inv_preamble_fd = numpy.array(block_header.pilotsym_fd[ block_header.channel_estimation_pilot[0]]) inv_preamble_fd = numpy.concatenate([ inv_preamble_fd[:total_subc / 2], inv_preamble_fd[total_subc / 2 + dc_null:] ]) #print "Channel estimation pilot: ", inv_preamble_fd inv_preamble_fd = 1. / inv_preamble_fd LS_channel_estimator = ofdm.multiply_const_vcc( list(inv_preamble_fd)) self.connect(sampled_chest_preamble, LS_channel_estimator) estimated_CTF = LS_channel_estimator if options.logcir: log_to_file(self, sampled_chest_preamble, "data/PREAM.compl") if not options.disable_ctf_enhancer: if options.logcir: ifft1 = fft_blocks.fft_vcc(total_subc, False, [], True) self.connect( estimated_CTF, ifft1, gr.null_sink(gr.sizeof_gr_complex * total_subc)) summ1 = ofdm.vector_sum_vcc(total_subc) c2m = gr.complex_to_mag(total_subc) self.connect(estimated_CTF, summ1, gr.null_sink(gr.sizeof_gr_complex)) self.connect(estimated_CTF, c2m, gr.null_sink(gr.sizeof_float * total_subc)) log_to_file(self, ifft1, "data/CIR1.compl") log_to_file(self, summ1, "data/CTFsumm1.compl") log_to_file(self, estimated_CTF, "data/CTF1.compl") log_to_file(self, c2m, "data/CTFmag1.float") ## MSE enhancer ctf_mse_enhancer = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect(estimated_CTF, ctf_mse_enhancer) # log_to_file( self, ctf_mse_enhancer, "data/ctf_mse_enhancer_original.compl") #ifft3 = fft_blocks.fft_vcc(total_subc,False,[],True) #null_noise = ofdm.noise_nulling(total_subc, cp_length + cp_length) #ctf_mse_enhancer = fft_blocks.fft_vcc(total_subc,True,[],True) #ctf_mse_enhancer = ofdm.vector_mask( fft_length, virtual_subc/2, # total_subc, [] ) #self.connect( estimated_CTF, ifft3,null_noise,ctf_mse_enhancer ) estimated_CTF = ctf_mse_enhancer print "Disabled CTF MSE enhancer" if options.logcir: ifft2 = fft_blocks.fft_vcc(total_subc, False, [], True) self.connect(estimated_CTF, ifft2, gr.null_sink(gr.sizeof_gr_complex * total_subc)) summ2 = ofdm.vector_sum_vcc(total_subc) c2m2 = gr.complex_to_mag(total_subc) self.connect(estimated_CTF, summ2, gr.null_sink(gr.sizeof_gr_complex)) self.connect(estimated_CTF, c2m2, gr.null_sink(gr.sizeof_float * total_subc)) log_to_file(self, ifft2, "data/CIR2.compl") log_to_file(self, summ2, "data/CTFsumm2.compl") log_to_file(self, estimated_CTF, "data/CTF2.compl") log_to_file(self, c2m2, "data/CTFmag2.float") ## Postprocess the CTF estimate ## CTF -> inverse CTF (for equalizer) ## CTF -> norm |.|^2 (for CTF display) ctf_postprocess = ofdm.postprocess_CTF_estimate(total_subc) self.connect(estimated_CTF, ctf_postprocess) inv_estimated_CTF = (ctf_postprocess, 0) disp_CTF = (ctf_postprocess, 1) # if options.disable_equalization or options.ideal: # terminate_stream(self, inv_estimated_CTF) # inv_estimated_CTF_vec = blocks.vector_source_c([1.0/fft_length*math.sqrt(total_subc)]*total_subc,True,total_subc) # inv_estimated_CTF_str = blocks.vector_to_stream(gr.sizeof_gr_complex, total_subc) # self.inv_estimated_CTF_mul = ofdm.multiply_const_ccf( 1.0/config.rms_amplitude ) # #inv_estimated_CTF_mul.set_k(1.0/config.rms_amplitude) # inv_estimated_CTF = blocks.stream_to_vector(gr.sizeof_gr_complex, total_subc) # self.connect( inv_estimated_CTF_vec, inv_estimated_CTF_str, self.inv_estimated_CTF_mul, inv_estimated_CTF) # print "Disabled equalization stage" ''' ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) print"\t\t\t\t\tnondata_blocks=",nondata_blocks pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers,0 ) self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) self.connect( inv_estimated_CTF, ( phase_tracking, 1 ) ) self.connect( frame_start, ( phase_tracking, 2 ) ) ## if options.scatter_plot_before_phase_tracking: self.before_phase_tracking = equalizer if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ''' ## Channel Equalizer if options.disable_equalization or options.ideal or options.ideal2: print "Disabled equalization stage" if options.ideal is False and options.ideal2 is False: terminate_stream(self, inv_estimated_CTF) else: equalizer = ofdm.channel_equalizer(total_subc) self.connect(ofdm_blocks, (equalizer, 0)) self.connect(inv_estimated_CTF, (equalizer, 1)) self.connect(frame_start, (equalizer, 2)) ofdm_blocks = equalizer #log_to_file(self, equalizer,"data/equalizer_siso.compl") #log_to_file(self, ofdm_blocks, "data/equalizer.compl") ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset if options.ideal is False and options.ideal2 is False: nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) print "\t\t\t\t\tnondata_blocks=", nondata_blocks pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking2 = ofdm.lms_phase_tracking_dc_null( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers, dc_null) self.connect(ofdm_blocks, (phase_tracking2, 0)) self.connect(frame_start, (phase_tracking2, 1)) ## if options.disable_phase_tracking or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, phase_tracking2) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking2 if options.scatter_plot_before_phase_tracking: self.before_phase_tracking = equalizer ## Output connections self.connect(ofdm_blocks, out_ofdm_blocks) self.connect(frame_start, out_frame_start) if options.ideal is False and options.ideal2 is False: self.connect(disp_CTF, out_disp_ctf) else: self.connect(blocks.vector_source_f([1.0] * total_subc), blocks.stream_to_vector(gr.sizeof_float, total_subc), out_disp_ctf) if log: log_to_file(self, sc_metric, "data/sc_metric.float") log_to_file(self, gi_metric, "data/gi_metric.float") log_to_file(self, morelli_foe, "data/morelli_foe.float") log_to_file(self, lms_fir, "data/lms_fir.float") log_to_file(self, sampler_preamble, "data/preamble.compl") log_to_file(self, sync, "data/sync.compl") log_to_file(self, frequency_shift, "data/frequency_shift.compl") log_to_file(self, fft, "data/fft.compl") log_to_file(self, fft, "data/fft.float", mag=True) if vars().has_key('subcarrier_mask'): log_to_file(self, subcarrier_mask, "data/subcarrier_mask.compl") log_to_file(self, ofdm_blocks, "data/ofdm_blocks_out.compl") log_to_file(self, frame_start, "data/frame_start.float", char_to_float=True) log_to_file(self, sampled_chest_preamble, "data/sampled_chest_preamble.compl") log_to_file(self, LS_channel_estimator, "data/ls_channel_estimator.compl") log_to_file(self, LS_channel_estimator, "data/ls_channel_estimator.float", mag=True) if "ctf_mse_enhancer" in locals(): log_to_file(self, ctf_mse_enhancer, "data/ctf_mse_enhancer.compl") log_to_file(self, ctf_mse_enhancer, "data/ctf_mse_enhancer.float", mag=True) log_to_file(self, (ctf_postprocess, 0), "data/inc_estimated_ctf.compl") log_to_file(self, (ctf_postprocess, 1), "data/disp_ctf.float") log_to_file(self, equalizer, "data/equalizer.compl") log_to_file(self, equalizer, "data/equalizer.float", mag=True) log_to_file(self, phase_tracking, "data/phase_tracking.compl")
def __init__( self, vlen, cp_len ): gr.hier_block2.__init__( self, "channel_estimator_003", gr.io_signature( 1, 1, gr.sizeof_gr_complex * vlen ), gr.io_signature( 1, 1, gr.sizeof_gr_complex * vlen ) ) self.ch_est = channel_estimator_001( vlen ) self.connect( self, self.ch_est ) cir_len = cp_len + 1 self.cir_len = cir_len self.vlen = vlen self.preamble_td = self.ch_est.td self.preamble_fd = self.ch_est.fd # CTF -> CIR self.cir_est = gr.fft_vcc( vlen, False, [], False ) # IFFT self.connect( self.ch_est, self.cir_est ) # CIR -> energy per sample self.cir_energy = gr.complex_to_mag_squared( vlen ) self.connect( self.cir_est, self.cir_energy ) # prepare cyclic convolution of CIR vector self.cyclic_cir = gr.streams_to_vector( gr.sizeof_float * vlen, 2 ) self.connect( self.cir_energy, ( self.cyclic_cir, 0 ) ) self.connect( self.cir_energy, ( self.cyclic_cir, 1 ) ) # Cyclic convolution of CIR vector # Pad CIR vector: 0 x cp_len, CIR, CIR, 0 x cp_len self.serial_ccir = gr.vector_to_stream( gr.sizeof_float, vlen * 2 ) self.padded_sccir = gr.stream_mux( gr.sizeof_float, [ cir_len-1, 2*vlen, cir_len-1 ] ) if cir_len > 1: self.conv = gr.fir_filter_fff( 1, [ 1 ] * cir_len ) else: self.conv = gr.kludge_copy( gr.sizeof_float ) self.connect( self.padded_sccir, self.conv ) self.null_source = gr.null_source( gr.sizeof_float ) self.connect( self.cyclic_cir, self.serial_ccir ) self.connect( self.null_source, ( self.padded_sccir, 0 ) ) self.connect( self.serial_ccir, ( self.padded_sccir, 1 ) ) self.connect( self.null_source, ( self.padded_sccir, 2 ) ) # Extract search window self.search_window = ofdm.vector_sampler( gr.sizeof_float, vlen ) periodic_trigger_seq = [ 0 ] * ( ( vlen + cir_len-1 ) * 2 ) periodic_trigger_seq[ cir_len-1 + cir_len-1 + vlen-1 ] = 1 self.sampler_trigsrc = gr.vector_source_b( periodic_trigger_seq, True ) self.connect( self.conv, self.search_window ) self.connect( self.sampler_trigsrc, ( self.search_window, 1 ) ) # Find point of maximum energy self.cir_start = gr.argmax_fs( vlen ) self.connect( self.search_window, self.cir_start ) self.connect( ( self.cir_start, 1 ), gr.null_sink( gr.sizeof_short ) ) # Set to zero all samples that do not belong to the CIR self.filtered_cir = ofdm.interp_cir_set_noncir_to_zero( vlen, cir_len ) #self.filtered_cir = gr.kludge_copy( gr.sizeof_gr_complex * vlen ) self.connect( self.cir_est, self.filtered_cir ) self.connect( self.cir_start, ( self.filtered_cir, 1 ) ) #self.connect( self.cir_start, gr.null_sink( gr.sizeof_short ) ) # CIR -> CTF self.filtered_ctf = gr.fft_vcc( vlen, True, [], False ) # FFT self.scaled_fctf = gr.multiply_const_vcc( [1./vlen]*vlen ) self.connect( self.filtered_cir, self.filtered_ctf ) self.connect( self.filtered_ctf, self.scaled_fctf ) # Output connection self.connect( self.scaled_fctf, self )
def __init__(self, fft_length, block_length, frame_data_part, block_header, options): gr.hier_block2.__init__( self, "ofdm_receiver", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature2(2, 2, gr.sizeof_gr_complex * fft_length, gr.sizeof_char)) frame_length = frame_data_part + block_header.no_pilotsyms cp_length = block_length - fft_length self.input = gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) self.blocks_out = (self, 0) self.frame_trigger_out = (self, 1) self.snr_out = (self, 2) if options.log: log_to_file(self, self.input, "data/receiver_input.compl") # peak detector: thresholds low, high #self._pd_thres_lo = 0.09 #self._pd_thres_hi = 0.1 self._pd_thres = 0.2 self._pd_lookahead = fft_length / 2 # empirically chosen ######################### # coarse timing offset estimator # self.tm = schmidl.modified_timing_metric(fft_length,[1]*(fft_length)) self.tm = schmidl.recursive_timing_metric(fft_length) self.connect(self.input, self.tm) assert (hasattr(block_header, 'sc_preamble_pos')) assert (block_header.sc_preamble_pos == 0 ) # TODO: relax this restriction if options.filter_timingmetric: timingmetric_shift = -2 #int(-cp_length * 0.8) tmfilter = gr.fir_filter_fff(1, [1. / cp_length] * cp_length) self.connect(self.tm, tmfilter) self.timing_metric = tmfilter print "Filtering timing metric, experimental" else: self.timing_metric = self.tm timingmetric_shift = int(-cp_length / 4) if options.log: log_to_file(self, self.timing_metric, "data/tm.float") # peak detection #threshold = gr.threshold_ff(self._pd_thres_lo,self._pd_thres_hi,0) #muted_tm = gr.multiply_ff() peak_detector = peak_detector_02_fb(self._pd_lookahead, self._pd_thres) #self.connect(self.timing_metric, threshold, (muted_tm,0)) #self.connect(self.timing_metric, (muted_tm,1)) #self.connect(muted_tm, peak_detector) self.connect(self.timing_metric, peak_detector) if options.log: pd_float = gr.char_to_float() self.connect(peak_detector, pd_float) log_to_file(self, pd_float, "data/peakdetector.float") if options.no_timesync: terminate_stream(self, peak_detector) trigger = [0] * (frame_length * block_length) trigger[block_length - 1] = 1 peak_detector = blocks.vector_source_b(trigger, True) print "Bypassing timing synchronisation" # TODO: refine detected peaks with 90% average method as proposed # from Schmidl & Cox: # Starting from peak, find first points to the left and right whose # value is less than or equal 90% of the peak value. New trigger point # is average of both # Frequency Offset Estimation # Used: Algorithm as proposed from Morelli & Mengali # Idea: Use periodic preamble, correlate identical parts, determine # phase offset. This phase offset is a function of the frequency offset. assert (hasattr(block_header, 'mm_preamble_pos')) foe = morelli_foe(fft_length, block_header.mm_periodic_parts) self.connect(self.input, (foe, 0)) if block_header.mm_preamble_pos > 0: delayed_trigger = gr.delay( gr.sizeof_char, block_header.mm_preamble_pos * block_length) self.connect(peak_detector, delayed_trigger, (foe, 1)) else: self.connect(peak_detector, (foe, 1)) self.freq_offset = foe if options.log: log_to_file(self, self.freq_offset, "data/freqoff_out.float") if options.average_freqoff: #avg_foe = gr.single_pole_iir_filter_ff( 0.1 ) avg_foe = ofdm.lms_fir_ff(20, 1e-3) self.connect(self.freq_offset, avg_foe) self.freq_offset = avg_foe #log_to_file( self, avg_foe, "data/freqoff_out_avg.float" ) print "EXPERIMENTAL!!! Filtering frequency offset estimate" if options.no_freqsync: terminate_stream(self, self.freq_offset) self.freq_offset = blocks.vector_source_f([0.0], True) print "Bypassing frequency offset estimator, offset=0.0" # TODO: dynamic solution frametrig_seq = concatenate([[1], [0] * (frame_length - 1)]) self.time_sync = peak_detector self.frame_trigger = blocks.vector_source_b(frametrig_seq, True) self.connect(self.frame_trigger, self.frame_trigger_out) ########################## # symbol extraction and processing # First, we extract the whole ofdm block, then we divide this block into # several ofdm symbols. This asserts that all symbols belonging to the # same ofdm block will be a consecutive order. # extract ofdm symbols # compensate frequency offset # TODO: use PLL and update/reset signals delayed_timesync = gr.delay(gr.sizeof_char, (frame_length - 1) * block_length + timingmetric_shift) self.connect(self.time_sync, delayed_timesync) self.block_sampler = vector_sampler(gr.sizeof_gr_complex, block_length * frame_length) self.discard_cp = vector_mask(block_length, cp_length, fft_length, []) if options.use_dpll: dpll = gr.dpll_bb(frame_length * block_length, .01) self.connect(delayed_timesync, dpll) if options.log: dpll_f = gr.char_to_float() delayed_timesync_f = gr.char_to_float() self.connect(dpll, dpll_f) self.connect(delayed_timesync, delayed_timesync_f) log_to_file(self, dpll_f, "data/dpll.float") log_to_file(self, delayed_timesync_f, "data/dpll_in.float") delayed_timesync = dpll print "Using DPLL, EXPERIMENTAL!!!!!" self.connect(self.input, self.block_sampler) self.connect(delayed_timesync, (self.block_sampler, 1)) if options.log: log_to_file(self, self.block_sampler, "data/block_sampler_out.compl") # TODO: dynamic solution self.ofdm_symbols = blocks.vector_to_stream( gr.sizeof_gr_complex * block_length, frame_length) self.connect(self.block_sampler, self.ofdm_symbols, self.discard_cp) if options.log: log_to_file(self, self.discard_cp, "data/discard_cp_out.compl") dcp_fft = gr.fft_vcc(fft_length, True, [], True) self.connect(self.discard_cp, dcp_fft) log_to_file(self, dcp_fft, "data/discard_cp_fft.compl") # reset phase accumulator inside freq_shift on every block start # setup output connection freq_shift = frequency_shift_vcc(fft_length, -1.0 / fft_length, cp_length) self.connect(self.discard_cp, (freq_shift, 0)) self.connect(self.freq_offset, (freq_shift, 1)) self.connect(self.frame_trigger, (freq_shift, 2)) self.connect(freq_shift, self.blocks_out) if options.log: log_to_file(self, freq_shift, "data/freqshift_out.compl") if options.no_freqshift: terminate_stream(self, freq_shift) freq_shift = self.discard_cp print "Bypassing frequency shift block"
def publish_rx_performance_measure(self): if self._rx_performance_measure_initialized(): return self.rx_performance_measure_initialized = True config = station_configuration() vlen = config.data_subcarriers vlen_sinr_sc = config.subcarriers if self.ideal2 is False: self.setup_ber_measurement() self.setup_snr_measurement() ber_mst = self._ber_measuring_tool if self._options.sinr_est: sinr_mst = self._sinr_measurement else: if self.ideal2 is False: snr_mst = self._snr_measurement if self.ideal is False and self.ideal2 is False: self.ctf = self.filter_ctf() self.zmq_probe_ctf = zeromq.pub_sink(gr.sizeof_float,config.data_subcarriers, "tcp://*:5559") self.connect(self.ctf, blocks.keep_one_in_n(gr.sizeof_float*config.data_subcarriers,20) ,self.zmq_probe_ctf) else: #self.zmq_probe_ctf = zeromq.pub_sink(gr.sizeof_float,config.subcarriers, "tcp://*:5559") self.connect(self.ctf,blocks.null_sink(gr.sizeof_float*config.subcarriers)) #self.rx_per_sink = rpsink = corba_rxinfo_sink("himalaya",config.ns_ip, # config.ns_port,vlen,config.rx_station_id) # print "BER img xfer" # self.connect(ber_mst,(rpsink,3)) # ## no sampling needed # 3. SNR if self.ideal2 is False: print "Normal BER measurement" trig_src = dynamic_trigger_ib(False) self.connect(self.bitcount_src,trig_src) ber_sampler = vector_sampler(gr.sizeof_float,1) self.connect(ber_mst,(ber_sampler,0)) self.connect(trig_src,(ber_sampler,1)) else: if(self._options.coding): demod = self._data_decoder else: demod = self._data_demodulator self.connect(self.bitcount_src,blocks.null_sink(gr.sizeof_int) ) self.connect(demod,blocks.null_sink(gr.sizeof_char)) if self._options.log: trig_src_float = gr.char_to_float() self.connect(trig_src,trig_src_float) log_to_file(self, trig_src_float , 'data/dynamic_trigger_out.float') if self._options.sinr_est is False and self.ideal2 is False: self.zmq_probe_ber = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5556") self.connect(ber_sampler,blocks.keep_one_in_n(gr.sizeof_float,20) ,self.zmq_probe_ber) if self.ideal2 is False: self.zmq_probe_snr = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5555") self.connect(snr_mst,blocks.keep_one_in_n(gr.sizeof_float,20) ,self.zmq_probe_snr)
def __init__( self, options, log = False ): ## Read configuration config = station_configuration() fft_length = config.fft_length cp_length = config.cp_length block_header = config.training_data data_subc = config.data_subcarriers virtual_subc = config.virtual_subcarriers total_subc = config.subcarriers block_length = config.block_length frame_length = config.frame_length L = block_header.mm_periodic_parts frame_data_blocks = options.data_blocks ## Set Input/Output signature gr.hier_block2.__init__( self, "ofdm_inner_receiver", gr.io_signature( 1, 1, gr.sizeof_gr_complex ), gr.io_signature4( 4, 4, gr.sizeof_gr_complex * total_subc, # OFDM blocks gr.sizeof_char, # Frame start gr.sizeof_float * total_subc, # Normalized |CTF|^2 gr.sizeof_float * total_subc ) ) # Normalized |CTF|^2 ## Input and output ports self.input = rx_input = (self,0) out_ofdm_blocks = ( self, 0 ) out_frame_start = ( self, 1 ) out_disp_ctf = ( self, 2 ) out_disp_ctf2 = ( self, 3 ) ## pre-FFT processing ## Compute autocorrelations for S&C preamble ## and cyclic prefix sc_metric = autocorrelator( fft_length/2, fft_length/2 ) gi_metric = autocorrelator( fft_length, cp_length ) self.connect( rx_input, sc_metric ) self.connect( rx_input, gi_metric ) ## Sync. Output contains OFDM blocks sync = ofdm.time_sync( fft_length, cp_length ) self.connect( rx_input, ( sync, 0 ) ) self.connect( sc_metric, ( sync, 1 ) ) self.connect( gi_metric, ( sync, 2 ) ) ofdm_blocks = ( sync, 0 ) frame_start = ( sync, 1 ) if options.disable_time_sync or options.ideal: terminate_stream(self, ofdm_blocks) terminate_stream(self, frame_start) serial_to_parallel = blocks.stream_to_vector(gr.sizeof_gr_complex,block_length) discard_cp = ofdm.vector_mask(block_length,cp_length,fft_length,[]) ofdm_blocks = discard_cp self.connect( rx_input, serial_to_parallel, discard_cp ) frame_start = [0]*frame_length frame_start[0] = 1 frame_start = blocks.vector_source_b(frame_start,True) print "Disabled time synchronization stage" ## Extract preamble, feed to Morelli & Mengali frequency offset estimator assert( block_header.mm_preamble_pos == 0 ) morelli_foe = ofdm.mm_frequency_estimator( fft_length, L ) sampler_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length, 1 ) self.connect( ofdm_blocks, ( sampler_preamble, 0 ) ) self.connect( frame_start, ( sampler_preamble, 1 ) ) self.connect( sampler_preamble, morelli_foe ) freq_offset = morelli_foe ## Adaptive LMS FIR filtering of frequency offset lms_fir = ofdm.lms_fir_ff( 20, 1e-3 ) # TODO: verify parameter choice self.connect( freq_offset, lms_fir ) freq_offset = lms_fir log_to_file(self, lms_fir, "data/foe_21.float") # log_to_file(self, lms_fir, "data/lms_fir.float") # log_to_file(self, lms_fir2, "data/lms_fir2.float") if options.disable_freq_sync or options.ideal: terminate_stream(self, freq_offset) freq_offset = blocks.vector_source_f([0.0],True) print "Disabled frequency synchronization stage" ## Correct frequency shift, feed-forward structure frequency_shift = ofdm.frequency_shift_vcc( fft_length, -1.0/fft_length, cp_length ) self.connect( ofdm_blocks, ( frequency_shift, 0 ) ) self.connect( freq_offset, ( frequency_shift, 1 ) ) self.connect( frame_start, ( frequency_shift, 2 ) ) ofdm_blocks = frequency_shift ## FFT fft = fft_blocks.fft_vcc( fft_length, True, [], True ) self.connect( ofdm_blocks, fft ) ofdm_blocks = fft ## Remove virtual subcarriers if fft_length > data_subc: subcarrier_mask = ofdm.vector_mask( fft_length, virtual_subc/2, total_subc, [] ) self.connect( ofdm_blocks, subcarrier_mask ) ofdm_blocks = subcarrier_mask ## Least Squares estimator for channel transfer function (CTF) # if options.logcir: # log_to_file( self, ofdm_blocks, "data/OFDM_Blocks.compl" ) # inv_preamble_fd = numpy.array( block_header.pilotsym_fd[ # block_header.channel_estimation_pilot[0] ] ) # print "Channel estimation pilot: ", inv_preamble_fd # inv_preamble_fd = 1. / inv_preamble_fd # LS_channel_estimator0 = ofdm.multiply_const_vcc( list( inv_preamble_fd ) ) # self.connect( ofdm_blocks, LS_channel_estimator0, blocks.null_sink(gr.sizeof_gr_complex*total_subc)) # log_to_file( self, LS_channel_estimator0, "data/OFDM_Blocks_eq.compl" ) ## post-FFT processing ## extract channel estimation preamble from frame if options.est_preamble==1: chest_pre_trigger = blocks.delay( gr.sizeof_char, 1 ) sampled_chest_preamble = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) self.connect( frame_start, chest_pre_trigger ) self.connect( chest_pre_trigger, ( sampled_chest_preamble, 1 ) ) self.connect( ofdm_blocks, ( sampled_chest_preamble, 0 ) ) ## Least Squares estimator for channel transfer function (CTF) # Taking inverse for estimating h11 (h12) inv_preamble_fd_1 = numpy.array( block_header.pilotsym_fd_1[ block_header.channel_estimation_pilot[0] ] ) print "inv_preamble_fd_1: ",inv_preamble_fd_1 inv_preamble_fd_1 = inv_preamble_fd_1[0::2] #print "inv_preamble_fd_1 ", inv_preamble_fd_1 # Taking inverse for estimating h21 (h22) inv_preamble_fd_2 = numpy.array( block_header.pilotsym_fd_2[ block_header.channel_estimation_pilot[0] ] ) print "inv_preamble_fd_2: ",inv_preamble_fd_2 inv_preamble_fd_2 = inv_preamble_fd_2[1::2] #print "inv_preamble_fd_2 ", inv_preamble_fd_2 print "inv_preamble_fd_1: ",inv_preamble_fd_1 print "inv_preamble_fd_2: ",inv_preamble_fd_2 inv_preamble_fd_1 = 1. / inv_preamble_fd_1 inv_preamble_fd_2 = 1. / inv_preamble_fd_2 dd = [] for i in range (total_subc/2): dd.extend([i*2]) skip_block_1 = ofdm.int_skip(total_subc,2,0) skip_block_2 = ofdm.int_skip(total_subc,2,1) # inta_estim_1 = ofdm.interpolator(total_subc,2,dd) # inta_estim_2 = ofdm.interpolator(total_subc,2,dd) LS_channel_estimator_1 = ofdm.multiply_const_vcc( list( inv_preamble_fd_1 ) ) LS_channel_estimator_2 = ofdm.multiply_const_vcc( list( inv_preamble_fd_2 ) ) self.connect( sampled_chest_preamble,skip_block_1, LS_channel_estimator_1)#,inta_estim_1 ) self.connect( sampled_chest_preamble,skip_block_2, LS_channel_estimator_2)#,inta_estim_2 ) estimated_CTF_1 = LS_channel_estimator_1 # h0 estimated_CTF_2 = LS_channel_estimator_2 # h1 # h3 if not options.disable_ctf_enhancer: # if options.logcir: # ifft1 = fft_blocks.fft_vcc(total_subc,False,[],True) # self.connect( estimated_CTF, ifft1,blocks.null_sink(gr.sizeof_gr_complex*total_subc)) # summ1 = ofdm.vector_sum_vcc(total_subc) # c2m =gr.complex_to_mag(total_subc) # self.connect( estimated_CTF,summ1 ,blocks.null_sink(gr.sizeof_gr_complex)) # self.connect( estimated_CTF, c2m,blocks.null_sink(gr.sizeof_float*total_subc)) # log_to_file( self, ifft1, "data/CIR1.compl" ) # log_to_file( self, summ1, "data/CTFsumm1.compl" ) # log_to_file( self, estimated_CTF, "data/CTF1.compl" ) # log_to_file( self, c2m, "data/CTFmag1.float" ) ## MSE enhancer ctf_mse_enhancer_1 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) ctf_mse_enhancer_2 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect( estimated_CTF_1, ctf_mse_enhancer_1 ) self.connect( estimated_CTF_2, ctf_mse_enhancer_2 ) estimated_CTF_1 = ctf_mse_enhancer_1 estimated_CTF_2 = ctf_mse_enhancer_2 print "Disabled CTF MSE enhancer" ctf_postprocess_1 = ofdm.postprocess_CTF_estimate( total_subc/2 ) self.connect( estimated_CTF_1, ( ctf_postprocess_1, 0 ) ) ctf_postprocess_2 = ofdm.postprocess_CTF_estimate( total_subc/2 ) self.connect( estimated_CTF_2, ( ctf_postprocess_2, 0 ) ) inv_CTF_1 = ( ctf_postprocess_1, 0 ) disp_CTF_1 = ( ctf_postprocess_1, 1 ) inv_CTF_2 = ( ctf_postprocess_2, 0 ) disp_CTF_2 = ( ctf_postprocess_2, 1 ) disp_CTF_RX0 = blocks.add_ff(total_subc/2) self.connect ( disp_CTF_1, (disp_CTF_RX0, 0) ) self.connect ( disp_CTF_2, (disp_CTF_RX0, 1) ) terminate_stream(self,disp_CTF_RX0) terminate_stream(self,inv_CTF_2) disp_CTF_RX0 = blocks.null_source(gr.sizeof_float*total_subc) disp_CTF_RX1 = blocks.null_source(gr.sizeof_float*total_subc) ## Channel Equalizer #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") #log_to_file(self, ofdm_blocks2, "data/vec_mask2.compl") nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks,pilot_subcarriers,0 ) ##phase_tracking = ofdm.lms_phase_tracking_02( total_subc, pilot_subc, ## nondata_blocks ) ##phase_tracking2 = ofdm.lms_phase_tracking_02( total_subc, pilot_subc, ## nondata_blocks ) # self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) # self.connect( ofdm_blocks2, ( phase_tracking, 1 )) # self.connect( inv_CTF_1, ( phase_tracking, 2 ) ) # self.connect( inv_CTF_3, ( phase_tracking, 3 ) ) # self.connect( frame_start, ( phase_tracking, 4 ) ) # self.connect( frame_start2, ( phase_tracking, 5) ) # # self.connect( ofdm_blocks2, ( phase_tracking2, 0 ) ) # self.connect( ofdm_blocks, ( phase_tracking2, 1 )) # self.connect( inv_CTF_3, ( phase_tracking2, 2 ) ) # self.connect( inv_CTF_1, ( phase_tracking2, 3 ) ) # self.connect( frame_start2, ( phase_tracking2, 4 ) ) # self.connect( frame_start, ( phase_tracking2, 5 ) ) self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) self.connect( inv_CTF_1, ( phase_tracking, 1 ) ) self.connect( frame_start, ( phase_tracking, 2 ) ) #self.connect(phase_tracking,blocks.null_sink(gr.sizeof_gr_complex*total_subc)) ofdm_blocks = phase_tracking '''equalizer = ofdm.channel_equalizer_mimo_2( total_subc ) self.connect( ofdm_blocks, ( equalizer, 0 ) ) self.connect( ofdm_blocks2, ( equalizer, 1 ) ) self.connect( inv_CTF_1, ( equalizer, 2 ) ) self.connect( inv_CTF_2, ( equalizer, 3 ) ) self.connect( inv_CTF_3, ( equalizer, 4 ) ) self.connect( inv_CTF_4, ( equalizer, 5 ) ) self.connect( frame_start, ( equalizer, 6 ) ) self.connect( frame_start2, ( equalizer, 7 ) ) ofdm_blocks = equalizer''' equalizer = ofdm.channel_equalizer_mimo_2( total_subc ) self.connect( ofdm_blocks, ( equalizer, 0 ) ) self.connect( estimated_CTF_1, ( equalizer, 1 ) ) self.connect( estimated_CTF_2, ( equalizer, 2 ) ) self.connect( frame_start, ( equalizer, 3 ) ) #ofdm_blocks = equalizer #ofdm_blocks2 = equalizer2 ofdm_blocks = equalizer #log_to_file(self, equalizer,"data/equalizer.compl") log_to_file(self, ofdm_blocks,"data/equalizer.compl") log_to_file(self, estimated_CTF_1,"data/estimated_CTF_1.compl") log_to_file(self, estimated_CTF_2,"data/estimated_CTF_2.compl") ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset ''' nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) pilot_subc = block_header.pilot_tones phase_tracking = ofdm.lms_phase_tracking_02( total_subc, pilot_subc, nondata_blocks ) self.connect( equalizer, ( phase_tracking, 0 ) ) self.connect( frame_start, ( phase_tracking, 1 ) ) phase_tracking2 = ofdm.lms_phase_tracking_02( total_subc, pilot_subc, nondata_blocks ) self.connect( equalizer2, ( phase_tracking2, 0 ) ) self.connect( frame_start2, ( phase_tracking2, 1 ) ) # if options.scatter_plot_before_phase_tracking: # self.before_phase_tracking = equalizer if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) terminate_stream(self, phase_tracking2) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ofdm_blocks2 = phase_tracking2 log_to_file(self,phase_tracking, "data/phase_tracking.compl") ''' '''combine = blocks.add_cc(config.subcarriers) self.connect(ofdm_blocks, (combine,0)) self.connect(ofdm_blocks2, (combine,1)) ofdm_blocks = combine''' ## div = gr.multiply_cc(config.subcarriers) ## const = blocks.vector_source_c([[0.5+0]*config.subcarriers],True) ## self.connect(ofdm_blocks,div) ## self.connect(const,(div,1)) ## ofdm_blocks=div # log_to_file(self,combine,"data/combine.compl") ## Output connections self.connect( ofdm_blocks, out_ofdm_blocks ) self.connect( frame_start, out_frame_start ) self.connect( disp_CTF_RX0, out_disp_ctf ) self.connect( disp_CTF_RX1, out_disp_ctf2 ) else: # (2 preambles for channel estimation) chest_pre_trigger_1 = blocks.delay( gr.sizeof_char, 1 ) chest_pre_trigger_2 = blocks.delay( gr.sizeof_char, 2 ) sampled_chest_preamble_1 = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) sampled_chest_preamble_2 = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) self.connect( frame_start, chest_pre_trigger_1 ) self.connect( chest_pre_trigger_1, ( sampled_chest_preamble_1, 1 ) ) self.connect( ofdm_blocks, ( sampled_chest_preamble_1, 0 ) ) self.connect( frame_start, chest_pre_trigger_2 ) self.connect( chest_pre_trigger_2, ( sampled_chest_preamble_2, 1 ) ) self.connect( ofdm_blocks, ( sampled_chest_preamble_2, 0 ) ) ## Least Squares estimator for channel transfer function (CTF) # Taking inverse for estimating h11 (h12) inv_preamble_fd_1 = numpy.array( block_header.pilotsym_fd_1[ block_header.channel_estimation_pilot[0] ] ) print "inv_preamble_fd_1: ",inv_preamble_fd_1 #inv_preamble_fd_1 = inv_preamble_fd_1[0::2] #print "inv_preamble_fd_1 ", inv_preamble_fd_1 # Taking inverse for estimating h21 (h22) inv_preamble_fd_2 = numpy.array( block_header.pilotsym_fd_2[ block_header.channel_estimation_pilot[0]+1 ] ) print "inv_preamble_fd_2: ",inv_preamble_fd_2 #inv_preamble_fd_2 = inv_preamble_fd_2[1::2] #print "inv_preamble_fd_2 ", inv_preamble_fd_2 print "inv_preamble_fd_1: ",inv_preamble_fd_1 print "inv_preamble_fd_2: ",inv_preamble_fd_2 inv_preamble_fd_1 = 1. / inv_preamble_fd_1 inv_preamble_fd_2 = 1. / inv_preamble_fd_2 #dd = [] #for i in range (total_subc/2): # dd.extend([i*2]) skip_block_1 = ofdm.int_skip(total_subc,2,0) skip_block_11 = ofdm.int_skip(total_subc,2,0) skip_block_2 = ofdm.int_skip(total_subc,2,1) # inta_estim_1 = ofdm.interpolator(total_subc,2,dd) # inta_estim_2 = ofdm.interpolator(total_subc,2,dd) LS_channel_estimator_1 = ofdm.multiply_const_vcc( list( inv_preamble_fd_1 ) ) LS_channel_estimator_2 = ofdm.multiply_const_vcc( list( inv_preamble_fd_2 ) ) self.connect( sampled_chest_preamble_1, LS_channel_estimator_1)#,inta_estim_1 ) self.connect( sampled_chest_preamble_2, LS_channel_estimator_2)#,inta_estim_2 ) estimated_CTF_1 = LS_channel_estimator_1 # h0 estimated_CTF_2 = LS_channel_estimator_2 # h1 # h3 if not options.disable_ctf_enhancer: # if options.logcir: # ifft1 = fft_blocks.fft_vcc(total_subc,False,[],True) # self.connect( estimated_CTF, ifft1,blocks.null_sink(gr.sizeof_gr_complex*total_subc)) # summ1 = ofdm.vector_sum_vcc(total_subc) # c2m =gr.complex_to_mag(total_subc) # self.connect( estimated_CTF,summ1 ,blocks.null_sink(gr.sizeof_gr_complex)) # self.connect( estimated_CTF, c2m,blocks.null_sink(gr.sizeof_float*total_subc)) # log_to_file( self, ifft1, "data/CIR1.compl" ) # log_to_file( self, summ1, "data/CTFsumm1.compl" ) # log_to_file( self, estimated_CTF, "data/CTF1.compl" ) # log_to_file( self, c2m, "data/CTFmag1.float" ) ## MSE enhancer ctf_mse_enhancer_1 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) ctf_mse_enhancer_2 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect( estimated_CTF_1, ctf_mse_enhancer_1 ) self.connect( estimated_CTF_2, ctf_mse_enhancer_2 ) estimated_CTF_1 = ctf_mse_enhancer_1 estimated_CTF_2 = ctf_mse_enhancer_2 print "Disabled CTF MSE enhancer" ctf_postprocess_1 = ofdm.postprocess_CTF_estimate( total_subc ) self.connect( estimated_CTF_1, ( ctf_postprocess_1, 0 ) ) ctf_postprocess_2 = ofdm.postprocess_CTF_estimate( total_subc ) self.connect( estimated_CTF_2, ( ctf_postprocess_2, 0 ) ) inv_CTF_1 = ( ctf_postprocess_1, 0 ) disp_CTF_1 = ( ctf_postprocess_1, 1 ) inv_CTF_2 = ( ctf_postprocess_2, 0 ) disp_CTF_2 = ( ctf_postprocess_2, 1 ) #disp_CTF_RX0 = blocks.add_ff(total_subc) #self.connect ( disp_CTF_1, (disp_CTF_RX0, 0) ) #self.connect ( disp_CTF_2, (disp_CTF_RX0, 1) ) #terminate_stream(self,disp_CTF_RX0) terminate_stream(self,inv_CTF_2) disp_CTF_RX0 = disp_CTF_1 disp_CTF_RX1 = disp_CTF_2 ## Channel Equalizer #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") #log_to_file(self, ofdm_blocks2, "data/vec_mask2.compl") nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks,pilot_subcarriers,0 ) ##phase_tracking = ofdm.lms_phase_tracking_02( total_subc, pilot_subc, ## nondata_blocks ) ##phase_tracking2 = ofdm.lms_phase_tracking_02( total_subc, pilot_subc, ## nondata_blocks ) # self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) # self.connect( ofdm_blocks2, ( phase_tracking, 1 )) # self.connect( inv_CTF_1, ( phase_tracking, 2 ) ) # self.connect( inv_CTF_3, ( phase_tracking, 3 ) ) # self.connect( frame_start, ( phase_tracking, 4 ) ) # self.connect( frame_start2, ( phase_tracking, 5) ) # # self.connect( ofdm_blocks2, ( phase_tracking2, 0 ) ) # self.connect( ofdm_blocks, ( phase_tracking2, 1 )) # self.connect( inv_CTF_3, ( phase_tracking2, 2 ) ) # self.connect( inv_CTF_1, ( phase_tracking2, 3 ) ) # self.connect( frame_start2, ( phase_tracking2, 4 ) ) # self.connect( frame_start, ( phase_tracking2, 5 ) ) self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) self.connect( inv_CTF_1, skip_block_11, ( phase_tracking, 1 ) ) self.connect( frame_start, ( phase_tracking, 2 ) ) #self.connect(phase_tracking,blocks.null_sink(gr.sizeof_gr_complex*total_subc)) if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking '''equalizer = ofdm.channel_equalizer_mimo_2( total_subc ) self.connect( ofdm_blocks, ( equalizer, 0 ) ) self.connect( ofdm_blocks2, ( equalizer, 1 ) ) self.connect( inv_CTF_1, ( equalizer, 2 ) ) self.connect( inv_CTF_2, ( equalizer, 3 ) ) self.connect( inv_CTF_3, ( equalizer, 4 ) ) self.connect( inv_CTF_4, ( equalizer, 5 ) ) self.connect( frame_start, ( equalizer, 6 ) ) self.connect( frame_start2, ( equalizer, 7 ) ) ofdm_blocks = equalizer''' equalizer = ofdm.channel_equalizer_mimo_2( total_subc ) self.connect( ofdm_blocks, ( equalizer, 0 ) ) self.connect( estimated_CTF_1, skip_block_1, ( equalizer, 1 ) ) self.connect( estimated_CTF_2, skip_block_2, ( equalizer, 2 ) ) self.connect( frame_start, ( equalizer, 3 ) ) #ofdm_blocks = equalizer #ofdm_blocks2 = equalizer2 ofdm_blocks = equalizer #log_to_file(self, equalizer,"data/equalizer.compl") log_to_file(self, ofdm_blocks,"data/equalizer.compl") log_to_file(self, estimated_CTF_1,"data/estimated_CTF_1.compl") log_to_file(self, estimated_CTF_2,"data/estimated_CTF_2.compl") ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset ''' nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) pilot_subc = block_header.pilot_tones phase_tracking = ofdm.lms_phase_tracking_02( total_subc, pilot_subc, nondata_blocks ) self.connect( equalizer, ( phase_tracking, 0 ) ) self.connect( frame_start, ( phase_tracking, 1 ) ) phase_tracking2 = ofdm.lms_phase_tracking_02( total_subc, pilot_subc, nondata_blocks ) self.connect( equalizer2, ( phase_tracking2, 0 ) ) self.connect( frame_start2, ( phase_tracking2, 1 ) ) # if options.scatter_plot_before_phase_tracking: # self.before_phase_tracking = equalizer if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) terminate_stream(self, phase_tracking2) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ofdm_blocks2 = phase_tracking2 log_to_file(self,phase_tracking, "data/phase_tracking.compl") ''' '''combine = blocks.add_cc(config.subcarriers) self.connect(ofdm_blocks, (combine,0)) self.connect(ofdm_blocks2, (combine,1)) ofdm_blocks = combine''' ## div = gr.multiply_cc(config.subcarriers) ## const = blocks.vector_source_c([[0.5+0]*config.subcarriers],True) ## self.connect(ofdm_blocks,div) ## self.connect(const,(div,1)) ## ofdm_blocks=div # log_to_file(self,combine,"data/combine.compl") ## Output connections self.connect( ofdm_blocks, out_ofdm_blocks ) self.connect( frame_start, out_frame_start ) self.connect( disp_CTF_RX0, out_disp_ctf ) self.connect( disp_CTF_RX1, out_disp_ctf2 )
def __init__( self, options, log = False ): ## Read configuration config = station_configuration() fft_length = config.fft_length #cp_length = config.cp_length block_header = config.training_data data_subc = config.data_subcarriers virtual_subc = config.virtual_subcarriers total_subc = config.subcarriers block_length = config.block_length frame_length = config.frame_length L = block_header.mm_periodic_parts cp_length = config.cp_length print "data_subc: ", config.data_subcarriers print "total_subc: ", config.subcarriers print "frame_lengthframe_length: ", frame_length ## Set Input/Output signature gr.hier_block2.__init__( self, "fbmc_inner_receiver", gr.io_signature( 1, 1, gr.sizeof_gr_complex ), gr.io_signaturev( 4, 4, [gr.sizeof_float * total_subc, # Normalized |CTF|^2 gr.sizeof_char, # Frame start gr.sizeof_gr_complex * total_subc, # OFDM blocks, SNR est gr.sizeof_float] ) ) # CFO ## Input and output ports self.input = rx_input = self out_ofdm_blocks = ( self, 2 ) out_frame_start = ( self, 1 ) out_disp_ctf = ( self, 0 ) out_disp_cfo = ( self, 3 ) #out_snr_pream = ( self, 3 ) ## pre-FFT processing ''' ## Compute autocorrelations for S&C preamble ## and cyclic prefix self._sc_metric = sc_metric = autocorrelator( fft_length/2, fft_length/2 ) self._gi_metric = gi_metric = autocorrelator( fft_length, cp_length ) self.connect( rx_input, sc_metric ) self.connect( rx_input, gi_metric ) terminate_stream(self, gi_metric) ## Sync. Output contains OFDM blocks sync = ofdm.time_sync( fft_length/2, 1) self.connect( rx_input, ( sync, 0 ) ) self.connect( sc_metric, ( sync, 1 ) ) self.connect( sc_metric, ( sync, 2 ) ) ofdm_blocks = ( sync, 0 ) frame_start = ( sync, 1 ) log_to_file( self, ( sync, 1 ), "data/fbmc_peak_detector.char" ) ''' if options.ideal is False and options.ideal2 is False: #Testing old/new metric self.tm = schmidl.recursive_timing_metric(2*fft_length) self.connect( self.input, self.tm) #log_to_file( self, self.tm, "data/fbmc_rec_sc_metric_ofdm.float" ) timingmetric_shift = 0 #-2 #int(-cp_length * 0.8) tmfilter = filter.fft_filter_fff(1, [2./fft_length]*(fft_length/2))# ofdm.lms_fir_ff( fft_length, 1e-3 ) #; filter.fir_filter_fff(1, [1./fft_length]*fft_length) self.connect( self.tm, tmfilter ) self.tm = tmfilter #log_to_file( self, self.tm, "data/fbmc_rec_sc_metric_ofdm2.float" ) self._pd_thres = 0.6 self._pd_lookahead = fft_length # empirically chosen peak_detector = ofdm.peak_detector_02_fb(self._pd_lookahead, self._pd_thres) self.connect(self.tm, peak_detector) #log_to_file( self, peak_detector, "data/fbmc_rec_peak_detector.char" ) #frame_start = [0]*frame_length #frame_start[0] = 1 #frame_start = blocks.vector_source_b(frame_start,True) #OLD #delayed_timesync = blocks.delay(gr.sizeof_char, # (frame_length-10)*fft_length/2 - fft_length/4 -1 + timingmetric_shift) delayed_timesync = blocks.delay(gr.sizeof_char, (frame_length-10)*fft_length/2 - fft_length/4 + int(2.5*fft_length) + timingmetric_shift-1) #delayed_timesync = blocks.delay(gr.sizeof_char, #(frame_length-10)*fft_length/2 - fft_length/4 + int(3.5*fft_length) + timingmetric_shift-1) self.connect( peak_detector, delayed_timesync ) self.block_sampler = ofdm.vector_sampler(gr.sizeof_gr_complex,fft_length/2*frame_length) self.connect(self.input,self.block_sampler) self.connect(delayed_timesync,(self.block_sampler,1)) #log_to_file( self, self.block_sampler, "data/fbmc_block_sampler.compl" ) vt2s = blocks.vector_to_stream(gr.sizeof_gr_complex*fft_length, frame_length/2) self.connect(self.block_sampler,vt2s) #terminate_stream(self,ofdm_blocks) ofdm_blocks = vt2s ''' # TODO: dynamic solution vt2s = blocks.vector_to_stream(gr.sizeof_gr_complex*block_length/2, frame_length) self.connect(self.block_sampler,vt2s) terminate_stream(self,( sync, 0 )) ofdm_blocks = vt2s ''' ##stv_help = blocks.stream_to_vector(gr.sizeof_gr_complex*config.fft_length/2, 1) #stv_help = blocks.vector_to_stream(gr.sizeof_gr_complex*config.fft_length/2, 2) ##self.connect(ofdm_blocks, stv_help) ##ofdm_blocks = stv_help #ofdm_blocks = ( sync, 0 ) #frame_start = ( sync, 1 ) #log_to_file(self, frame_start, "data/frame_start.compl") #log_to_file( self, sc_metric, "data/sc_metric.float" ) #log_to_file( self, gi_metric, "data/gi_metric.float" ) #log_to_file( self, (sync,1), "data/sync.float" ) # log_to_file(self,ofdm_blocks,"data/ofdm_blocks_original.compl") frame_start = [0]*int(frame_length/2) frame_start[0] = 1 frame_start = blocks.vector_source_b(frame_start,True) #frame_start2 = [0]*int(frame_length/2) #frame_start2[0] = 1 #frame_start2 = blocks.vector_source_b(frame_start2,True) if options.disable_time_sync or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, ofdm_blocks) terminate_stream(self, frame_start) serial_to_parallel = blocks.stream_to_vector(gr.sizeof_gr_complex,fft_length) #discard_cp = ofdm.vector_mask(block_length,cp_length,fft_length,[]) #serial_to_parallel = blocks.stream_to_vector(gr.sizeof_gr_complex,block_length) #discard_cp = ofdm.vector_mask(block_length,cp_length,fft_length,[]) #self.connect( rx_input,serial_to_parallel) #self.connect( rx_input, blocks.delay(gr.sizeof_gr_complex,0),serial_to_parallel) initial_skip = blocks.skiphead(gr.sizeof_gr_complex,2*fft_length) self.connect( rx_input, initial_skip) if options.ideal is False and options.ideal2 is False: self.connect( initial_skip, serial_to_parallel) ofdm_blocks = serial_to_parallel else: ofdm_blocks = initial_skip #self.connect( rx_input, serial_to_parallel, discard_cp ) frame_start = [0]*int(frame_length/2) frame_start[0] = 1 frame_start = blocks.vector_source_b(frame_start,True) #frame_start2 = [0]*int(frame_length/2) #frame_start2[0] = 1 #frame_start2 = blocks.vector_source_b(frame_start2,True) print "Disabled time synchronization stage" print"\t\t\t\t\tframe_length = ",frame_length if options.ideal is False and options.ideal2 is False: ## Extract preamble, feed to Morelli & Mengali frequency offset estimator assert( block_header.mm_preamble_pos == 0 ) morelli_foe = ofdm.mm_frequency_estimator( fft_length, 2, 1, config.fbmc ) sampler_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length, 1 ) self.connect( ofdm_blocks, ( sampler_preamble, 0 ) ) self.connect( frame_start, blocks.delay( gr.sizeof_char, 1 ), ( sampler_preamble, 1 ) ) self.connect( sampler_preamble, morelli_foe ) freq_offset = morelli_foe print "FRAME_LENGTH: ", frame_length #log_to_file( self, sampler_preamble, "data/sampler_preamble.compl" ) #log_to_file( self, rx_input, "data/rx_input.compl" ) #log_to_file( self, ofdm_blocks, "data/rx_input.compl" ) #Extracting preamble for SNR estimation #fft_snr_est = fft_blocks.fft_vcc( fft_length, True, [], True ) #self.connect( sampler_preamble, blocks.stream_to_vector(gr.sizeof_gr_complex*fft_length/2, 2), fft_snr_est ) ## Remove virtual subcarriers #if fft_length > data_subc: #subcarrier_mask_snr_est = ofdm.vector_mask( fft_length, virtual_subc/2, # total_subc, [] ) #self.connect( fft_snr_est, subcarrier_mask_snr_est ) #fft_snr_est = subcarrier_mask_snr_est #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") ## Least Squares estimator for channel transfer function (CTF) #self.connect( fft_snr_est, out_snr_pream ) # Connecting to output ## Adaptive LMS FIR filtering of frequency offset lms_fir = ofdm.lms_fir_ff( 20, 1e-3 ) # TODO: verify parameter choice self.connect( freq_offset, lms_fir ) freq_offset = lms_fir self.connect(freq_offset, blocks.keep_one_in_n(gr.sizeof_float,20) ,out_disp_cfo) else: self.connect(blocks.vector_source_f ([1]) ,out_disp_cfo) #log_to_file(self, lms_fir, "data/lms_fir.float") if options.disable_freq_sync or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, freq_offset) freq_offset = blocks.vector_source_f([0.0],True) print "Disabled frequency synchronization stage" if options.ideal is False and options.ideal2 is False: ## Correct frequency shift, feed-forward structure frequency_shift = ofdm.frequency_shift_vcc( fft_length, -1.0/fft_length, 0) #freq_shift = blocks.multiply_cc() #norm_freq = -0.1 / config.fft_length #freq_off = self.freq_off_src = analog.sig_source_c(1.0, analog.GR_SIN_WAVE, norm_freq, 1.0, 0.0 ) self.connect( ofdm_blocks, ( frequency_shift, 0 ) ) self.connect( freq_offset, ( frequency_shift, 1 ) ) self.connect( frame_start,blocks.delay( gr.sizeof_char, 0), ( frequency_shift, 2 ) ) #self.connect(frequency_shift,s2help) #ofdm_blocks = s2help ofdm_blocks = frequency_shift #terminate_stream(self, frequency_shift) #inner_pb_filt = self._inner_pilot_block_filter = fbmc_inner_pilot_block_filter() #self.connect(ofdm_blocks,inner_pb_filt) #self.connect(frame_start,(inner_pb_filt,1)) #self.connect((inner_pb_filt,1),blocks.null_sink(gr.sizeof_char)) #ofdm_blocks = (inner_pb_filt,0) overlap_ser_to_par = ofdm.fbmc_overlapping_serial_to_parallel_cvc(fft_length) self.separate_vcvc = ofdm.fbmc_separate_vcvc(fft_length, 2) self.polyphase_network_vcvc_1 = ofdm.fbmc_polyphase_network_vcvc(fft_length, 4, 4*fft_length-1, True) self.polyphase_network_vcvc_2 = ofdm.fbmc_polyphase_network_vcvc(fft_length, 4, 4*fft_length-1, True) self.junction_vcvc = ofdm.fbmc_junction_vcvc(fft_length, 2) self.fft_fbmc = fft_blocks.fft_vcc(fft_length, True, [], True) print "config.training_data.fbmc_no_preambles: ", config.training_data.fbmc_no_preambles #center_preamble = [1, -1j, -1, 1j] #self.preamble = preamble = [0]*total_subc + center_preamble*((int)(total_subc/len(center_preamble)))+[0]*total_subc self.preamble = preamble = config.training_data.fbmc_pilotsym_fd_list #inv_preamble = 1. / numpy.array(self.preamble) #print "self.preamble: ", len(self.preamble #print "inv_preamble: ", list(inv_preamble) #print "self.preamble", self.preamble #print "self.preamble2", self.preamble2 self.multiply_const= ofdm.multiply_const_vcc(([1.0/(math.sqrt(fft_length*0.6863))]*total_subc)) self.beta_multiplier_vcvc = ofdm.fbmc_beta_multiplier_vcvc(total_subc, 4, 4*fft_length-1, 0) #self.skiphead = blocks.skiphead(gr.sizeof_gr_complex*total_subc, 2*4-1-1) self.skiphead = blocks.skiphead(gr.sizeof_gr_complex*total_subc,2) self.skiphead_1 = blocks.skiphead(gr.sizeof_gr_complex*total_subc, 0) #self.remove_preamble_vcvc = ofdm.fbmc_remove_preamble_vcvc(total_subc, config.frame_data_part/2, config.training_data.fbmc_no_preambles*total_subc/2) #self.subchannel_processing_vcvc = ofdm.fbmc_subchannel_processing_vcvc(total_subc, config.frame_data_part, 1, 2, 1, 0) self.oqam_postprocessing_vcvc = ofdm.fbmc_oqam_postprocessing_vcvc(total_subc, 0, 0) #log_to_file( self, ofdm_blocks, "data/PRE_FBMC.compl" ) #log_to_file( self, self.fft_fbmc, "data/FFT_FBMC.compl" ) if options.ideal is False and options.ideal2 is False: self.subchannel_processing_vcvc = ofdm.fbmc_subchannel_processing_vcvc(total_subc, config.frame_data_part, 3, 2, 1, 0) help2 = blocks.keep_one_in_n(gr.sizeof_gr_complex*total_subc,frame_length) self.connect ((self.subchannel_processing_vcvc,1),help2) #log_to_file( self, self.subchannel_processing_vcvc, "data/fbmc_subc.compl" ) #terminate_stream(self, help2) if options.ideal is False and options.ideal2 is False: self.connect(ofdm_blocks, blocks.vector_to_stream(gr.sizeof_gr_complex, fft_length),overlap_ser_to_par) else: self.connect(ofdm_blocks,overlap_ser_to_par) self.connect(overlap_ser_to_par, self.separate_vcvc) self.connect((self.separate_vcvc, 1), (self.polyphase_network_vcvc_2, 0)) self.connect((self.separate_vcvc, 0), (self.polyphase_network_vcvc_1, 0)) self.connect((self.polyphase_network_vcvc_1, 0), (self.junction_vcvc, 0)) self.connect((self.polyphase_network_vcvc_2, 0), (self.junction_vcvc, 1)) self.connect(self.junction_vcvc, self.fft_fbmc) ofdm_blocks = self.fft_fbmc print "config.dc_null: ", config.dc_null if fft_length > data_subc: subcarrier_mask_fbmc = ofdm.vector_mask_dc_null( fft_length, virtual_subc/2, total_subc, config.dc_null, [] ) self.connect( ofdm_blocks, subcarrier_mask_fbmc ) ofdm_blocks = subcarrier_mask_fbmc #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") ## Least Squares estimator for channel transfer function (CTF) #log_to_file( self, subcarrier_mask, "data/OFDM_Blocks.compl" ) self.connect(ofdm_blocks, self.beta_multiplier_vcvc) ofdm_blocks = self.beta_multiplier_vcvc #self.connect(ofdm_blocks,self.multiply_const) #self.connect(self.multiply_const, (self.skiphead, 0)) self.connect(ofdm_blocks, (self.skiphead, 0)) #log_to_file( self, self.skiphead, "data/fbmc_skiphead_4.compl" ) #self.connect(ofdm_blocks, self.multiply_const) #self.connect(self.multiply_const, self.beta_multiplier_vcvc) #self.connect((self.beta_multiplier_vcvc, 0), (self.skiphead, 0)) if options.ideal or options.ideal2: self.connect((self.skiphead, 0),(self.skiphead_1, 0)) else: self.connect((self.skiphead, 0), (self.subchannel_processing_vcvc, 0)) self.connect((self.subchannel_processing_vcvc, 0), (self.skiphead_1, 0)) #log_to_file( self, self.skiphead, "data/fbmc_subc.compl" ) #self.connect((self.skiphead_1, 0),(self.remove_preamble_vcvc, 0)) #self.connect((self.remove_preamble_vcvc, 0), (self.oqam_postprocessing_vcvc, 0)) #ofdm_blocks = self.oqam_postprocessing_vcvc #log_to_file( self, self.subchannel_processing_vcvc, "data/subc_0.compl" ) #log_to_file( self, (self.subchannel_processing_vcvc,1), "data/subc_1.compl" ) self.connect((self.skiphead_1, 0),(self.oqam_postprocessing_vcvc, 0)) #self.connect((self.oqam_postprocessing_vcvc, 0), (self.remove_preamble_vcvc, 0) ) ofdm_blocks = (self.oqam_postprocessing_vcvc, 0)#(self.remove_preamble_vcvc, 0) #log_to_file( self, (self.oqam_postprocessing_vcvc, 0), "data/fbmc_before_remove.compl" ) #log_to_file( self, self.skiphead, "data/SKIP_HEAD_FBMC.compl" ) #log_to_file( self, self.beta_multiplier_vcvc, "data/BETA_REC_FBMC.compl" ) #log_to_file( self, self.oqam_postprocessing_vcvc, "data/REC_OUT_FBMC.compl" ) """ DISABLED OFDM CHANNEL ESTIMATION PREMBLE -> CORRECT LATER to compare FBMC and OFDM channel estimation #TAKING THE CHANNEL ESTIMATION PREAMBLE chest_pre_trigger = blocks.delay( gr.sizeof_char, 3 ) sampled_chest_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length/2, 2 ) self.connect( frame_start, chest_pre_trigger ) self.connect( chest_pre_trigger, ( sampled_chest_preamble, 1 ) ) self.connect( frequency_shift, ( sampled_chest_preamble, 0 ) ) #ofdm_blocks = sampled_chest_preamble ## FFT fft = fft_blocks.fft_vcc( fft_length, True, [], True ) self.connect( sampled_chest_preamble, fft ) ofdm_blocks_est = fft log_to_file( self, sampled_chest_preamble, "data/SAMPLED_EST_PREAMBLE.compl" ) log_to_file( self, ofdm_blocks_est, "data/FFT.compl" ) ## Remove virtual subcarriers if fft_length > data_subc: subcarrier_mask = ofdm.vector_mask( fft_length, virtual_subc/2, total_subc, [] ) self.connect( ofdm_blocks_est, subcarrier_mask ) ofdm_blocks_est = subcarrier_mask #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") ## Least Squares estimator for channel transfer function (CTF) log_to_file( self, subcarrier_mask, "data/OFDM_Blocks.compl" ) ## post-FFT processing ## extract channel estimation preamble from frame ##chest_pre_trigger = blocks.delay( gr.sizeof_char, ##1 ) ##sampled_chest_preamble = \ ## ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) ##self.connect( frame_start, chest_pre_trigger ) ##self.connect( chest_pre_trigger, ( sampled_chest_preamble, 1 ) ) ##self.connect( ofdm_blocks, ( sampled_chest_preamble, 0 ) ) ## Least Squares estimator for channel transfer function (CTF) inv_preamble_fd = numpy.array( block_header.pilotsym_fd[ block_header.channel_estimation_pilot[0] ] ) #print "Channel estimation pilot: ", inv_preamble_fd inv_preamble_fd = 1. / inv_preamble_fd LS_channel_estimator = ofdm.multiply_const_vcc( list( inv_preamble_fd ) ) self.connect( ofdm_blocks_est, LS_channel_estimator ) estimated_CTF = LS_channel_estimator terminate_stream(self,estimated_CTF) """ if options.ideal is False and options.ideal2 is False: if options.logcir: log_to_file( self, sampled_chest_preamble, "data/PREAM.compl" ) if not options.disable_ctf_enhancer: if options.logcir: ifft1 = fft_blocks.fft_vcc(total_subc,False,[],True) self.connect( estimated_CTF, ifft1,gr.null_sink(gr.sizeof_gr_complex*total_subc)) summ1 = ofdm.vector_sum_vcc(total_subc) c2m =gr.complex_to_mag(total_subc) self.connect( estimated_CTF,summ1 ,gr.null_sink(gr.sizeof_gr_complex)) self.connect( estimated_CTF, c2m,gr.null_sink(gr.sizeof_float*total_subc)) log_to_file( self, ifft1, "data/CIR1.compl" ) log_to_file( self, summ1, "data/CTFsumm1.compl" ) log_to_file( self, estimated_CTF, "data/CTF1.compl" ) log_to_file( self, c2m, "data/CTFmag1.float" ) ## MSE enhancer ctf_mse_enhancer = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect( estimated_CTF, ctf_mse_enhancer ) # log_to_file( self, ctf_mse_enhancer, "data/ctf_mse_enhancer_original.compl") #ifft3 = fft_blocks.fft_vcc(total_subc,False,[],True) #null_noise = ofdm.noise_nulling(total_subc, cp_length + cp_length) #ctf_mse_enhancer = fft_blocks.fft_vcc(total_subc,True,[],True) #ctf_mse_enhancer = ofdm.vector_mask( fft_length, virtual_subc/2, # total_subc, [] ) #self.connect( estimated_CTF, ifft3,null_noise,ctf_mse_enhancer ) estimated_CTF = ctf_mse_enhancer print "Disabled CTF MSE enhancer" if options.logcir: ifft2 = fft_blocks.fft_vcc(total_subc,False,[],True) self.connect( estimated_CTF, ifft2,gr.null_sink(gr.sizeof_gr_complex*total_subc)) summ2 = ofdm.vector_sum_vcc(total_subc) c2m2 =gr.complex_to_mag(total_subc) self.connect( estimated_CTF,summ2 ,gr.null_sink(gr.sizeof_gr_complex)) self.connect( estimated_CTF, c2m2,gr.null_sink(gr.sizeof_float*total_subc)) log_to_file( self, ifft2, "data/CIR2.compl" ) log_to_file( self, summ2, "data/CTFsumm2.compl" ) log_to_file( self, estimated_CTF, "data/CTF2.compl" ) log_to_file( self, c2m2, "data/CTFmag2.float" ) ## Postprocess the CTF estimate ## CTF -> inverse CTF (for equalizer) ## CTF -> norm |.|^2 (for CTF display) ctf_postprocess = ofdm.fbmc_postprocess_CTF_estimate( total_subc ) self.connect( help2, ctf_postprocess ) #estimated_SNR = ( ctf_postprocess, 0 ) disp_CTF = ( ctf_postprocess, 0 ) #self.connect(estimated_SNR,out_snr_pream) #log_to_file( self, estimated_SNR, "data/fbmc_SNR.float" ) #Disable measured SNR output #terminate_stream(self, estimated_SNR) #self.connect(blocks.vector_source_f([10.0],True) ,out_snr_pream) # if options.disable_equalization or options.ideal: # terminate_stream(self, inv_estimated_CTF) # inv_estimated_CTF_vec = blocks.vector_source_c([1.0/fft_length*math.sqrt(total_subc)]*total_subc,True,total_subc) # inv_estimated_CTF_str = blocks.vector_to_stream(gr.sizeof_gr_complex, total_subc) # self.inv_estimated_CTF_mul = ofdm.multiply_const_ccf( 1.0/config.rms_amplitude ) # #inv_estimated_CTF_mul.set_k(1.0/config.rms_amplitude) # inv_estimated_CTF = blocks.stream_to_vector(gr.sizeof_gr_complex, total_subc) # self.connect( inv_estimated_CTF_vec, inv_estimated_CTF_str, self.inv_estimated_CTF_mul, inv_estimated_CTF) # print "Disabled equalization stage" ''' ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) print"\t\t\t\t\tnondata_blocks=",nondata_blocks pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers,0 ) self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) self.connect( inv_estimated_CTF, ( phase_tracking, 1 ) ) self.connect( frame_start, ( phase_tracking, 2 ) ) ## if options.scatter_plot_before_phase_tracking: self.before_phase_tracking = equalizer if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ''' ## Channel Equalizer ##equalizer = ofdm.channel_equalizer( total_subc ) ##self.connect( ofdm_blocks, ( equalizer, 0 ) ) ##self.connect( inv_estimated_CTF, ( equalizer, 1 ) ) ##self.connect( frame_start, ( equalizer, 2 ) ) ##ofdm_blocks = equalizer #log_to_file(self, equalizer,"data/equalizer_siso.compl") #log_to_file(self, ofdm_blocks, "data/equalizer.compl") ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) print"\t\t\t\t\tnondata_blocks=",nondata_blocks pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers if options.scatter_plot_before_phase_tracking: self.before_phase_tracking = equalizer ## Output connections self.connect( ofdm_blocks, out_ofdm_blocks ) self.connect( frame_start, out_frame_start ) if options.ideal is False and options.ideal2 is False: self.connect( disp_CTF, out_disp_ctf ) else: self.connect( blocks.vector_source_f([1.0]*total_subc),blocks.stream_to_vector(gr.sizeof_float,total_subc), out_disp_ctf ) if log: log_to_file( self, sc_metric, "data/sc_metric.float" ) log_to_file( self, gi_metric, "data/gi_metric.float" ) log_to_file( self, morelli_foe, "data/morelli_foe.float" ) log_to_file( self, lms_fir, "data/lms_fir.float" ) log_to_file( self, sampler_preamble, "data/preamble.compl" ) log_to_file( self, sync, "data/sync.compl" ) log_to_file( self, frequency_shift, "data/frequency_shift.compl" ) log_to_file( self, fft, "data/fft.compl") log_to_file( self, fft, "data/fft.float", mag=True ) if vars().has_key( 'subcarrier_mask' ): log_to_file( self, subcarrier_mask, "data/subcarrier_mask.compl" ) log_to_file( self, ofdm_blocks, "data/ofdm_blocks_out.compl" ) log_to_file( self, frame_start, "data/frame_start.float", char_to_float=True ) log_to_file( self, sampled_chest_preamble, "data/sampled_chest_preamble.compl" ) log_to_file( self, LS_channel_estimator, "data/ls_channel_estimator.compl" ) log_to_file( self, LS_channel_estimator, "data/ls_channel_estimator.float", mag=True ) if "ctf_mse_enhancer" in locals(): log_to_file( self, ctf_mse_enhancer, "data/ctf_mse_enhancer.compl" ) log_to_file( self, ctf_mse_enhancer, "data/ctf_mse_enhancer.float", mag=True ) log_to_file( self, (ctf_postprocess,0), "data/inc_estimated_ctf.compl" ) log_to_file( self, (ctf_postprocess,1), "data/disp_ctf.float" ) log_to_file( self, equalizer, "data/equalizer.compl" ) log_to_file( self, equalizer, "data/equalizer.float", mag=True ) log_to_file( self, phase_tracking, "data/phase_tracking.compl" )
def __init__(self, options): gr.hier_block2.__init__(self, "fbmc_receive_path", gr.io_signature(1,1,gr.sizeof_gr_complex), gr.io_signature(0,0,0)) print "This is FBMC receive path 1x1" common_options.defaults(options) config = self.config = station_configuration() config.data_subcarriers = dsubc = options.subcarriers config.cp_length = 0 config.frame_data_blocks = options.data_blocks config._verbose = options.verbose #TODO: update config.fft_length = options.fft_length config.dc_null = options.dc_null config.training_data = default_block_header(dsubc, config.fft_length,config.dc_null,options) config.coding = options.coding config.ber_window = options.ber_window config.periodic_parts = 8 config.frame_id_blocks = 1 # FIXME self._options = copy.copy(options) #FIXME: do we need this? config.fbmc = options.fbmc config.block_length = config.fft_length + config.cp_length config.frame_data_part = config.frame_data_blocks + config.frame_id_blocks config.frame_length = config.training_data.fbmc_no_preambles + 2*config.frame_data_part config.postpro_frame_length = config.frame_data_part + \ config.training_data.no_pilotsyms config.subcarriers = dsubc + \ config.training_data.pilot_subcarriers config.virtual_subcarriers = config.fft_length - config.subcarriers - config.dc_null total_subc = config.subcarriers # check some bounds if config.fft_length < config.subcarriers: raise SystemError, "Subcarrier number must be less than FFT length" if config.fft_length < config.cp_length: raise SystemError, "Cyclic prefix length must be less than FFT length" #self.input = gr.kludge_copy(gr.sizeof_gr_complex) #self.connect( self, self.input ) self.input = self self.ideal = options.ideal self.ideal2 = options.ideal2 ## Inner receiver ## Timing & Frequency Synchronization ## Channel estimation + Equalization ## Phase Tracking for sampling clock frequency offset correction inner_receiver = self.inner_receiver = fbmc_inner_receiver( options, options.log ) self.connect( self.input, inner_receiver ) ofdm_blocks = ( inner_receiver, 2 ) frame_start = ( inner_receiver, 1 ) disp_ctf = ( inner_receiver, 0 ) #self.snr_est_preamble = ( inner_receiver, 3 ) #terminate_stream(self,snr_est_preamble) disp_cfo = ( inner_receiver, 3 ) if self.ideal is False and self.ideal2 is False: self.zmq_probe_freqoff = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5557") self.connect(disp_cfo, self.zmq_probe_freqoff) else: self.connect(disp_cfo, blocks.null_sink(gr.sizeof_float)) # for ID decoder used_id_bits = config.used_id_bits = 8 #TODO: constant in source code! rep_id_bits = config.rep_id_bits = dsubc/used_id_bits #BPSK if options.log: print "rep_id_bits %d" % (rep_id_bits) if dsubc % used_id_bits <> 0: raise SystemError,"Data subcarriers need to be multiple of 10" ## Workaround to avoid periodic structure seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] ## NOTE!!! BIG HACK!!! ## first preamble ain't equalized .... ## for Milan's SNR estimator ## Outer Receiver ## Make new inner receiver compatible with old outer receiver ## FIXME: renew outer receiver self.ctf = disp_ctf #frame_sampler = ofdm_frame_sampler(options) frame_sampler = fbmc_frame_sampler(options) self.connect( ofdm_blocks, frame_sampler) self.connect( frame_start, (frame_sampler,1) ) # # ft = [0] * config.frame_length # ft[0] = 1 # # # The next block ensures that only complete frames find their way into # # the old outer receiver. The dynamic frame start trigger is hence # # replaced with a static one, fixed to the frame length. # # frame_sampler = ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, # config.frame_length ) # self.symbol_output = blocks.vector_to_stream( gr.sizeof_gr_complex * total_subc, # config.frame_length ) # delayed_frame_start = blocks.delay( gr.sizeof_char, config.frame_length - 1 ) # damn_static_frame_trigger = blocks.vector_source_b( ft, True ) # # if options.enable_erasure_decision: # frame_gate = vector_sampler( # gr.sizeof_gr_complex * total_subc * config.frame_length, 1 ) # self.connect( ofdm_blocks, frame_sampler, frame_gate, # self.symbol_output ) # else: # self.connect( ofdm_blocks, frame_sampler, self.symbol_output ) # # self.connect( frame_start, delayed_frame_start, ( frame_sampler, 1 ) ) if options.enable_erasure_decision: frame_gate = frame_sampler.frame_gate self.symbol_output = frame_sampler orig_frame_start = frame_start frame_start = (frame_sampler,1) self.frame_trigger = frame_start #terminate_stream(self, self.frame_trigger) ## Pilot block filter pb_filt = self._pilot_block_filter = fbmc_pilot_block_filter() self.connect(self.symbol_output,pb_filt) self.connect(self.frame_trigger,(pb_filt,1)) self.frame_data_trigger = (pb_filt,1) #self.symbol_output = pb_filt #if options.log: #log_to_file(self, pb_filt, "data/pb_filt_out.compl") if config.fbmc: pda_in = pb_filt else: ## Pilot subcarrier filter ps_filt = self._pilot_subcarrier_filter = pilot_subcarrier_filter() self.connect(self.symbol_output,ps_filt) if options.log: log_to_file(self, ps_filt, "data/ps_filt_out.compl") pda_in = ps_filt ## Workaround to avoid periodic structure # for ID decoder seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] if not options.enable_erasure_decision: ## ID Block Filter # Filter ID block, skip data blocks id_bfilt = self._id_block_filter = vector_sampler( gr.sizeof_gr_complex * dsubc, 1 ) if not config.frame_id_blocks == 1: raise SystemExit, "# ID Blocks > 1 not supported" self.connect( pda_in , id_bfilt ) self.connect( self.frame_data_trigger, ( id_bfilt, 1 ) ) # trigger #log_to_file( self, id_bfilt, "data/id_bfilt.compl" ) ## ID Demapper and Decoder, soft decision self.id_dec = self._id_decoder = ofdm.coded_bpsk_soft_decoder( dsubc, used_id_bits, whitener_pn ) self.connect( id_bfilt, self.id_dec ) print "Using coded BPSK soft decoder for ID detection" else: # options.enable_erasure_decision: id_bfilt = self._id_block_filter = vector_sampler( gr.sizeof_gr_complex * total_subc, config.frame_id_blocks ) id_bfilt_trig_delay = 0 for x in range( config.frame_length ): if x in config.training_data.pilotsym_pos: id_bfilt_trig_delay += 1 else: break print "Position of ID block within complete frame: %d" %(id_bfilt_trig_delay) assert( id_bfilt_trig_delay > 0 ) # else not supported id_bfilt_trig = blocks.delay( gr.sizeof_char, id_bfilt_trig_delay ) self.connect( ofdm_blocks, id_bfilt ) self.connect( orig_frame_start, id_bfilt_trig, ( id_bfilt, 1 ) ) self.id_dec = self._id_decoder = ofdm.coded_bpsk_soft_decoder( total_subc, used_id_bits, whitener_pn, config.training_data.shifted_pilot_tones ) self.connect( id_bfilt, self.id_dec ) print "Using coded BPSK soft decoder for ID detection" # The threshold block either returns 1.0 if the llr-value from the # id decoder is below the threshold, else 0.0. Hence we convert this # into chars, 0 and 1, and use it as trigger for the sampler. min_llr = ( self.id_dec, 1 ) erasure_threshold = gr.threshold_ff( 10.0, 10.0, 0 ) # FIXME is it the optimal threshold? erasure_dec = gr.float_to_char() id_gate = vector_sampler( gr.sizeof_short, 1 ) ctf_gate = vector_sampler( gr.sizeof_float * total_subc, 1 ) self.connect( self.id_dec , id_gate ) self.connect( self.ctf, ctf_gate ) self.connect( min_llr, erasure_threshold, erasure_dec ) self.connect( erasure_dec, ( frame_gate, 1 ) ) self.connect( erasure_dec, ( id_gate, 1 ) ) self.connect( erasure_dec, ( ctf_gate, 1 ) ) self.id_dec = self._id_decoder = id_gate self.ctf = ctf_gate print "Erasure decision for IDs is enabled" if options.log: id_dec_f = gr.short_to_float() self.connect(self.id_dec,id_dec_f) log_to_file(self, id_dec_f, "data/id_dec_out.float") if options.log: log_to_file(self, id_bfilt, "data/id_blockfilter_out.compl") # TODO: refactor names if options.log: map_src_f = gr.char_to_float(dsubc) self.connect(map_src,map_src_f) log_to_file(self, map_src_f, "data/map_src_out.float") ## Allocation Control if options.static_allocation: #DEBUG if options.coding: mode = 1 # Coding mode 1-9 bitspermode = [0.5,1,1.5,2,3,4,4.5,5,6] # Information bits per mode bitcount_vec = [(int)(config.data_subcarriers*config.frame_data_blocks*bitspermode[mode-1])] bitloading = mode else: bitloading = 1 bitcount_vec = [config.data_subcarriers*config.frame_data_blocks*bitloading] #bitcount_vec = [config.data_subcarriers*config.frame_data_blocks] self.bitcount_src = blocks.vector_source_i(bitcount_vec,True,1) # 0s for ID block, then data #bitloading_vec = [0]*dsubc+[0]*(dsubc/2)+[2]*(dsubc/2) bitloading_vec = [0]*dsubc+[bitloading]*dsubc bitloading_src = blocks.vector_source_b(bitloading_vec,True,dsubc) power_vec = [1]*config.data_subcarriers power_src = blocks.vector_source_f(power_vec,True,dsubc) else: self.allocation_buffer = ofdm.allocation_buffer(config.data_subcarriers, config.frame_data_blocks, "tcp://"+options.tx_hostname+":3333",config.coding) self.bitcount_src = (self.allocation_buffer,0) bitloading_src = (self.allocation_buffer,1) power_src = (self.allocation_buffer,2) self.connect(self.id_dec, self.allocation_buffer) if options.benchmarking: self.allocation_buffer.set_allocation([4]*config.data_subcarriers,[1]*config.data_subcarriers) if options.log: log_to_file(self, self.bitcount_src, "data/bitcount_src_rx.int") log_to_file(self, bitloading_src, "data/bitloading_src_rx.char") log_to_file(self, power_src, "data/power_src_rx.cmplx") log_to_file(self, self.id_dec, "data/id_dec_rx.short") ## Power Deallocator pda = self._power_deallocator = multiply_frame_fc(config.frame_data_part, dsubc) self.connect(pda_in,(pda,0)) self.connect(power_src,(pda,1)) ## Demodulator # if 0: # ac_vector = [0.0+0.0j]*208 # ac_vector[0] = (2*10**(-0.452)) # ac_vector[3] = (10**(-0.651)) # ac_vector[7] = (10**(-1.151)) # csi_vector_inv=abs(numpy.fft.fft(numpy.sqrt(ac_vector)))**2 # dm_csi = numpy.fft.fftshift(csi_vector_inv) # TODO dm_csi = [1]*dsubc # TODO dm_csi = blocks.vector_source_f(dm_csi,True) ## Depuncturer dp_trig = [0]*(config.frame_data_blocks/2) dp_trig[0] = 1 dp_trig = blocks.vector_source_b(dp_trig,True) # TODO if(options.coding): fo=ofdm.fsm(1,2,[91,121]) if options.interleave: int_object=trellis.interleaver(2000,666) deinterlv = trellis.permutation(int_object.K(),int_object.DEINTER(),1,gr.sizeof_float) demod = self._data_demodulator = generic_softdemapper_vcf(dsubc, config.frame_data_part, config.coding) #self.connect(dm_csi,blocks.stream_to_vector(gr.sizeof_float,dsubc),(demod,2)) if(options.ideal): self.connect(dm_csi,blocks.stream_to_vector(gr.sizeof_float,dsubc),(demod,2)) else: dm_csi_filter = self.dm_csi_filter = filter.single_pole_iir_filter_ff(0.01,dsubc) self.connect(self.ctf, self.dm_csi_filter,(demod,2)) #log_to_file(self, dm_csi_filter, "data/softs_csi.float") #self.connect(dm_trig,(demod,3)) else: demod = self._data_demodulator = generic_demapper_vcb(dsubc, config.frame_data_part) if options.benchmarking: # Do receiver benchmarking until the number of frames x symbols are collected self.connect(pda,blocks.head(gr.sizeof_gr_complex*dsubc, options.N*config.frame_data_blocks),demod) else: self.connect(pda,demod) self.connect(bitloading_src,(demod,1)) if(options.coding): ## Depuncturing if not options.nopunct: depuncturing = depuncture_ff(dsubc,0) frametrigger_bitmap_filter = blocks.vector_source_b([1,0],True) self.connect(bitloading_src,(depuncturing,1)) self.connect(dp_trig,(depuncturing,2)) ## Decoding chunkdivisor = int(numpy.ceil(config.frame_data_blocks/5.0)) print "Number of chunks at Viterbi decoder: ", chunkdivisor decoding = self._data_decoder = ofdm.viterbi_combined_fb(fo,dsubc,-1,-1,2,chunkdivisor,[-1,-1,-1,1,1,-1,1,1],ofdm.TRELLIS_EUCLIDEAN) if options.log and options.coding: log_to_file(self, decoding, "data/decoded.char") if not options.nopunct: log_to_file(self, depuncturing, "data/vit_in.float") if not options.nopunct: if options.interleave: self.connect(demod,deinterlv,depuncturing,decoding) else: self.connect(demod,depuncturing,decoding) else: self.connect(demod,decoding) self.connect(self.bitcount_src, multiply_const_ii(1./chunkdivisor), (decoding,1)) if options.scatterplot or options.scatter_plot_before_phase_tracking: if self.ideal2 is False: scatter_vec_elem = self._scatter_vec_elem = ofdm.vector_element(dsubc,40) scatter_s2v = self._scatter_s2v = blocks.stream_to_vector(gr.sizeof_gr_complex,config.frame_data_blocks) scatter_id_filt = skip(gr.sizeof_gr_complex*dsubc,config.frame_data_blocks) scatter_id_filt.skip_call(0) scatter_trig = [0]*config.frame_data_part scatter_trig[0] = 1 scatter_trig = blocks.vector_source_b(scatter_trig,True) self.connect(scatter_trig,(scatter_id_filt,1)) self.connect(scatter_vec_elem,scatter_s2v) if not options.scatter_plot_before_phase_tracking: print "Enabling Scatterplot for data subcarriers" self.connect(pda,scatter_id_filt,scatter_vec_elem) # Work on this #scatter_sink = ofdm.scatterplot_sink(dsubc) #self.connect(pda,scatter_sink) #self.connect(map_src,(scatter_sink,1)) #self.connect(dm_trig,(scatter_sink,2)) #print "Enabled scatterplot gui interface" self.zmq_probe_scatter = zeromq.pub_sink(gr.sizeof_gr_complex,config.frame_data_blocks, "tcp://*:5560") self.connect(scatter_s2v, blocks.keep_one_in_n(gr.sizeof_gr_complex*config.frame_data_blocks,20), self.zmq_probe_scatter) else: print "Enabling Scatterplot for data before phase tracking" inner_rx = inner_receiver.before_phase_tracking #scatter_sink2 = ofdm.scatterplot_sink(dsubc,"phase_tracking") op = copy.copy(options) op.enable_erasure_decision = False new_framesampler = ofdm_frame_sampler(op) self.connect( inner_rx, new_framesampler ) self.connect( orig_frame_start, (new_framesampler,1) ) new_ps_filter = pilot_subcarrier_filter() new_pb_filter = fbmc_pilot_block_filter() self.connect( (new_framesampler,1), (new_pb_filter,1) ) self.connect( new_framesampler, new_pb_filter, new_ps_filter, scatter_id_filt, scatter_vec_elem ) #self.connect( new_ps_filter, scatter_sink2 ) #self.connect( map_src, (scatter_sink2,1)) #self.connect( dm_trig, (scatter_sink2,2)) if options.log: if(options.coding): log_to_file(self, demod, "data/data_stream_out.float") else: data_f = gr.char_to_float() self.connect(demod,data_f) log_to_file(self, data_f, "data/data_stream_out.float") if options.sfo_feedback: used_id_bits = 8 rep_id_bits = config.data_subcarriers/used_id_bits seed(1) whitener_pn = [randint(0,1) for i in range(used_id_bits*rep_id_bits)] id_enc = ofdm.repetition_encoder_sb(used_id_bits,rep_id_bits,whitener_pn) self.connect( self.id_dec, id_enc ) id_mod = ofdm_bpsk_modulator(dsubc) self.connect( id_enc, id_mod ) id_mod_conj = gr.conjugate_cc(dsubc) self.connect( id_mod, id_mod_conj ) id_mult = blocks.multiply_vcc(dsubc) self.connect( id_bfilt, ( id_mult,0) ) self.connect( id_mod_conj, ( id_mult,1) ) # id_mult_avg = filter.single_pole_iir_filter_cc(0.01,dsubc) # self.connect( id_mult, id_mult_avg ) id_phase = gr.complex_to_arg(dsubc) self.connect( id_mult, id_phase ) log_to_file( self, id_phase, "data/id_phase.float" ) est=ofdm.LS_estimator_straight_slope(dsubc) self.connect(id_phase,est) slope=blocks.multiply_const_ff(1e6/2/3.14159265) self.connect( (est,0), slope ) log_to_file( self, slope, "data/slope.float" ) log_to_file( self, (est,1), "data/offset.float" ) # ------------------------------------------------------------------------ # # Display some information about the setup if config._verbose: self._print_verbage() ## debug logging ## if options.log: # log_to_file(self,self.ofdm_symbols,"data/unequalized_rx_ofdm_symbols.compl") # log_to_file(self,self.ofdm_symbols,"data/unequalized_rx_ofdm_symbols.float",mag=True) fftlen = 256 my_window = window.hamming(fftlen) #.blackmanharris(fftlen) rxs_sampler = vector_sampler(gr.sizeof_gr_complex,fftlen) rxs_sampler_vect = concatenate([[1],[0]*49]) rxs_trigger = blocks.vector_source_b(rxs_sampler_vect.tolist(),True) rxs_window = blocks.multiply_const_vcc(my_window) rxs_spectrum = gr.fft_vcc(fftlen,True,[],True) rxs_mag = gr.complex_to_mag(fftlen) rxs_avg = filter.single_pole_iir_filter_ff(0.01,fftlen) #rxs_logdb = blocks.nlog10_ff(20.0,fftlen,-20*log10(fftlen)) rxs_logdb = gr.kludge_copy( gr.sizeof_float * fftlen ) rxs_decimate_rate = gr.keep_one_in_n(gr.sizeof_float*fftlen,1) self.connect(rxs_trigger,(rxs_sampler,1)) self.connect(self.input,rxs_sampler,rxs_window, rxs_spectrum,rxs_mag,rxs_avg,rxs_logdb, rxs_decimate_rate) log_to_file( self, rxs_decimate_rate, "data/psd_input.float" ) #output branches self.publish_rx_performance_measure()
def __init__(self, options, log=False): ## Read configuration config = station_configuration() fft_length = config.fft_length cp_length = config.cp_length block_header = config.training_data data_subc = config.data_subcarriers virtual_subc = config.virtual_subcarriers total_subc = config.subcarriers block_length = config.block_length frame_length = config.frame_length L = block_header.mm_periodic_parts frame_data_blocks = options.data_blocks ## Set Input/Output signature gr.hier_block2.__init__( self, "ofdm_inner_receiver", gr.io_signature(2, 2, gr.sizeof_gr_complex), gr.io_signature5( 5, 5, gr.sizeof_gr_complex * total_subc, # OFDM blocks gr.sizeof_char, # Frame start gr.sizeof_float * total_subc, # Normalized |CTF|^2 gr.sizeof_char, # Frame start gr.sizeof_float * total_subc)) # Normalized |CTF|^2 ## Input and output ports self.input = rx_input = (self, 0) self.input2 = rx2_input = (self, 1) out_ofdm_blocks = (self, 0) out_frame_start = (self, 1) out_disp_ctf = (self, 2) out_frame_start2 = (self, 3) out_disp_ctf2 = (self, 4) ## pre-FFT processing ## Compute autocorrelations for S&C preamble ## and cyclic prefix sc_metric = autocorrelator(fft_length / 2, fft_length / 2) gi_metric = autocorrelator(fft_length, cp_length) self.connect(rx_input, sc_metric) self.connect(rx_input, gi_metric) sc_metric2 = autocorrelator(fft_length / 2, fft_length / 2) gi_metric2 = autocorrelator(fft_length, cp_length) self.connect(rx2_input, sc_metric2) self.connect(rx2_input, gi_metric2) ## Sync. Output contains OFDM blocks sync = ofdm.time_sync(fft_length, cp_length) self.connect(rx_input, (sync, 0)) self.connect(sc_metric, (sync, 1)) self.connect(gi_metric, (sync, 2)) ofdm_blocks = (sync, 0) frame_start = (sync, 1) sync2 = ofdm.time_sync(fft_length, cp_length) self.connect(rx2_input, (sync2, 0)) self.connect(sc_metric2, (sync2, 1)) self.connect(gi_metric2, (sync2, 2)) ofdm_blocks2 = (sync2, 0) frame_start2 = (sync2, 1) if options.disable_time_sync or options.ideal: terminate_stream(self, ofdm_blocks) terminate_stream(self, ofdm_blocks2) terminate_stream(self, frame_start) terminate_stream(self, frame_start2) serial_to_parallel = blocks.stream_to_vector( gr.sizeof_gr_complex, block_length) serial_to_parallel2 = blocks.stream_to_vector( gr.sizeof_gr_complex, block_length) discard_cp = ofdm.vector_mask(block_length, cp_length, fft_length, []) discard_cp2 = ofdm.vector_mask(block_length, cp_length, fft_length, []) ofdm_blocks = discard_cp ofdm_blocks2 = discard_cp2 self.connect(rx_input, serial_to_parallel, discard_cp) self.connect(rx2_input, serial_to_parallel2, discard_cp2) frame_start = [0] * frame_length frame_start[0] = 1 frame_start = blocks.vector_source_b(frame_start, True) frame_start2 = [0] * frame_length frame_start2[0] = 1 frame_start2 = blocks.vector_source_b(frame_start2, True) print "Disabled time synchronization stage" ## Extract preamble, feed to Morelli & Mengali frequency offset estimator assert (block_header.mm_preamble_pos == 0) morelli_foe = ofdm.mm_frequency_estimator(fft_length, L) sampler_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length, 1) self.connect(ofdm_blocks, (sampler_preamble, 0)) self.connect(frame_start, (sampler_preamble, 1)) self.connect(sampler_preamble, morelli_foe) freq_offset = morelli_foe morelli_foe2 = ofdm.mm_frequency_estimator(fft_length, L) sampler_preamble2 = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length, 1) self.connect(ofdm_blocks2, (sampler_preamble2, 0)) self.connect(frame_start2, (sampler_preamble2, 1)) self.connect(sampler_preamble2, morelli_foe2) freq_offset2 = morelli_foe2 ## Adaptive LMS FIR filtering of frequency offset lms_fir = ofdm.lms_fir_ff(20, 1e-3) # TODO: verify parameter choice self.connect(freq_offset, lms_fir) freq_offset = lms_fir lms_fir2 = ofdm.lms_fir_ff(20, 1e-3) # TODO: verify parameter choice self.connect(freq_offset2, lms_fir2) freq_offset2 = lms_fir2 # log_to_file(self, lms_fir, "data/lms_fir.float") # log_to_file(self, lms_fir2, "data/lms_fir2.float") if options.disable_freq_sync or options.ideal: terminate_stream(self, freq_offset) terminate_stream(self, freq_offset2) freq_offset = blocks.vector_source_f([0.0], True) freq_offset2 = blocks.vector_source_f([0.0], True) print "Disabled frequency synchronization stage" ## Correct frequency shift, feed-forward structure frequency_shift = ofdm.frequency_shift_vcc(fft_length, -1.0 / fft_length, cp_length) self.connect(ofdm_blocks, (frequency_shift, 0)) self.connect(freq_offset, (frequency_shift, 1)) self.connect(frame_start, (frequency_shift, 2)) ofdm_blocks = frequency_shift frequency_shift2 = ofdm.frequency_shift_vcc(fft_length, -1.0 / fft_length, cp_length) self.connect(ofdm_blocks2, (frequency_shift2, 0)) self.connect(freq_offset2, (frequency_shift2, 1)) self.connect(frame_start2, (frequency_shift2, 2)) ofdm_blocks2 = frequency_shift2 ## FFT fft = fft_blocks.fft_vcc(fft_length, True, [], True) self.connect(ofdm_blocks, fft) ofdm_blocks = fft fft2 = fft_blocks.fft_vcc(fft_length, True, [], True) self.connect(ofdm_blocks2, fft2) ofdm_blocks2 = fft2 ## Remove virtual subcarriers if fft_length > data_subc: subcarrier_mask = ofdm.vector_mask(fft_length, virtual_subc / 2, total_subc, []) self.connect(ofdm_blocks, subcarrier_mask) ofdm_blocks = subcarrier_mask subcarrier_mask2 = ofdm.vector_mask(fft_length, virtual_subc / 2, total_subc, []) self.connect(ofdm_blocks2, subcarrier_mask2) ofdm_blocks2 = subcarrier_mask2 ## Least Squares estimator for channel transfer function (CTF) # if options.logcir: # log_to_file( self, ofdm_blocks, "data/OFDM_Blocks.compl" ) # inv_preamble_fd = numpy.array( block_header.pilotsym_fd[ # block_header.channel_estimation_pilot[0] ] ) # print "Channel estimation pilot: ", inv_preamble_fd # inv_preamble_fd = 1. / inv_preamble_fd # LS_channel_estimator0 = ofdm.multiply_const_vcc( list( inv_preamble_fd ) ) # self.connect( ofdm_blocks, LS_channel_estimator0, blocks.null_sink(gr.sizeof_gr_complex*total_subc)) # log_to_file( self, LS_channel_estimator0, "data/OFDM_Blocks_eq.compl" ) ## post-FFT processing if options.est_preamble == 1: ## extract channel estimation preamble from frame chest_pre_trigger = blocks.delay(gr.sizeof_char, 1) sampled_chest_preamble = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) self.connect(frame_start, chest_pre_trigger) self.connect(chest_pre_trigger, (sampled_chest_preamble, 1)) self.connect(ofdm_blocks, (sampled_chest_preamble, 0)) chest_pre_trigger2 = blocks.delay(gr.sizeof_char, 1) sampled_chest_preamble2 = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) self.connect(frame_start2, chest_pre_trigger2) self.connect(chest_pre_trigger2, (sampled_chest_preamble2, 1)) self.connect(ofdm_blocks2, (sampled_chest_preamble2, 0)) ## Least Squares estimator for channel transfer function (CTF) # Taking inverse for estimating h11 (h12) inv_preamble_fd_1 = numpy.array(block_header.pilotsym_fd_1[ block_header.channel_estimation_pilot[0]]) inv_preamble_fd_1 = inv_preamble_fd_1[0::2] # Taking inverse for estimating h21 (h22) inv_preamble_fd_2 = numpy.array(block_header.pilotsym_fd_2[ block_header.channel_estimation_pilot[0]]) inv_preamble_fd_2 = inv_preamble_fd_2[1::2] inv_preamble_fd_1 = 1. / inv_preamble_fd_1 inv_preamble_fd_2 = 1. / inv_preamble_fd_2 ## Least Squares estimator for channel transfer function (CTF) dd = [] for i in range(total_subc / 2): dd.extend([i * 2]) skip_block_1 = ofdm.int_skip(total_subc, 2, 0) skip_block_2 = ofdm.int_skip(total_subc, 2, 1) # inta_estim_1 = ofdm.interpolator(total_subc,2,dd) # inta_estim_2 = ofdm.interpolator(total_subc,2,dd) LS_channel_estimator_1 = ofdm.multiply_const_vcc( list(inv_preamble_fd_1)) LS_channel_estimator_2 = ofdm.multiply_const_vcc( list(inv_preamble_fd_2)) self.connect(sampled_chest_preamble, skip_block_1, LS_channel_estimator_1) #,inta_estim_1 ) self.connect(sampled_chest_preamble, skip_block_2, LS_channel_estimator_2) #,inta_estim_2 ) estimated_CTF_1 = LS_channel_estimator_1 # h0 estimated_CTF_2 = LS_channel_estimator_2 # h1 skip_block_3 = ofdm.int_skip(total_subc, 2, 0) skip_block_4 = ofdm.int_skip(total_subc, 2, 1) # inta_estim_3 = ofdm.interpolator(total_subc,2,dd) # inta_estim_4 = ofdm.interpolator(total_subc,2,dd) LS_channel_estimator_3 = ofdm.multiply_const_vcc( list(inv_preamble_fd_1)) LS_channel_estimator_4 = ofdm.multiply_const_vcc( list(inv_preamble_fd_2)) self.connect(sampled_chest_preamble2, skip_block_3, LS_channel_estimator_3) #,inta_estim_3 ) self.connect(sampled_chest_preamble2, skip_block_4, LS_channel_estimator_4) #,inta_estim_4 ) estimated_CTF_3 = LS_channel_estimator_3 # h2 estimated_CTF_4 = LS_channel_estimator_4 # h3 if not options.disable_ctf_enhancer: # if options.logcir: # ifft1 = fft_blocks.fft_vcc(total_subc,False,[],True) # self.connect( estimated_CTF, ifft1,blocks.null_sink(gr.sizeof_gr_complex*total_subc)) # summ1 = ofdm.vector_sum_vcc(total_subc) # c2m =gr.complex_to_mag(total_subc) # self.connect( estimated_CTF,summ1 ,blocks.null_sink(gr.sizeof_gr_complex)) # self.connect( estimated_CTF, c2m,blocks.null_sink(gr.sizeof_float*total_subc)) # log_to_file( self, ifft1, "data/CIR1.compl" ) # log_to_file( self, summ1, "data/CTFsumm1.compl" ) # log_to_file( self, estimated_CTF, "data/CTF1.compl" ) # log_to_file( self, c2m, "data/CTFmag1.float" ) ## MSE enhancer ctf_mse_enhancer_1 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) ctf_mse_enhancer_2 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect(estimated_CTF_1, ctf_mse_enhancer_1) self.connect(estimated_CTF_2, ctf_mse_enhancer_2) ctf_mse_enhancer_3 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) ctf_mse_enhancer_4 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect(estimated_CTF_3, ctf_mse_enhancer_3) self.connect(estimated_CTF_4, ctf_mse_enhancer_4) estimated_CTF_1 = ctf_mse_enhancer_1 estimated_CTF_2 = ctf_mse_enhancer_2 estimated_CTF_3 = ctf_mse_enhancer_3 estimated_CTF_4 = ctf_mse_enhancer_4 print "Disabled CTF MSE enhancer" ctf_postprocess_1 = ofdm.postprocess_CTF_estimate(total_subc / 2) self.connect(estimated_CTF_1, (ctf_postprocess_1, 0)) ctf_postprocess_2 = ofdm.postprocess_CTF_estimate(total_subc / 2) self.connect(estimated_CTF_2, (ctf_postprocess_2, 0)) ctf_postprocess_3 = ofdm.postprocess_CTF_estimate(total_subc / 2) self.connect(estimated_CTF_3, (ctf_postprocess_3, 0)) ctf_postprocess_4 = ofdm.postprocess_CTF_estimate(total_subc / 2) self.connect(estimated_CTF_4, (ctf_postprocess_4, 0)) inv_CTF_1 = (ctf_postprocess_1, 0) disp_CTF_1 = (ctf_postprocess_1, 1) inv_CTF_2 = (ctf_postprocess_2, 0) disp_CTF_2 = (ctf_postprocess_2, 1) inv_CTF_3 = (ctf_postprocess_3, 0) disp_CTF_3 = (ctf_postprocess_3, 1) inv_CTF_4 = (ctf_postprocess_4, 0) disp_CTF_4 = (ctf_postprocess_4, 1) disp_CTF_RX0 = blocks.add_ff(total_subc / 2) disp_CTF_RX1 = blocks.add_ff(total_subc / 2) self.connect(disp_CTF_1, (disp_CTF_RX0, 0)) self.connect(disp_CTF_2, (disp_CTF_RX0, 1)) self.connect(disp_CTF_3, (disp_CTF_RX1, 0)) self.connect(disp_CTF_4, (disp_CTF_RX1, 1)) terminate_stream(self, disp_CTF_RX0) terminate_stream(self, disp_CTF_RX1) disp_CTF_RX0 = blocks.null_source(gr.sizeof_float * total_subc) disp_CTF_RX1 = blocks.null_source(gr.sizeof_float * total_subc) ## Channel Equalizer #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") #log_to_file(self, ofdm_blocks2, "data/vec_mask2.compl") nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers, 0) phase_tracking2 = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers, 0) ##phase_tracking = ofdm.LMS_phase_tracking2( total_subc, pilot_subc, ## nondata_blocks ) ##phase_tracking2 = ofdm.LMS_phase_tracking2( total_subc, pilot_subc, ## nondata_blocks ) # self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) # self.connect( ofdm_blocks2, ( phase_tracking, 1 )) # self.connect( inv_CTF_1, ( phase_tracking, 2 ) ) # self.connect( inv_CTF_3, ( phase_tracking, 3 ) ) # self.connect( frame_start, ( phase_tracking, 4 ) ) # self.connect( frame_start2, ( phase_tracking, 5) ) # # self.connect( ofdm_blocks2, ( phase_tracking2, 0 ) ) # self.connect( ofdm_blocks, ( phase_tracking2, 1 )) # self.connect( inv_CTF_3, ( phase_tracking2, 2 ) ) # self.connect( inv_CTF_1, ( phase_tracking2, 3 ) ) # self.connect( frame_start2, ( phase_tracking2, 4 ) ) # self.connect( frame_start, ( phase_tracking2, 5 ) ) self.connect(ofdm_blocks, (phase_tracking, 0)) self.connect(inv_CTF_1, (phase_tracking, 1)) self.connect(frame_start, (phase_tracking, 2)) self.connect(ofdm_blocks2, (phase_tracking2, 0)) self.connect(inv_CTF_3, (phase_tracking2, 1)) self.connect(frame_start2, (phase_tracking2, 2)) #ofdm_blocks = phase_tracking #ofdm_blocks2 = phase_tracking2 self.connect(phase_tracking, blocks.null_sink(gr.sizeof_gr_complex * total_subc)) self.connect(phase_tracking2, blocks.null_sink(gr.sizeof_gr_complex * total_subc)) terminate_stream(self, inv_CTF_2) terminate_stream(self, inv_CTF_4) #terminate_stream(self, inv_CTF_1) #terminate_stream(self, inv_CTF_3) terminate_stream(self, estimated_CTF_3) terminate_stream(self, estimated_CTF_4) ##terminate_stream(self, (phase_tracking,1)) ##terminate_stream(self, (phase_tracking2,1)) '''equalizer = ofdm.channel_equalizer_mimo_2( total_subc ) self.connect( ofdm_blocks, ( equalizer, 0 ) ) self.connect( ofdm_blocks2, ( equalizer, 1 ) ) self.connect( inv_CTF_1, ( equalizer, 2 ) ) self.connect( inv_CTF_2, ( equalizer, 3 ) ) self.connect( inv_CTF_3, ( equalizer, 4 ) ) self.connect( inv_CTF_4, ( equalizer, 5 ) ) self.connect( frame_start, ( equalizer, 6 ) ) self.connect( frame_start2, ( equalizer, 7 ) ) ofdm_blocks = equalizer''' terminate_stream(self, inv_CTF_1) terminate_stream(self, inv_CTF_3) equalizer = ofdm.channel_equalizer_mimo_2(total_subc) self.connect(ofdm_blocks, (equalizer, 0)) self.connect(estimated_CTF_1, (equalizer, 1)) self.connect(estimated_CTF_2, (equalizer, 2)) self.connect(frame_start, (equalizer, 3)) ofdm_blocks = equalizer equalizer2 = ofdm.channel_equalizer_mimo_2(total_subc) self.connect(ofdm_blocks2, (equalizer2, 0)) self.connect(estimated_CTF_3, (equalizer2, 1)) self.connect(estimated_CTF_4, (equalizer2, 2)) self.connect(frame_start2, (equalizer2, 3)) ofdm_blocks2 = equalizer2 #ofdm_blocks = equalizer #ofdm_blocks2 = equalizer2 #log_to_file(self, equalizer,"data/equalizer.compl") #log_to_file(self, ofdm_blocks2,"data/equalizer.compl") #log_to_file(self, ofdm_blocks,"data/equalizer2.compl") ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset ''' nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) pilot_subc = block_header.pilot_tones phase_tracking = ofdm.LMS_phase_tracking2( total_subc, pilot_subc, nondata_blocks ) self.connect( equalizer, ( phase_tracking, 0 ) ) self.connect( frame_start, ( phase_tracking, 1 ) ) phase_tracking2 = ofdm.LMS_phase_tracking2( total_subc, pilot_subc, nondata_blocks ) self.connect( equalizer2, ( phase_tracking2, 0 ) ) self.connect( frame_start2, ( phase_tracking2, 1 ) ) # if options.scatter_plot_before_phase_tracking: # self.before_phase_tracking = equalizer if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) terminate_stream(self, phase_tracking2) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ofdm_blocks2 = phase_tracking2 log_to_file(self,phase_tracking, "data/phase_tracking.compl") ''' combine = blocks.add_cc(config.subcarriers) self.connect(ofdm_blocks, (combine, 0)) self.connect(ofdm_blocks2, (combine, 1)) norm_val = [0.5] * 208 norm = ofdm.multiply_const_vcc(norm_val) self.connect(combine, norm) ofdm_blocks = norm ## div = gr.multiply_cc(config.subcarriers) ## const = blocks.vector_source_c([[0.5+0]*config.subcarriers],True) ## self.connect(ofdm_blocks,div) ## self.connect(const,(div,1)) ## ofdm_blocks=div # log_to_file(self,combine,"data/combine.compl") ## Output connections self.connect(ofdm_blocks, out_ofdm_blocks) self.connect(frame_start, out_frame_start) self.connect(disp_CTF_RX0, out_disp_ctf) self.connect(frame_start2, out_frame_start2) self.connect(disp_CTF_RX1, out_disp_ctf2) else: ## extract channel estimation preamble from frame chest_pre_trigger_11 = blocks.delay(gr.sizeof_char, 1) chest_pre_trigger_12 = blocks.delay(gr.sizeof_char, 2) sampled_chest_preamble_11 = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) sampled_chest_preamble_12 = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) self.connect(frame_start, chest_pre_trigger_11) self.connect(chest_pre_trigger_11, (sampled_chest_preamble_11, 1)) self.connect(ofdm_blocks, (sampled_chest_preamble_11, 0)) self.connect(frame_start, chest_pre_trigger_12) self.connect(chest_pre_trigger_12, (sampled_chest_preamble_12, 1)) self.connect(ofdm_blocks, (sampled_chest_preamble_12, 0)) chest_pre_trigger_21 = blocks.delay(gr.sizeof_char, 1) chest_pre_trigger_22 = blocks.delay(gr.sizeof_char, 2) sampled_chest_preamble_21 = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) sampled_chest_preamble_22 = \ ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1 ) self.connect(frame_start2, chest_pre_trigger_21) self.connect(chest_pre_trigger_21, (sampled_chest_preamble_21, 1)) self.connect(ofdm_blocks2, (sampled_chest_preamble_21, 0)) self.connect(frame_start2, chest_pre_trigger_22) self.connect(chest_pre_trigger_22, (sampled_chest_preamble_22, 1)) self.connect(ofdm_blocks2, (sampled_chest_preamble_22, 0)) # Taking inverse for estimating h11 (h12) inv_preamble_fd_1 = numpy.array(block_header.pilotsym_fd_1[ block_header.channel_estimation_pilot[0]]) #inv_preamble_fd_1 = inv_preamble_fd_1[0::2] # Taking inverse for estimating h21 (h22) inv_preamble_fd_2 = numpy.array(block_header.pilotsym_fd_2[ block_header.channel_estimation_pilot[0] + 1]) #inv_preamble_fd_2 = inv_preamble_fd_2[1::2] inv_preamble_fd_1 = 1. / inv_preamble_fd_1 inv_preamble_fd_2 = 1. / inv_preamble_fd_2 # dd = [] #for i in range (total_subc/2): # dd.extend([i*2]) skip_block_11 = ofdm.int_skip(total_subc, 2, 0) skip_block_111 = ofdm.int_skip(total_subc, 2, 0) skip_block_12 = ofdm.int_skip(total_subc, 2, 1) # inta_estim_1 = ofdm.interpolator(total_subc,2,dd) # inta_estim_2 = ofdm.interpolator(total_subc,2,dd) LS_channel_estimator_11 = ofdm.multiply_const_vcc( list(inv_preamble_fd_1)) LS_channel_estimator_12 = ofdm.multiply_const_vcc( list(inv_preamble_fd_2)) self.connect(sampled_chest_preamble_11, LS_channel_estimator_11) #,inta_estim_1 ) self.connect(sampled_chest_preamble_12, LS_channel_estimator_12) #,inta_estim_2 ) estimated_CTF_11 = LS_channel_estimator_11 # h0 estimated_CTF_12 = LS_channel_estimator_12 # h1 skip_block_21 = ofdm.int_skip(total_subc, 2, 0) skip_block_211 = ofdm.int_skip(total_subc, 2, 0) skip_block_22 = ofdm.int_skip(total_subc, 2, 1) # inta_estim_3 = ofdm.interpolator(total_subc,2,dd) # inta_estim_4 = ofdm.interpolator(total_subc,2,dd) LS_channel_estimator_21 = ofdm.multiply_const_vcc( list(inv_preamble_fd_1)) LS_channel_estimator_22 = ofdm.multiply_const_vcc( list(inv_preamble_fd_2)) self.connect(sampled_chest_preamble_21, LS_channel_estimator_21) #,inta_estim_3 ) self.connect(sampled_chest_preamble_22, LS_channel_estimator_22) #,inta_estim_4 ) estimated_CTF_21 = LS_channel_estimator_21 # h2 estimated_CTF_22 = LS_channel_estimator_22 # h3 if not options.disable_ctf_enhancer: # if options.logcir: # ifft1 = fft_blocks.fft_vcc(total_subc,False,[],True) # self.connect( estimated_CTF, ifft1,blocks.null_sink(gr.sizeof_gr_complex*total_subc)) # summ1 = ofdm.vector_sum_vcc(total_subc) # c2m =gr.complex_to_mag(total_subc) # self.connect( estimated_CTF,summ1 ,blocks.null_sink(gr.sizeof_gr_complex)) # self.connect( estimated_CTF, c2m,blocks.null_sink(gr.sizeof_float*total_subc)) # log_to_file( self, ifft1, "data/CIR1.compl" ) # log_to_file( self, summ1, "data/CTFsumm1.compl" ) # log_to_file( self, estimated_CTF, "data/CTF1.compl" ) # log_to_file( self, c2m, "data/CTFmag1.float" ) ## MSE enhancer ctf_mse_enhancer_11 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) ctf_mse_enhancer_12 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect(estimated_CTF_11, ctf_mse_enhancer_11) self.connect(estimated_CTF_12, ctf_mse_enhancer_12) ctf_mse_enhancer_21 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) ctf_mse_enhancer_22 = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect(estimated_CTF_21, ctf_mse_enhancer_21) self.connect(estimated_CTF_22, ctf_mse_enhancer_22) estimated_CTF_11 = ctf_mse_enhancer_11 estimated_CTF_12 = ctf_mse_enhancer_12 estimated_CTF_21 = ctf_mse_enhancer_21 estimated_CTF_22 = ctf_mse_enhancer_22 print "Disabled CTF MSE enhancer" ctf_postprocess_11 = ofdm.postprocess_CTF_estimate(total_subc) self.connect(estimated_CTF_11, (ctf_postprocess_11, 0)) ctf_postprocess_12 = ofdm.postprocess_CTF_estimate(total_subc) self.connect(estimated_CTF_12, (ctf_postprocess_12, 0)) ctf_postprocess_21 = ofdm.postprocess_CTF_estimate(total_subc) self.connect(estimated_CTF_21, (ctf_postprocess_21, 0)) ctf_postprocess_22 = ofdm.postprocess_CTF_estimate(total_subc) self.connect(estimated_CTF_22, (ctf_postprocess_22, 0)) inv_CTF_11 = (ctf_postprocess_11, 0) disp_CTF_11 = (ctf_postprocess_11, 1) inv_CTF_12 = (ctf_postprocess_12, 0) disp_CTF_12 = (ctf_postprocess_12, 1) inv_CTF_21 = (ctf_postprocess_21, 0) disp_CTF_21 = (ctf_postprocess_21, 1) inv_CTF_22 = (ctf_postprocess_22, 0) disp_CTF_22 = (ctf_postprocess_22, 1) #disp_CTF_RX0 = blocks.add_ff(total_subc) #disp_CTF_RX1 = blocks.add_ff(total_subc) #self.connect ( disp_CTF_11, (disp_CTF_RX0, 0) ) #self.connect ( disp_CTF_12, (disp_CTF_RX0, 1) ) #self.connect ( disp_CTF_21, (disp_CTF_RX1, 0) ) #self.connect ( disp_CTF_22, (disp_CTF_RX1, 1) ) terminate_stream(self, disp_CTF_21) terminate_stream(self, disp_CTF_22) disp_CTF_RX0 = disp_CTF_11 disp_CTF_RX1 = disp_CTF_12 ## Channel Equalizer #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") #log_to_file(self, ofdm_blocks2, "data/vec_mask2.compl") nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers, 0) phase_tracking2 = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers, 0) ##phase_tracking = ofdm.LMS_phase_tracking2( total_subc, pilot_subc, ## nondata_blocks ) ##phase_tracking2 = ofdm.LMS_phase_tracking2( total_subc, pilot_subc, ## nondata_blocks ) # self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) # self.connect( ofdm_blocks2, ( phase_tracking, 1 )) # self.connect( inv_CTF_1, ( phase_tracking, 2 ) ) # self.connect( inv_CTF_3, ( phase_tracking, 3 ) ) # self.connect( frame_start, ( phase_tracking, 4 ) ) # self.connect( frame_start2, ( phase_tracking, 5) ) # # self.connect( ofdm_blocks2, ( phase_tracking2, 0 ) ) # self.connect( ofdm_blocks, ( phase_tracking2, 1 )) # self.connect( inv_CTF_3, ( phase_tracking2, 2 ) ) # self.connect( inv_CTF_1, ( phase_tracking2, 3 ) ) # self.connect( frame_start2, ( phase_tracking2, 4 ) ) # self.connect( frame_start, ( phase_tracking2, 5 ) ) self.connect(ofdm_blocks, (phase_tracking, 0)) self.connect(inv_CTF_11, skip_block_111, (phase_tracking, 1)) self.connect(frame_start, (phase_tracking, 2)) self.connect(ofdm_blocks2, (phase_tracking2, 0)) self.connect(inv_CTF_21, skip_block_211, (phase_tracking2, 1)) self.connect(frame_start2, (phase_tracking2, 2)) if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) terminate_stream(self, phase_tracking2) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ofdm_blocks2 = phase_tracking2 self.connect(phase_tracking, blocks.null_sink(gr.sizeof_gr_complex * total_subc)) self.connect(phase_tracking2, blocks.null_sink(gr.sizeof_gr_complex * total_subc)) terminate_stream(self, inv_CTF_12) terminate_stream(self, inv_CTF_22) #terminate_stream(self, inv_CTF_1) #terminate_stream(self, inv_CTF_3) #terminate_stream(self, estimated_CTF_21) #terminate_stream(self, estimated_CTF_22) ##terminate_stream(self, (phase_tracking,1)) ##terminate_stream(self, (phase_tracking2,1)) equalizer = ofdm.channel_equalizer_mimo_3(total_subc) self.connect(ofdm_blocks, (equalizer, 0)) self.connect(ofdm_blocks2, (equalizer, 1)) self.connect(estimated_CTF_11, skip_block_11, (equalizer, 2)) self.connect(estimated_CTF_12, skip_block_12, (equalizer, 3)) self.connect(estimated_CTF_21, skip_block_21, (equalizer, 4)) self.connect(estimated_CTF_22, skip_block_22, (equalizer, 5)) self.connect(frame_start, (equalizer, 6)) self.connect(frame_start, (equalizer, 7)) ofdm_blocks = equalizer #terminate_stream(self, inv_CTF_11) #terminate_stream(self, inv_CTF_21) '''equalizer = ofdm.channel_equalizer_mimo_2( total_subc ) self.connect( ofdm_blocks, ( equalizer, 0 ) ) self.connect( estimated_CTF_11, skip_block_11, ( equalizer, 1 ) ) self.connect( estimated_CTF_12, skip_block_12, ( equalizer, 2 ) ) self.connect( frame_start, ( equalizer, 3 ) ) ofdm_blocks = equalizer equalizer2 = ofdm.channel_equalizer_mimo_2( total_subc ) self.connect( ofdm_blocks2, ( equalizer2, 0 ) ) self.connect( estimated_CTF_21, skip_block_21, ( equalizer2, 1 ) ) self.connect( estimated_CTF_22, skip_block_22, ( equalizer2, 2 ) ) self.connect( frame_start2, ( equalizer2, 3 ) ) ofdm_blocks2 = equalizer2''' #ofdm_blocks = equalizer #ofdm_blocks2 = equalizer2 #log_to_file(self, equalizer,"data/equalizer.compl") #log_to_file(self, ofdm_blocks2,"data/equalizer.compl") #log_to_file(self, ofdm_blocks,"data/equalizer2.compl") ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset ''' nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) pilot_subc = block_header.pilot_tones phase_tracking = ofdm.LMS_phase_tracking2( total_subc, pilot_subc, nondata_blocks ) self.connect( equalizer, ( phase_tracking, 0 ) ) self.connect( frame_start, ( phase_tracking, 1 ) ) phase_tracking2 = ofdm.LMS_phase_tracking2( total_subc, pilot_subc, nondata_blocks ) self.connect( equalizer2, ( phase_tracking2, 0 ) ) self.connect( frame_start2, ( phase_tracking2, 1 ) ) # if options.scatter_plot_before_phase_tracking: # self.before_phase_tracking = equalizer if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) terminate_stream(self, phase_tracking2) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ofdm_blocks2 = phase_tracking2 log_to_file(self,phase_tracking, "data/phase_tracking.compl") ''' '''combine = blocks.add_cc(config.subcarriers) self.connect(ofdm_blocks, (combine,0)) self.connect(ofdm_blocks2, (combine,1)) norm_val = [0.5]*config.subcarriers norm = ofdm.multiply_const_vcc( norm_val) self.connect(combine,norm) ofdm_blocks = norm''' ## div = gr.multiply_cc(config.subcarriers) ## const = blocks.vector_source_c([[0.5+0]*config.subcarriers],True) ## self.connect(ofdm_blocks,div) ## self.connect(const,(div,1)) ## ofdm_blocks=div # log_to_file(self,combine,"data/combine.compl") ## Output connections self.connect(ofdm_blocks, out_ofdm_blocks) self.connect(frame_start, out_frame_start) self.connect(disp_CTF_RX0, out_disp_ctf) self.connect(frame_start2, out_frame_start2) self.connect(disp_CTF_RX1, out_disp_ctf2)
def __init__( self, options, log = False ): ## Read configuration config = station_configuration() fft_length = config.fft_length cp_length = config.cp_length block_header = config.training_data data_subc = config.data_subcarriers virtual_subc = config.virtual_subcarriers total_subc = config.subcarriers block_length = config.block_length frame_length = config.frame_length dc_null = config.dc_null L = block_header.mm_periodic_parts ## Set Input/Output signature gr.hier_block2.__init__( self, "ofdm_inner_receiver", gr.io_signature( 1, 1, gr.sizeof_gr_complex ), gr.io_signaturev( 4, 4, [gr.sizeof_gr_complex * total_subc, # OFDM blocks gr.sizeof_char, # Frame start gr.sizeof_float * total_subc, gr.sizeof_float] ) ) # Normalized |CTF|^2 ## Input and output ports self.input = rx_input = self out_ofdm_blocks = ( self, 0 ) out_frame_start = ( self, 1 ) out_disp_ctf = ( self, 2 ) out_disp_cfo = ( self, 3 ) ## pre-FFT processing if options.ideal is False and options.ideal2 is False: if options.old_receiver is False: ## Compute autocorrelations for S&C preamble ## and cyclic prefix self._sc_metric = sc_metric = autocorrelator( fft_length/2, fft_length/2 ) self._gi_metric = gi_metric = autocorrelator( fft_length, cp_length ) self.connect( rx_input, sc_metric ) self.connect( rx_input, gi_metric ) ## Sync. Output contains OFDM blocks sync = ofdm.time_sync( fft_length, cp_length ) self.connect( rx_input, ( sync, 0 ) ) self.connect( sc_metric, ( sync, 1 ) ) self.connect( gi_metric, ( sync, 2 ) ) ofdm_blocks = ( sync, 0 ) frame_start = ( sync, 1 ) #log_to_file( self, ( sync, 1 ), "data/peak_detector.char" ) else: #Testing old/new metric self.tm = schmidl.recursive_timing_metric(fft_length) self.connect( self.input, self.tm) #log_to_file( self, self.tm, "data/rec_sc_metric_ofdm.float" ) timingmetric_shift = -2#int(-cp_length/4)# 0#-2 #int(-cp_length * 0.8) tmfilter = filter.fft_filter_fff(1, [1./cp_length]*cp_length) self.connect( self.tm, tmfilter ) self.tm = tmfilter self._pd_thres = 0.3 self._pd_lookahead = fft_length / 2 # empirically chosen peak_detector = ofdm.peak_detector_02_fb(self._pd_lookahead, self._pd_thres) self.connect(self.tm, peak_detector) #log_to_file( self, peak_detector, "data/rec_peak_detector.char" ) frame_start = [0]*frame_length frame_start[0] = 1 frame_start = self.frame_trigger_old = blocks.vector_source_b(frame_start,True) delayed_timesync = blocks.delay(gr.sizeof_char, (frame_length-1)*block_length + timingmetric_shift) self.connect( peak_detector, delayed_timesync ) self.block_sampler = ofdm.vector_sampler(gr.sizeof_gr_complex,block_length*frame_length) self.discard_cp = ofdm.vector_mask(block_length,cp_length,fft_length,[]) self.connect(self.input,self.block_sampler) self.connect(delayed_timesync,(self.block_sampler,1)) # TODO: dynamic solution vt2s = blocks.vector_to_stream(gr.sizeof_gr_complex*block_length, frame_length) self.connect(self.block_sampler,vt2s,self.discard_cp) #terminate_stream(self,ofdm_blocks) ofdm_blocks = self.discard_cp # else: # serial_to_parallel = blocks.stream_to_vector(gr.sizeof_gr_complex,block_length) # discard_cp = ofdm.vector_mask(block_length,cp_length,fft_length,[]) # ofdm_blocks = discard_cp # self.connect( rx_input, serial_to_parallel, discard_cp ) # frame_start = [0]*frame_length # frame_start[0] = 1 # frame_start = blocks.vector_source_b(frame_start,True) # # print "Disabled time synchronization stage" ## Compute autocorrelations for S&C preamble ## and cyclic prefix #log_to_file( self, sc_metric, "data/sc_metric_ofdm.float" ) #log_to_file(self, frame_start, "data/frame_start.compl") # log_to_file(self,ofdm_blocks,"data/ofdm_blocks_original.compl") if options.disable_time_sync or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, ofdm_blocks) terminate_stream(self, frame_start) serial_to_parallel = blocks.stream_to_vector(gr.sizeof_gr_complex,block_length) discard_cp = ofdm.vector_mask_dc_null(block_length,cp_length,fft_length,dc_null, []) ofdm_blocks = discard_cp self.connect( rx_input, serial_to_parallel, discard_cp ) frame_start = [0]*frame_length frame_start[0] = 1 frame_start = blocks.vector_source_b(frame_start,True) print "Disabled time synchronization stage" print"\t\t\t\t\tframe_length = ",frame_length if options.ideal is False and options.ideal2 is False: ## Extract preamble, feed to Morelli & Mengali frequency offset estimator assert( block_header.mm_preamble_pos == 0 ) morelli_foe = ofdm.mm_frequency_estimator( fft_length, L,1,0 ) sampler_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * fft_length, 1 ) self.connect( ofdm_blocks, ( sampler_preamble, 0 ) ) self.connect( frame_start, ( sampler_preamble, 1 ) ) self.connect( sampler_preamble, morelli_foe ) freq_offset = morelli_foe ## Adaptive LMS FIR filtering of frequency offset lms_fir = ofdm.lms_fir_ff( 20, 1e-3 ) # TODO: verify parameter choice self.connect( freq_offset, lms_fir ) freq_offset = lms_fir #self.zmq_probe_freqoff = zeromq.pub_sink(gr.sizeof_float, 1, "tcp://*:5557") self.connect(lms_fir, blocks.keep_one_in_n(gr.sizeof_float,20) ,out_disp_cfo) else: self.connect(blocks.vector_source_f ([1]) ,out_disp_cfo) #log_to_file(self, lms_fir, "data/lms_fir.float") if options.disable_freq_sync or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, freq_offset) freq_offset = blocks.vector_source_f([0.0],True) print "Disabled frequency synchronization stage" if options.ideal is False and options.ideal2 is False: ## Correct frequency shift, feed-forward structure frequency_shift = ofdm.frequency_shift_vcc( fft_length, -1.0/fft_length, cp_length ) self.connect( ofdm_blocks, ( frequency_shift, 0 ) ) self.connect( freq_offset, ( frequency_shift, 1 ) ) self.connect( frame_start, ( frequency_shift, 2 ) ) ofdm_blocks = frequency_shift ## FFT fft = fft_blocks.fft_vcc( fft_length, True, [], True ) self.connect( ofdm_blocks, fft ) ofdm_blocks = fft #log_to_file( self, fft, "data/compen.float" ) ## Remove virtual subcarriers if fft_length > data_subc: subcarrier_mask = ofdm.vector_mask_dc_null( fft_length, virtual_subc/2, total_subc, dc_null, [] ) self.connect( ofdm_blocks, subcarrier_mask ) ofdm_blocks = subcarrier_mask #log_to_file(self, ofdm_blocks, "data/vec_mask.compl") ## Least Squares estimator for channel transfer function (CTF) if options.logcir: log_to_file( self, ofdm_blocks, "data/OFDM_Blocks.compl" ) inv_preamble_fd = numpy.array( block_header.pilotsym_fd[ block_header.channel_estimation_pilot[0] ] ) inv_preamble_fd = numpy.concatenate([inv_preamble_fd[:total_subc/2],inv_preamble_fd[total_subc/2+dc_null:]]) #print "Channel estimation pilot: ", inv_preamble_fd inv_preamble_fd = 1. / inv_preamble_fd LS_channel_estimator0 = ofdm.multiply_const_vcc( list( inv_preamble_fd ) ) self.connect( ofdm_blocks, LS_channel_estimator0, gr.null_sink(gr.sizeof_gr_complex*total_subc)) log_to_file( self, LS_channel_estimator0, "data/OFDM_Blocks_eq.compl" ) ## post-FFT processing ## extract channel estimation preamble from frame if options.ideal is False and options.ideal2 is False: chest_pre_trigger = blocks.delay( gr.sizeof_char, 1) sampled_chest_preamble = ofdm.vector_sampler( gr.sizeof_gr_complex * total_subc, 1) self.connect( frame_start, chest_pre_trigger ) self.connect( chest_pre_trigger, ( sampled_chest_preamble, 1 ) ) self.connect( ofdm_blocks, ( sampled_chest_preamble, 0 ) ) ## Least Squares estimator for channel transfer function (CTF) inv_preamble_fd = numpy.array( block_header.pilotsym_fd[ block_header.channel_estimation_pilot[0] ] ) inv_preamble_fd = numpy.concatenate([inv_preamble_fd[:total_subc/2],inv_preamble_fd[total_subc/2+dc_null:]]) #print "Channel estimation pilot: ", inv_preamble_fd inv_preamble_fd = 1. / inv_preamble_fd LS_channel_estimator = ofdm.multiply_const_vcc( list( inv_preamble_fd ) ) self.connect( sampled_chest_preamble, LS_channel_estimator ) estimated_CTF = LS_channel_estimator if options.logcir: log_to_file( self, sampled_chest_preamble, "data/PREAM.compl" ) if not options.disable_ctf_enhancer: if options.logcir: ifft1 = fft_blocks.fft_vcc(total_subc,False,[],True) self.connect( estimated_CTF, ifft1,gr.null_sink(gr.sizeof_gr_complex*total_subc)) summ1 = ofdm.vector_sum_vcc(total_subc) c2m =gr.complex_to_mag(total_subc) self.connect( estimated_CTF,summ1 ,gr.null_sink(gr.sizeof_gr_complex)) self.connect( estimated_CTF, c2m,gr.null_sink(gr.sizeof_float*total_subc)) log_to_file( self, ifft1, "data/CIR1.compl" ) log_to_file( self, summ1, "data/CTFsumm1.compl" ) log_to_file( self, estimated_CTF, "data/CTF1.compl" ) log_to_file( self, c2m, "data/CTFmag1.float" ) ## MSE enhancer ctf_mse_enhancer = ofdm.CTF_MSE_enhancer( total_subc, cp_length + cp_length) self.connect( estimated_CTF, ctf_mse_enhancer ) # log_to_file( self, ctf_mse_enhancer, "data/ctf_mse_enhancer_original.compl") #ifft3 = fft_blocks.fft_vcc(total_subc,False,[],True) #null_noise = ofdm.noise_nulling(total_subc, cp_length + cp_length) #ctf_mse_enhancer = fft_blocks.fft_vcc(total_subc,True,[],True) #ctf_mse_enhancer = ofdm.vector_mask( fft_length, virtual_subc/2, # total_subc, [] ) #self.connect( estimated_CTF, ifft3,null_noise,ctf_mse_enhancer ) estimated_CTF = ctf_mse_enhancer print "Disabled CTF MSE enhancer" if options.logcir: ifft2 = fft_blocks.fft_vcc(total_subc,False,[],True) self.connect( estimated_CTF, ifft2,gr.null_sink(gr.sizeof_gr_complex*total_subc)) summ2 = ofdm.vector_sum_vcc(total_subc) c2m2 =gr.complex_to_mag(total_subc) self.connect( estimated_CTF,summ2 ,gr.null_sink(gr.sizeof_gr_complex)) self.connect( estimated_CTF, c2m2,gr.null_sink(gr.sizeof_float*total_subc)) log_to_file( self, ifft2, "data/CIR2.compl" ) log_to_file( self, summ2, "data/CTFsumm2.compl" ) log_to_file( self, estimated_CTF, "data/CTF2.compl" ) log_to_file( self, c2m2, "data/CTFmag2.float" ) ## Postprocess the CTF estimate ## CTF -> inverse CTF (for equalizer) ## CTF -> norm |.|^2 (for CTF display) ctf_postprocess = ofdm.postprocess_CTF_estimate( total_subc ) self.connect( estimated_CTF, ctf_postprocess ) inv_estimated_CTF = ( ctf_postprocess, 0 ) disp_CTF = ( ctf_postprocess, 1 ) # if options.disable_equalization or options.ideal: # terminate_stream(self, inv_estimated_CTF) # inv_estimated_CTF_vec = blocks.vector_source_c([1.0/fft_length*math.sqrt(total_subc)]*total_subc,True,total_subc) # inv_estimated_CTF_str = blocks.vector_to_stream(gr.sizeof_gr_complex, total_subc) # self.inv_estimated_CTF_mul = ofdm.multiply_const_ccf( 1.0/config.rms_amplitude ) # #inv_estimated_CTF_mul.set_k(1.0/config.rms_amplitude) # inv_estimated_CTF = blocks.stream_to_vector(gr.sizeof_gr_complex, total_subc) # self.connect( inv_estimated_CTF_vec, inv_estimated_CTF_str, self.inv_estimated_CTF_mul, inv_estimated_CTF) # print "Disabled equalization stage" ''' ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) print"\t\t\t\t\tnondata_blocks=",nondata_blocks pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking = ofdm.lms_phase_tracking_03( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers,0 ) self.connect( ofdm_blocks, ( phase_tracking, 0 ) ) self.connect( inv_estimated_CTF, ( phase_tracking, 1 ) ) self.connect( frame_start, ( phase_tracking, 2 ) ) ## if options.scatter_plot_before_phase_tracking: self.before_phase_tracking = equalizer if options.disable_phase_tracking or options.ideal: terminate_stream(self, phase_tracking) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking ''' ## Channel Equalizer if options.disable_equalization or options.ideal or options.ideal2: print "Disabled equalization stage" if options.ideal is False and options.ideal2 is False: terminate_stream(self, inv_estimated_CTF) else: equalizer = ofdm.channel_equalizer( total_subc ) self.connect( ofdm_blocks, ( equalizer, 0 ) ) self.connect( inv_estimated_CTF, ( equalizer, 1 ) ) self.connect( frame_start, ( equalizer, 2 ) ) ofdm_blocks = equalizer #log_to_file(self, equalizer,"data/equalizer_siso.compl") #log_to_file(self, ofdm_blocks, "data/equalizer.compl") ## LMS Phase tracking ## Track residual frequency offset and sampling clock frequency offset if options.ideal is False and options.ideal2 is False: nondata_blocks = [] for i in range(config.frame_length): if i in config.training_data.pilotsym_pos: nondata_blocks.append(i) print"\t\t\t\t\tnondata_blocks=",nondata_blocks pilot_subc = block_header.pilot_tones pilot_subcarriers = block_header.pilot_subc_sym print "PILOT SUBCARRIERS: ", pilot_subcarriers phase_tracking2 = ofdm.lms_phase_tracking_dc_null( total_subc, pilot_subc, nondata_blocks, pilot_subcarriers, dc_null ) self.connect( ofdm_blocks, ( phase_tracking2, 0 ) ) self.connect( frame_start, ( phase_tracking2, 1 ) ) ## if options.disable_phase_tracking or options.ideal or options.ideal2: if options.ideal is False and options.ideal2 is False: terminate_stream(self, phase_tracking2) print "Disabled phase tracking stage" else: ofdm_blocks = phase_tracking2 if options.scatter_plot_before_phase_tracking: self.before_phase_tracking = equalizer ## Output connections self.connect( ofdm_blocks, out_ofdm_blocks ) self.connect( frame_start, out_frame_start ) if options.ideal is False and options.ideal2 is False: self.connect( disp_CTF, out_disp_ctf ) else: self.connect( blocks.vector_source_f([1.0]*total_subc),blocks.stream_to_vector(gr.sizeof_float,total_subc), out_disp_ctf ) if log: log_to_file( self, sc_metric, "data/sc_metric.float" ) log_to_file( self, gi_metric, "data/gi_metric.float" ) log_to_file( self, morelli_foe, "data/morelli_foe.float" ) log_to_file( self, lms_fir, "data/lms_fir.float" ) log_to_file( self, sampler_preamble, "data/preamble.compl" ) log_to_file( self, sync, "data/sync.compl" ) log_to_file( self, frequency_shift, "data/frequency_shift.compl" ) log_to_file( self, fft, "data/fft.compl") log_to_file( self, fft, "data/fft.float", mag=True ) if vars().has_key( 'subcarrier_mask' ): log_to_file( self, subcarrier_mask, "data/subcarrier_mask.compl" ) log_to_file( self, ofdm_blocks, "data/ofdm_blocks_out.compl" ) log_to_file( self, frame_start, "data/frame_start.float", char_to_float=True ) log_to_file( self, sampled_chest_preamble, "data/sampled_chest_preamble.compl" ) log_to_file( self, LS_channel_estimator, "data/ls_channel_estimator.compl" ) log_to_file( self, LS_channel_estimator, "data/ls_channel_estimator.float", mag=True ) if "ctf_mse_enhancer" in locals(): log_to_file( self, ctf_mse_enhancer, "data/ctf_mse_enhancer.compl" ) log_to_file( self, ctf_mse_enhancer, "data/ctf_mse_enhancer.float", mag=True ) log_to_file( self, (ctf_postprocess,0), "data/inc_estimated_ctf.compl" ) log_to_file( self, (ctf_postprocess,1), "data/disp_ctf.float" ) log_to_file( self, equalizer, "data/equalizer.compl" ) log_to_file( self, equalizer, "data/equalizer.float", mag=True ) log_to_file( self, phase_tracking, "data/phase_tracking.compl" )
def __init__(self, fft_length, block_length, frame_data_part, block_header, options): gr.hier_block2.__init__(self, "ofdm_receiver", gr.io_signature (1,1,gr.sizeof_gr_complex), gr.io_signature2(2,2,gr.sizeof_gr_complex*fft_length, gr.sizeof_char)) frame_length = frame_data_part + block_header.no_pilotsyms cp_length = block_length-fft_length self.input=gr.kludge_copy(gr.sizeof_gr_complex) self.connect(self, self.input) self.blocks_out = (self,0) self.frame_trigger_out = (self,1) self.snr_out = (self,2) if options.log: log_to_file(self, self.input, "data/receiver_input.compl") # peak detector: thresholds low, high #self._pd_thres_lo = 0.09 #self._pd_thres_hi = 0.1 self._pd_thres = 0.2 self._pd_lookahead = fft_length / 2 # empirically chosen ######################### # coarse timing offset estimator # self.tm = schmidl.modified_timing_metric(fft_length,[1]*(fft_length)) self.tm = schmidl.recursive_timing_metric(fft_length) self.connect(self.input,self.tm) assert(hasattr(block_header, 'sc_preamble_pos')) assert(block_header.sc_preamble_pos == 0) # TODO: relax this restriction if options.filter_timingmetric: timingmetric_shift = -2 #int(-cp_length * 0.8) tmfilter = gr.fir_filter_fff(1, [1./cp_length]*cp_length) self.connect( self.tm, tmfilter ) self.timing_metric = tmfilter print "Filtering timing metric, experimental" else: self.timing_metric = self.tm timingmetric_shift = int(-cp_length/4) if options.log: log_to_file(self, self.timing_metric, "data/tm.float") # peak detection #threshold = gr.threshold_ff(self._pd_thres_lo,self._pd_thres_hi,0) #muted_tm = gr.multiply_ff() peak_detector = peak_detector_02_fb(self._pd_lookahead, self._pd_thres) #self.connect(self.timing_metric, threshold, (muted_tm,0)) #self.connect(self.timing_metric, (muted_tm,1)) #self.connect(muted_tm, peak_detector) self.connect(self.timing_metric, peak_detector) if options.log: pd_float = gr.char_to_float() self.connect(peak_detector,pd_float) log_to_file(self, pd_float, "data/peakdetector.float") if options.no_timesync: terminate_stream( self, peak_detector ) trigger = [0]*(frame_length*block_length) trigger[ block_length-1 ] = 1 peak_detector = blocks.vector_source_b( trigger, True ) print "Bypassing timing synchronisation" # TODO: refine detected peaks with 90% average method as proposed # from Schmidl & Cox: # Starting from peak, find first points to the left and right whose # value is less than or equal 90% of the peak value. New trigger point # is average of both # Frequency Offset Estimation # Used: Algorithm as proposed from Morelli & Mengali # Idea: Use periodic preamble, correlate identical parts, determine # phase offset. This phase offset is a function of the frequency offset. assert(hasattr(block_header, 'mm_preamble_pos')) foe = morelli_foe(fft_length,block_header.mm_periodic_parts) self.connect(self.input,(foe,0)) if block_header.mm_preamble_pos > 0: delayed_trigger = gr.delay(gr.sizeof_char, block_header.mm_preamble_pos*block_length) self.connect(peak_detector,delayed_trigger,(foe,1)) else: self.connect(peak_detector,(foe,1)) self.freq_offset = foe if options.log: log_to_file(self, self.freq_offset, "data/freqoff_out.float") if options.average_freqoff: #avg_foe = gr.single_pole_iir_filter_ff( 0.1 ) avg_foe = ofdm.lms_fir_ff( 20, 1e-3 ) self.connect( self.freq_offset, avg_foe ) self.freq_offset = avg_foe #log_to_file( self, avg_foe, "data/freqoff_out_avg.float" ) print "EXPERIMENTAL!!! Filtering frequency offset estimate" if options.no_freqsync: terminate_stream( self, self.freq_offset ) self.freq_offset = blocks.vector_source_f( [0.0], True ) print "Bypassing frequency offset estimator, offset=0.0" # TODO: dynamic solution frametrig_seq = concatenate([[1],[0]*(frame_length-1)]) self.time_sync = peak_detector self.frame_trigger = blocks.vector_source_b(frametrig_seq,True) self.connect(self.frame_trigger, self.frame_trigger_out) ########################## # symbol extraction and processing # First, we extract the whole ofdm block, then we divide this block into # several ofdm symbols. This asserts that all symbols belonging to the # same ofdm block will be a consecutive order. # extract ofdm symbols # compensate frequency offset # TODO: use PLL and update/reset signals delayed_timesync = gr.delay(gr.sizeof_char, (frame_length-1)*block_length+timingmetric_shift) self.connect( self.time_sync, delayed_timesync ) self.block_sampler = vector_sampler(gr.sizeof_gr_complex,block_length*frame_length) self.discard_cp = vector_mask(block_length,cp_length,fft_length,[]) if options.use_dpll: dpll = gr.dpll_bb( frame_length * block_length , .01 ) self.connect( delayed_timesync, dpll ) if options.log: dpll_f = gr.char_to_float() delayed_timesync_f = gr.char_to_float() self.connect( dpll, dpll_f ) self.connect( delayed_timesync, delayed_timesync_f ) log_to_file( self, dpll_f, "data/dpll.float" ) log_to_file( self, delayed_timesync_f, "data/dpll_in.float" ) delayed_timesync = dpll print "Using DPLL, EXPERIMENTAL!!!!!" self.connect(self.input,self.block_sampler) self.connect(delayed_timesync,(self.block_sampler,1)) if options.log: log_to_file(self, self.block_sampler, "data/block_sampler_out.compl") # TODO: dynamic solution self.ofdm_symbols = blocks.vector_to_stream(gr.sizeof_gr_complex*block_length, frame_length) self.connect(self.block_sampler,self.ofdm_symbols,self.discard_cp) if options.log: log_to_file(self, self.discard_cp, "data/discard_cp_out.compl") dcp_fft = gr.fft_vcc(fft_length, True, [], True) self.connect(self.discard_cp,dcp_fft) log_to_file(self, dcp_fft, "data/discard_cp_fft.compl") # reset phase accumulator inside freq_shift on every block start # setup output connection freq_shift = frequency_shift_vcc(fft_length, -1.0/fft_length, cp_length) self.connect(self.discard_cp,(freq_shift,0)) self.connect(self.freq_offset,(freq_shift,1)) self.connect(self.frame_trigger,(freq_shift,2)) self.connect(freq_shift, self.blocks_out) if options.log: log_to_file(self, freq_shift, "data/freqshift_out.compl") if options.no_freqshift: terminate_stream( self, freq_shift ) freq_shift = self.discard_cp print "Bypassing frequency shift block"