예제 #1
0
	def __init__(self, options, rx_callback):
		phy_base.__init__(self, options, rx_callback)

		# Transmit chain
		self._tx_mod = blks2.ofdm_mod(options, msgq_limit=4, pad_for_usrp=False)
		self.connect(self._tx_mod, gr.kludge_copy(gr.sizeof_gr_complex), self)

		# Receive chain
		self._rx_demod = blks2.ofdm_demod(options, callback=self._rx_callback)
		self.connect(self, gr.kludge_copy(gr.sizeof_gr_complex), self._rx_demod)
예제 #2
0
    def __init__(self, options, need_padding=False):
        gr.hier_block2.__init__(
            self, "cs_mac", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_gr_complex)
        )

        # Create a logger
        self._logger = logging.getLogger("MAC(" + str(options.mac_address) + ")")

        # Extract options
        self._local_addr = options.mac_address
        self._need_padding = need_padding

        # Create queue for packets received from physical layer
        self._phy_rx_queue = Queue.Queue()

        # Create the ack_queue which the condition variable and thread use
        self.ack_queue = Queue.Queue()
        self.condition = threading.Condition()

        # Create physical layer implementation (modulator, demodulator)
        self._phy = cs434_hw4_phy.AVAILABLE[options.phy](options, lambda ok, payload: self._handle_recv(ok, payload))

        # Received samples are redirected to two places. GNU Radio doesn't
        # currently let us attach 2 blocks to a hierarchical block input, so
        # we first copy, then split.
        rx_dup = gr.kludge_copy(gr.sizeof_gr_complex)
        self.connect(self, rx_dup)

        # Create and connect carrier-sensing block
        self._phy_rx_probe = gr.probe_avg_mag_sqrd_c(options.cs_threshold, 0.001)
        self.connect(rx_dup, self._phy_rx_probe)

        # Construct receive chain: received packets are delivered via callback from PHY
        self.connect(rx_dup, self._phy)

        # For certain settings, we may need to 'pad' our
        # transmissions with a 0-powered signal when we
        # aren't actively transmitting a packet
        if need_padding:
            import cs434_hw4

            self._phy_tx_padder = cs434_hw4.channel_runner_cc()
        else:
            self._phy_tx_padder = gr.kludge_copy(gr.sizeof_gr_complex)

        # Construct transmit chain: packets are delivered to PHY via 'send_pkt()' method
        self._phy_tx_scale = gr.multiply_const_cc(options.tx_amplitude)
        self.connect(self._phy, self._phy_tx_scale, self._phy_tx_padder, self)
예제 #3
0
	def __init__(self, options):
		gr.hier_block2.__init__(self, "software_channel",
					gr.io_signature(1, 1, gr.sizeof_gr_complex),
					gr.io_signature(1, 1, gr.sizeof_gr_complex))

		self._copy_in = gr.kludge_copy(gr.sizeof_gr_complex)

		self._channel = blks2impl.channel_model.channel_model(
			options.noise_amplitude,
			options.freq_offset,
			options.timing_ratio,
			[1.0, 0.0],
			options.noise_seed)
		self._copy_out = gr.kludge_copy(gr.sizeof_gr_complex)

		self.connect(self, self._copy_in, self._channel, self._copy_out, self)
예제 #4
0
	def __init__(self, item_size, num_inputs, num_outputs, input_index, output_index):
		"""
		Selector constructor.
		@param item_size the size of the gr data stream in bytes
		@param num_inputs the number of inputs (integer)
		@param num_outputs the number of outputs (integer)
		@param input_index the index for the source data
		@param output_index the index for the destination data
		"""
		gr.hier_block2.__init__(
			self, 'selector',
			gr.io_signature(num_inputs, num_inputs, item_size),
			gr.io_signature(num_outputs, num_outputs, item_size),
		)
		#terminator blocks for unused inputs and outputs
		self.input_terminators = [gr.null_sink(item_size) for i in range(num_inputs)]
		self.output_terminators = [gr.head(item_size, 0) for i in range(num_outputs)]
		self.copy = gr.kludge_copy(item_size)
		#connections
		for i in range(num_inputs): self.connect((self, i), self.input_terminators[i])
		for i in range(num_outputs): self.connect(gr.null_source(item_size), self.output_terminators[i], (self, i))
		self.item_size = item_size
		self.input_index = input_index
		self.output_index = output_index
		self.num_inputs = num_inputs
		self.num_outputs = num_outputs
		self._connect_current()
예제 #5
0
  def __init__(self, fft_length):
    gr.hier_block2.__init__(self, "timing_metric",
        gr.io_signature(1,1,gr.sizeof_gr_complex),
        gr.io_signature(1,1,gr.sizeof_float))

    self.input = gr.kludge_copy(gr.sizeof_gr_complex)
    self.connect(self,self.input)

    # P(d)
    nominator = schmidl_nominator(fft_length)

    # R(d)
    denominator = schmidl_denominator(fft_length)

    # |P(d)| ** 2 / (R(d)) ** 2
    p_mag_sqrd = gr.complex_to_mag_squared()
    r_sqrd = gr.multiply_ff()
    self.timing_metric = gr.divide_ff()

    self.connect(self.input, nominator, p_mag_sqrd, (self.timing_metric,0))
    self.connect(self.input, denominator, (r_sqrd,0))
    self.connect(denominator, (r_sqrd,1))
    self.connect(r_sqrd, (self.timing_metric,1))
    self.connect(self.timing_metric, self)

    # calculate epsilon from P(d), epsilon is normalized fractional frequency offset
    #angle = gr.complex_to_arg()
    #self.epsilon = gr.multiply_const_ff(1.0/math.pi)
    #self.connect(nominator, angle, self.epsilon)

    try:
        gr.hier_block.update_var_names(self, "schmidl", vars())
        gr.hier_block.update_var_names(self, "schmidl", vars(self))
    except:
        pass
예제 #6
0
  def __init__(self, fft_length, pn_weights):
    gr.hier_block2.__init__(self, "modified_timing_metric",
        gr.io_signature(1,1,gr.sizeof_gr_complex),
        gr.io_signature(1,1,gr.sizeof_float))

    assert(len(pn_weights) == fft_length)

    self.input = gr.kludge_copy(gr.sizeof_gr_complex)
    self.connect(self,self.input)

    # P(d) = sum(0 to L-1, conj(delayed(r)) * r)
    conj = gr.conjugate_cc()
    mixer = gr.multiply_cc()
    nominator = gr.fir_filter_ccf(1,[pn_weights[fft_length-i-1]*pn_weights[fft_length/2-i-1] for i in range(fft_length/2)])

    self.connect(self.input, delay(gr.sizeof_gr_complex,fft_length/2), conj, (mixer,0))
    self.connect(self.input, (mixer,1))
    self.connect(mixer, nominator)
    # moving_avg = P(d)

    # R(d)
    denominator = schmidl_denominator(fft_length)

    # |P(d)| ** 2 / (R(d)) ** 2
    p_mag_sqrd = gr.complex_to_mag_squared()
    r_sqrd = gr.multiply_ff()
    self.timing_metric = gr.divide_ff()

    self.connect(nominator, p_mag_sqrd, (self.timing_metric,0))
    self.connect(self.input, denominator, (r_sqrd,0))
    self.connect(denominator, (r_sqrd,1))
    self.connect(r_sqrd, (self.timing_metric,1))
    self.connect(self.timing_metric, self)
예제 #7
0
	def __init__(self, options, rx_callback):
		phy_base.__init__(self, options, rx_callback)

		# Receive chain
		demod_class = phy_psk.DEMODS[options.psk_modulation]
		demod_kwargs = demod_class.extract_kwargs_from_options(options)
		self._rx_demod = blks2.demod_pkts(
				demod_class(**demod_kwargs),
				callback=self._rx_callback)
		sw_decim = 1
		self._rx_chan_filt = gr.fft_filter_ccc(
				sw_decim, 
				gr.firdes.low_pass (
					1.0,								# gain
					sw_decim * self._rx_demod._demodulator.samples_per_symbol(),	# sampling rate
					1.0,								# midpoint of trans. band
					0.5,								# width of trans. band
					gr.firdes.WIN_HANN))						# filter type
		self.connect(self, self._rx_chan_filt, self._rx_demod)

		# Trasmit chain
		mod_class = phy_psk.MODS[options.psk_modulation]
		mod_kwargs = mod_class.extract_kwargs_from_options(options)
		self._tx_mod = blks2.mod_pkts(
				mod_class(**mod_kwargs),
				pad_for_usrp=True)
		self.connect(self._tx_mod, gr.kludge_copy(gr.sizeof_gr_complex), self)
예제 #8
0
    def __init__(self, vlen):
        gr.hier_block2.__init__(
            self, "snr_estimator",
            gr.io_signature(2, 2, gr.sizeof_gr_complex * vlen),
            gr.io_signature(1, 1, gr.sizeof_float))

        reference = gr.kludge_copy(gr.sizeof_gr_complex * vlen)
        received = gr.kludge_copy(gr.sizeof_gr_complex * vlen)
        self.connect((self, 0), reference)
        self.connect((self, 1), received)

        received_conjugated = gr.conjugate_cc(vlen)
        self.connect(received, received_conjugated)

        R_innerproduct = gr.multiply_vcc(vlen)
        self.connect(reference, R_innerproduct)
        self.connect(received_conjugated, (R_innerproduct, 1))

        R_sum = vector_sum_vcc(vlen)
        self.connect(R_innerproduct, R_sum)

        R = gr.complex_to_mag_squared()
        self.connect(R_sum, R)

        received_magsqrd = gr.complex_to_mag_squared(vlen)
        reference_magsqrd = gr.complex_to_mag_squared(vlen)
        self.connect(received, received_magsqrd)
        self.connect(reference, reference_magsqrd)

        received_sum = vector_sum_vff(vlen)
        reference_sum = vector_sum_vff(vlen)
        self.connect(received_magsqrd, received_sum)
        self.connect(reference_magsqrd, reference_sum)

        P = gr.multiply_ff()
        self.connect(received_sum, (P, 0))
        self.connect(reference_sum, (P, 1))

        denominator = gr.sub_ff()
        self.connect(P, denominator)
        self.connect(R, (denominator, 1))

        rho_hat = gr.divide_ff()
        self.connect(R, rho_hat)
        self.connect(denominator, (rho_hat, 1))
        self.connect(rho_hat, self)
예제 #9
0
  def __init__(self,vlen):
    gr.hier_block2.__init__(self,"snr_estimator",
      gr.io_signature(2,2,gr.sizeof_gr_complex*vlen),
      gr.io_signature(1,1,gr.sizeof_float))

    reference = gr.kludge_copy(gr.sizeof_gr_complex*vlen)
    received = gr.kludge_copy(gr.sizeof_gr_complex*vlen)
    self.connect((self,0),reference)
    self.connect((self,1),received)

    received_conjugated = gr.conjugate_cc(vlen)
    self.connect(received,received_conjugated)

    R_innerproduct = gr.multiply_vcc(vlen)
    self.connect(reference,R_innerproduct)
    self.connect(received_conjugated,(R_innerproduct,1))

    R_sum = vector_sum_vcc(vlen)
    self.connect(R_innerproduct,R_sum)

    R = gr.complex_to_mag_squared()
    self.connect(R_sum,R)

    received_magsqrd = gr.complex_to_mag_squared(vlen)
    reference_magsqrd = gr.complex_to_mag_squared(vlen)
    self.connect(received,received_magsqrd)
    self.connect(reference,reference_magsqrd)

    received_sum = vector_sum_vff(vlen)
    reference_sum = vector_sum_vff(vlen)
    self.connect(received_magsqrd,received_sum)
    self.connect(reference_magsqrd,reference_sum)

    P = gr.multiply_ff()
    self.connect(received_sum,(P,0))
    self.connect(reference_sum,(P,1))

    denominator = gr.sub_ff()
    self.connect(P,denominator)
    self.connect(R,(denominator,1))

    rho_hat = gr.divide_ff()
    self.connect(R,rho_hat)
    self.connect(denominator,(rho_hat,1))
    self.connect(rho_hat,self)
예제 #10
0
 def test_001(self):
     # 1 input stream; 1 output stream
     src0_data = self.make_random_int_tuple(16000)
     src0 = gr.vector_source_i(src0_data)
     op = gr.kludge_copy(gr.sizeof_int)
     dst0 = gr.vector_sink_i()
     self.tb.connect(src0, op, dst0)
     self.tb.run()
     dst0_data = dst0.data()
     self.assertEqual(src0_data, dst0_data)
 def test_001(self):
     # 1 input stream; 1 output stream
     src0_data = self.make_random_int_tuple(16000)
     src0 = gr.vector_source_i(src0_data)
     op = gr.kludge_copy(gr.sizeof_int)
     dst0 = gr.vector_sink_i()
     self.fg.connect(src0, op, dst0)
     self.fg.run()
     dst0_data = dst0.data()
     self.assertEqual(src0_data, dst0_data)
예제 #12
0
 def test_030_nested_input(self):
     tb = gr.top_block()
     src = gr.vector_source_b([1])
     hb1 = gr.hier_block2("hb1", gr.io_signature(1, 1, gr.sizeof_char), gr.io_signature(0, 0, 0))
     hb2 = gr.hier_block2("hb2", gr.io_signature(1, 1, gr.sizeof_char), gr.io_signature(0, 0, 0))
     dst = gr.vector_sink_b()
     tb.connect(src, hb1)
     hb1.connect(hb1, hb2)
     hb2.connect(hb2, gr.kludge_copy(gr.sizeof_char), dst)
     tb.run()
     self.assertEquals(dst.data(), (1,))
예제 #13
0
 def xtest_003(self):
     # number of input streams != number of output streams
     src0_data = self.make_random_int_tuple(16000)
     src1_data = self.make_random_int_tuple(16000)
     src0 = gr.vector_source_i(src0_data)
     src1 = gr.vector_source_i(src1_data)
     op = gr.kludge_copy(gr.sizeof_int)
     dst0 = gr.vector_sink_i()
     dst1 = gr.vector_sink_i()
     self.tb.connect(src0, (op, 0), dst0)
     self.tb.connect(src1, (op, 1))
     self.assertRaises(ValueError, self.tb.run)
 def test_003(self):
     # number of input streams != number of output streams
     src0_data = self.make_random_int_tuple(16000)
     src1_data = self.make_random_int_tuple(16000)
     src0 = gr.vector_source_i(src0_data)
     src1 = gr.vector_source_i(src1_data)
     op = gr.kludge_copy(gr.sizeof_int)
     dst0 = gr.vector_sink_i()
     dst1 = gr.vector_sink_i()
     self.fg.connect(src0, (op, 0), dst0)
     self.fg.connect(src1, (op, 1))
     self.assertRaises(ValueError, self.fg.run)
예제 #15
0
  def __init__ ( self, noise_power, coherence_time, taps ):
    gr.hier_block2.__init__(self,
      "fading_channel", 
      gr.io_signature( 1, 1, gr.sizeof_gr_complex ),
      gr.io_signature( 1, 1, gr.sizeof_gr_complex ) )

    inp = gr.kludge_copy( gr.sizeof_gr_complex )
    self.connect( self, inp )
    
    tap1_delay = gr.delay( gr.sizeof_gr_complex, 1 )
    tap2_delay = gr.delay( gr.sizeof_gr_complex, 2 )
    self.connect( inp, tap1_delay )
    self.connect( inp, tap2_delay )
    
    fd = 100
    z = numpy.arange(-fd+0.1,fd,0.1)
    t = numpy.sqrt(1. / ( pi * fd * numpy.sqrt( 1. - (z/fd)**2 ) ) )
    
    tap1_noise = ofdm.complex_white_noise( 0.0, taps[0] )
    tap1_filter = gr.fft_filter_ccc(1, t)
    self.connect( tap1_noise, tap1_filter )
    
    tap1_mult = gr.multiply_cc()
    self.connect( tap1_filter, (tap1_mult,0) )
    self.connect( tap1_delay, (tap1_mult,1) )
    
    tap2_noise = ofdm.complex_white_noise( 0.0, taps[1] )
    tap2_filter = gr.fft_filter_ccc(1, t)
    
    self.connect( tap2_noise, tap2_filter )
    
    tap2_mult = gr.multiply_cc()
    self.connect( tap2_filter, (tap2_mult,0) )
    self.connect( tap2_delay, (tap2_mult,1) )
    
    noise_src = ofdm.complex_white_noise( 0.0, sqrt( noise_power ) )
    chan = gr.add_cc()
    
    self.connect( inp, (chan,0) )
    self.connect( tap1_mult, (chan,1) )
    self.connect( tap2_mult, (chan,2) )
    self.connect( noise_src, (chan,3) )
    
    self.connect( chan, self )
    
    log_to_file( self, tap1_filter, "data/tap1_filter.compl")
    log_to_file( self, tap1_filter, "data/tap1_filter.float", mag=True)
    log_to_file( self, tap2_filter, "data/tap2_filter.compl")
    log_to_file( self, tap2_filter, "data/tap2_filter.float", mag=True)
예제 #16
0
 def test_030_nested_input(self):
     tb = gr.top_block()
     src = gr.vector_source_b([
         1,
     ])
     hb1 = gr.hier_block2("hb1", gr.io_signature(1, 1, gr.sizeof_char),
                          gr.io_signature(0, 0, 0))
     hb2 = gr.hier_block2("hb2", gr.io_signature(1, 1, gr.sizeof_char),
                          gr.io_signature(0, 0, 0))
     dst = gr.vector_sink_b()
     tb.connect(src, hb1)
     hb1.connect(hb1, hb2)
     hb2.connect(hb2, gr.kludge_copy(gr.sizeof_char), dst)
     tb.run()
     self.assertEquals(dst.data(), (1, ))
예제 #17
0
    def __init__(self, noise_power, coherence_time, taps):
        gr.hier_block2.__init__(self, "fading_channel",
                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
                                gr.io_signature(1, 1, gr.sizeof_gr_complex))

        inp = gr.kludge_copy(gr.sizeof_gr_complex)
        self.connect(self, inp)

        tap1_delay = gr.delay(gr.sizeof_gr_complex, 1)
        tap2_delay = gr.delay(gr.sizeof_gr_complex, 2)
        self.connect(inp, tap1_delay)
        self.connect(inp, tap2_delay)

        fd = 100
        z = numpy.arange(-fd + 0.1, fd, 0.1)
        t = numpy.sqrt(1. / (pi * fd * numpy.sqrt(1. - (z / fd)**2)))

        tap1_noise = ofdm.complex_white_noise(0.0, taps[0])
        tap1_filter = gr.fft_filter_ccc(1, t)
        self.connect(tap1_noise, tap1_filter)

        tap1_mult = gr.multiply_cc()
        self.connect(tap1_filter, (tap1_mult, 0))
        self.connect(tap1_delay, (tap1_mult, 1))

        tap2_noise = ofdm.complex_white_noise(0.0, taps[1])
        tap2_filter = gr.fft_filter_ccc(1, t)

        self.connect(tap2_noise, tap2_filter)

        tap2_mult = gr.multiply_cc()
        self.connect(tap2_filter, (tap2_mult, 0))
        self.connect(tap2_delay, (tap2_mult, 1))

        noise_src = ofdm.complex_white_noise(0.0, sqrt(noise_power))
        chan = gr.add_cc()

        self.connect(inp, (chan, 0))
        self.connect(tap1_mult, (chan, 1))
        self.connect(tap2_mult, (chan, 2))
        self.connect(noise_src, (chan, 3))

        self.connect(chan, self)

        log_to_file(self, tap1_filter, "data/tap1_filter.compl")
        log_to_file(self, tap1_filter, "data/tap1_filter.float", mag=True)
        log_to_file(self, tap2_filter, "data/tap2_filter.compl")
        log_to_file(self, tap2_filter, "data/tap2_filter.float", mag=True)
예제 #18
0
 def test_002(self):
     # 2 input streams; 2 output streams
     src0_data = self.make_random_int_tuple(16000)
     src1_data = self.make_random_int_tuple(16000)
     src0 = gr.vector_source_i(src0_data)
     src1 = gr.vector_source_i(src1_data)
     op = gr.kludge_copy(gr.sizeof_int)
     dst0 = gr.vector_sink_i()
     dst1 = gr.vector_sink_i()
     self.tb.connect(src0, (op, 0), dst0)
     self.tb.connect(src1, (op, 1), dst1)
     self.tb.run()
     dst0_data = dst0.data()
     dst1_data = dst1.data()
     self.assertEqual(src0_data, dst0_data)
     self.assertEqual(src1_data, dst1_data)
 def test_002(self):
     # 2 input streams; 2 output streams
     src0_data = self.make_random_int_tuple(16000)
     src1_data = self.make_random_int_tuple(16000)
     src0 = gr.vector_source_i(src0_data)
     src1 = gr.vector_source_i(src1_data)
     op = gr.kludge_copy(gr.sizeof_int)
     dst0 = gr.vector_sink_i()
     dst1 = gr.vector_sink_i()
     self.fg.connect(src0, (op, 0), dst0)
     self.fg.connect(src1, (op, 1), dst1)
     self.fg.run()
     dst0_data = dst0.data()
     dst1_data = dst1.data()
     self.assertEqual(src0_data, dst0_data)
     self.assertEqual(src1_data, dst1_data)
예제 #20
0
 def __init__ ( self, fft_length ):
   gr.hier_block2.__init__(self, "recursive_timing_metric",
       gr.io_signature(1,1,gr.sizeof_gr_complex),
       gr.io_signature(1,1,gr.sizeof_float))
   
   self.input = gr.kludge_copy(gr.sizeof_gr_complex)
   self.connect(self, self.input)
   
   # P(d) = sum(0 to L-1, conj(delayed(r)) * r)
   conj = gr.conjugate_cc()
   mixer = gr.multiply_cc()
   mix_delay = delay(gr.sizeof_gr_complex,fft_length/2+1)
   mix_diff = gr.sub_cc()
   nominator = accumulator_cc()
   inpdelay = delay(gr.sizeof_gr_complex,fft_length/2)
   
   self.connect(self.input, inpdelay, 
                conj, (mixer,0))
   self.connect(self.input, (mixer,1))
   self.connect(mixer,(mix_diff,0))
   self.connect(mixer, mix_delay, (mix_diff,1))
   self.connect(mix_diff,nominator)
   
   rmagsqrd = gr.complex_to_mag_squared()
   rm_delay = delay(gr.sizeof_float,fft_length+1)
   rm_diff = gr.sub_ff()
   denom = accumulator_ff()
   self.connect(self.input,rmagsqrd,rm_diff,gr.multiply_const_ff(0.5),denom)
   self.connect(rmagsqrd,rm_delay,(rm_diff,1))
   
   
   ps = gr.complex_to_mag_squared()
   rs = gr.multiply_ff()
   self.connect(nominator,ps)
   self.connect(denom,rs)
   self.connect(denom,(rs,1))
   
   div = gr.divide_ff()
   self.connect(ps,div)
   self.connect(rs,(div,1))
   
   self.connect(div,self)
예제 #21
0
    def __init__(self, fft_length):
        gr.hier_block2.__init__(self, "recursive_timing_metric",
                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
                                gr.io_signature(1, 1, gr.sizeof_float))

        self.input = gr.kludge_copy(gr.sizeof_gr_complex)
        self.connect(self, self.input)

        # P(d) = sum(0 to L-1, conj(delayed(r)) * r)
        conj = gr.conjugate_cc()
        mixer = gr.multiply_cc()
        mix_delay = delay(gr.sizeof_gr_complex, fft_length / 2 + 1)
        mix_diff = gr.sub_cc()
        nominator = accumulator_cc()
        inpdelay = delay(gr.sizeof_gr_complex, fft_length / 2)

        self.connect(self.input, inpdelay, conj, (mixer, 0))
        self.connect(self.input, (mixer, 1))
        self.connect(mixer, (mix_diff, 0))
        self.connect(mixer, mix_delay, (mix_diff, 1))
        self.connect(mix_diff, nominator)

        rmagsqrd = gr.complex_to_mag_squared()
        rm_delay = delay(gr.sizeof_float, fft_length + 1)
        rm_diff = gr.sub_ff()
        denom = accumulator_ff()
        self.connect(self.input, rmagsqrd, rm_diff, gr.multiply_const_ff(0.5),
                     denom)
        self.connect(rmagsqrd, rm_delay, (rm_diff, 1))

        ps = gr.complex_to_mag_squared()
        rs = gr.multiply_ff()
        self.connect(nominator, ps)
        self.connect(denom, rs)
        self.connect(denom, (rs, 1))

        div = gr.divide_ff()
        self.connect(ps, div)
        self.connect(rs, (div, 1))

        self.connect(div, self)
예제 #22
0
  def __init__(self, fft_length):
    gr.hier_block2.__init__(self, "schmidl_nominator",
        gr.io_signature(1,1,gr.sizeof_gr_complex),
        gr.io_signature(1,1,gr.sizeof_gr_complex))

    self.input=gr.kludge_copy(gr.sizeof_gr_complex)

    # P(d) = sum(0 to L-1, conj(delayed(r)) * r)
    conj = gr.conjugate_cc()
    mixer = gr.multiply_cc()
    moving_avg = gr.fir_filter_ccf(1,[1.0 for i in range(fft_length/2)])

    self.connect(self, self.input, delay(gr.sizeof_gr_complex,fft_length/2), conj, (mixer,0))
    self.connect(self.input, (mixer,1))
    self.connect(mixer, moving_avg, self)
    # moving_avg = P(d)
    try:
        gr.hier_block.update_var_names(self, "schmidl_nom", vars())
        gr.hier_block.update_var_names(self, "schmidl_nom", vars(self))
    except:
        pass
예제 #23
0
    def __init__(self, fft_length):
        gr.hier_block2.__init__(self, "schmidl_nominator",
                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
                                gr.io_signature(1, 1, gr.sizeof_gr_complex))

        self.input = gr.kludge_copy(gr.sizeof_gr_complex)

        # P(d) = sum(0 to L-1, conj(delayed(r)) * r)
        conj = gr.conjugate_cc()
        mixer = gr.multiply_cc()
        moving_avg = gr.fir_filter_ccf(1, [1.0 for i in range(fft_length / 2)])

        self.connect(self, self.input,
                     delay(gr.sizeof_gr_complex, fft_length / 2), conj,
                     (mixer, 0))
        self.connect(self.input, (mixer, 1))
        self.connect(mixer, moving_avg, self)
        # moving_avg = P(d)
        try:
            gr.hier_block.update_var_names(self, "schmidl_nom", vars())
            gr.hier_block.update_var_names(self, "schmidl_nom", vars(self))
        except:
            pass
예제 #24
0
    def __init__(self, fft_length, pn_weights):
        gr.hier_block2.__init__(self, "modified_timing_metric",
                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
                                gr.io_signature(1, 1, gr.sizeof_float))

        assert (len(pn_weights) == fft_length)

        self.input = gr.kludge_copy(gr.sizeof_gr_complex)
        self.connect(self, self.input)

        # P(d) = sum(0 to L-1, conj(delayed(r)) * r)
        conj = gr.conjugate_cc()
        mixer = gr.multiply_cc()
        nominator = gr.fir_filter_ccf(1, [
            pn_weights[fft_length - i - 1] * pn_weights[fft_length / 2 - i - 1]
            for i in range(fft_length / 2)
        ])

        self.connect(self.input, delay(gr.sizeof_gr_complex, fft_length / 2),
                     conj, (mixer, 0))
        self.connect(self.input, (mixer, 1))
        self.connect(mixer, nominator)
        # moving_avg = P(d)

        # R(d)
        denominator = schmidl_denominator(fft_length)

        # |P(d)| ** 2 / (R(d)) ** 2
        p_mag_sqrd = gr.complex_to_mag_squared()
        r_sqrd = gr.multiply_ff()
        self.timing_metric = gr.divide_ff()

        self.connect(nominator, p_mag_sqrd, (self.timing_metric, 0))
        self.connect(self.input, denominator, (r_sqrd, 0))
        self.connect(denominator, (r_sqrd, 1))
        self.connect(r_sqrd, (self.timing_metric, 1))
        self.connect(self.timing_metric, self)
예제 #25
0
    def __init__(self, fft_length):
        gr.hier_block2.__init__(self, "timing_metric",
                                gr.io_signature(1, 1, gr.sizeof_gr_complex),
                                gr.io_signature(1, 1, gr.sizeof_float))

        self.input = gr.kludge_copy(gr.sizeof_gr_complex)
        self.connect(self, self.input)

        # P(d)
        nominator = schmidl_nominator(fft_length)

        # R(d)
        denominator = schmidl_denominator(fft_length)

        # |P(d)| ** 2 / (R(d)) ** 2
        p_mag_sqrd = gr.complex_to_mag_squared()
        r_sqrd = gr.multiply_ff()
        self.timing_metric = gr.divide_ff()

        self.connect(self.input, nominator, p_mag_sqrd,
                     (self.timing_metric, 0))
        self.connect(self.input, denominator, (r_sqrd, 0))
        self.connect(denominator, (r_sqrd, 1))
        self.connect(r_sqrd, (self.timing_metric, 1))
        self.connect(self.timing_metric, self)

        # calculate epsilon from P(d), epsilon is normalized fractional frequency offset
        #angle = gr.complex_to_arg()
        #self.epsilon = gr.multiply_const_ff(1.0/math.pi)
        #self.connect(nominator, angle, self.epsilon)

        try:
            gr.hier_block.update_var_names(self, "schmidl", vars())
            gr.hier_block.update_var_names(self, "schmidl", vars(self))
        except:
            pass
예제 #26
0
    def __init__(self, item_size, num_inputs, num_outputs, input_index,
                 output_index):
        """
		Selector constructor.
		@param item_size the size of the gr data stream in bytes
		@param num_inputs the number of inputs (integer)
		@param num_outputs the number of outputs (integer)
		@param input_index the index for the source data
		@param output_index the index for the destination data
		"""
        gr.hier_block2.__init__(
            self,
            'selector',
            gr.io_signature(num_inputs, num_inputs, item_size),
            gr.io_signature(num_outputs, num_outputs, item_size),
        )
        #terminator blocks for unused inputs and outputs
        self.input_terminators = [
            gr.null_sink(item_size) for i in range(num_inputs)
        ]
        self.output_terminators = [
            gr.head(item_size, 0) for i in range(num_outputs)
        ]
        self.copy = gr.kludge_copy(item_size)
        #connections
        for i in range(num_inputs):
            self.connect((self, i), self.input_terminators[i])
        for i in range(num_outputs):
            self.connect(gr.null_source(item_size), self.output_terminators[i],
                         (self, i))
        self.item_size = item_size
        self.input_index = input_index
        self.output_index = output_index
        self.num_inputs = num_inputs
        self.num_outputs = num_outputs
        self._connect_current()
    def __init__ (self, fg, demod_rate, audio_decimation):
        """
        Hierarchical block for demodulating a broadcast FM signal.
        
        The input is the downconverted complex baseband signal (gr_complex).
        The output is two streams of the demodulated audio (float) 0=Left, 1=Right.
        
        @param fg: flow graph.
        @type fg: flow graph
        @param demod_rate: input sample rate of complex baseband input.
        @type demod_rate: float
        @param audio_decimation: how much to decimate demod_rate to get to audio.
        @type audio_decimation: integer
        """

        bandwidth = 200e3
        audio_rate = demod_rate / audio_decimation


        # We assign to self so that outsiders can grab the demodulator 
        # if they need to.  E.g., to plot its output.
        #
        # input: complex; output: float
        alpha = 0.25*bandwidth * math.pi / demod_rate
        beta = alpha * alpha / 4.0
        max_freq = 2.0*math.pi*100e3/demod_rate
            
        self.fm_demod = gr.pll_freqdet_cf (alpha,beta,max_freq,-max_freq)

        # input: float; output: float
        self.deemph_Left  = fm_deemph (fg, audio_rate)
        self.deemph_Right = fm_deemph (fg, audio_rate)
        
        # compute FIR filter taps for audio filter
        width_of_transition_band = audio_rate / 32
        audio_coeffs = gr.firdes.low_pass (1.0 ,         # gain
                                           demod_rate,      # sampling rate
                                           15000 ,
                                           width_of_transition_band,
                                           gr.firdes.WIN_HAMMING)
        # input: float; output: float
        self.audio_filter = gr.fir_filter_fff (audio_decimation, audio_coeffs)
        if 1:
            # Pick off the stereo carrier/2 with this filter. It attenuated 10 dB so apply 10 dB gain
            # We pick off the negative frequency half because we want to base band by it!
            ##  NOTE  THIS WAS HACKED TO OFFSET INSERTION LOSS DUE TO DEEMPHASIS

            stereo_carrier_filter_coeffs = gr.firdes.complex_band_pass(10.0,
                                                                   demod_rate,
                                                                   -19020,
                                                                   -18980,
                                                                   width_of_transition_band,
                                                                   gr.firdes.WIN_HAMMING)
            
            #print "len stereo carrier filter = ",len(stereo_carrier_filter_coeffs)
            #print "stereo carrier filter ", stereo_carrier_filter_coeffs
            #print "width of transition band = ",width_of_transition_band, " audio rate = ", audio_rate

            # Pick off the double side band suppressed carrier Left-Right audio. It is attenuated 10 dB so apply 10 dB gain

            stereo_dsbsc_filter_coeffs = gr.firdes.complex_band_pass(20.0,
                                                                     demod_rate,
                                                                     38000-15000/2,
                                                                     38000+15000/2,
                                                                     width_of_transition_band,
                                                                     gr.firdes.WIN_HAMMING)
            #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs)
            #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs
            # construct overlap add filter system from coefficients for stereo carrier

            self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs)

            # carrier is twice the picked off carrier so arrange to do a commplex multiply

            self.stereo_carrier_generator = gr.multiply_cc();

            # Pick off the rds signal

            stereo_rds_filter_coeffs = gr.firdes.complex_band_pass(30.0,
                                                                     demod_rate,
                                                                     57000 - 1500,
                                                                     57000 + 1500,
                                                                     width_of_transition_band,
                                                                     gr.firdes.WIN_HAMMING)
            #print "len stereo dsbsc filter = ",len(stereo_dsbsc_filter_coeffs)
            #print "stereo dsbsc filter ", stereo_dsbsc_filter_coeffs
            # construct overlap add filter system from coefficients for stereo carrier

            self.stereo_carrier_filter = gr.fir_filter_fcc(audio_decimation, stereo_carrier_filter_coeffs)
	    self.rds_signal_filter = gr.fir_filter_fcc(audio_decimation, stereo_rds_filter_coeffs)






	    self.rds_carrier_generator = gr.multiply_cc();
	    self.rds_signal_generator = gr.multiply_cc();
	    self_rds_signal_processor = gr.null_sink(gr.sizeof_gr_complex);



            alpha = 5 * 0.25 * math.pi / (audio_rate)
            beta = alpha * alpha / 4.0
            max_freq = -2.0*math.pi*18990/audio_rate;
            min_freq = -2.0*math.pi*19010/audio_rate;
            
            self.stereo_carrier_pll_recovery = gr.pll_refout_cc(alpha,beta,max_freq,min_freq);
            #self.stereo_carrier_pll_recovery.squelch_enable(False) #pll_refout does not have squelch yet, so disabled for now 
            

            # set up mixer (multiplier) to get the L-R signal at baseband

            self.stereo_basebander = gr.multiply_cc();

            # pick off the real component of the basebanded L-R signal.  The imaginary SHOULD be zero

            self.LmR_real = gr.complex_to_real();
            self.Make_Left = gr.add_ff();
            self.Make_Right = gr.sub_ff();
            
            self.stereo_dsbsc_filter = gr.fir_filter_fcc(audio_decimation, stereo_dsbsc_filter_coeffs)


        if 1:

            # send the real signal to complex filter to pick off the carrier and then to one side of a multiplier
            fg.connect (self.fm_demod,self.stereo_carrier_filter,self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,0))
            # send the already filtered carrier to the otherside of the carrier
            fg.connect (self.stereo_carrier_pll_recovery, (self.stereo_carrier_generator,1))
            # the resulting signal from this multiplier is the carrier with correct phase but at -38000 Hz.

            # send the new carrier to one side of the mixer (multiplier)
            fg.connect (self.stereo_carrier_generator, (self.stereo_basebander,0))
            # send the demphasized audio to the DSBSC pick off filter,  the complex
            # DSBSC signal at +38000 Hz is sent to the other side of the mixer/multiplier
            fg.connect (self.fm_demod,self.stereo_dsbsc_filter, (self.stereo_basebander,1))
            # the result is BASEBANDED DSBSC with phase zero!

            # Pick off the real part since the imaginary is theoretically zero and then to one side of a summer
            fg.connect (self.stereo_basebander, self.LmR_real, (self.Make_Left,0))
            #take the same real part of the DSBSC baseband signal and send it to negative side of a subtracter
            fg.connect (self.LmR_real,(self.Make_Right,1))

	    # Make rds carrier by taking the squared pilot tone and multiplying by pilot tone
	    fg.connect (self.stereo_basebander,(self.rds_carrier_generator,0))
            fg.connect (self.stereo_carrier_pll_recovery,(self.rds_carrier_generator,1)) 
	    # take signal, filter off rds,  send into mixer 0 channel
	    fg.connect (self.fm_demod,self.rds_signal_filter,(self.rds_signal_generator,0))
            # take rds_carrier_generator output and send into mixer 1 channel
	    fg.connect (self.rds_carrier_generator,(self.rds_signal_generator,1))
	    # send basebanded rds signal and send into "processor" which for now is a null sink
	    fg.connect (self.rds_signal_generator,self_rds_signal_processor)
	    

        if 1:
            # pick off the audio, L+R that is what we used to have and send it to the summer
            fg.connect(self.fm_demod, self.audio_filter, (self.Make_Left, 1))
            # take the picked off L+R audio and send it to the PLUS side of the subtractor
            fg.connect(self.audio_filter,(self.Make_Right, 0))
            # The result of  Make_Left  gets    (L+R) +  (L-R) and results in 2*L
            # The result of Make_Right gets  (L+R) - (L-R) and results in 2*R


            # kludge the signals into a stereo channel
            kludge = gr.kludge_copy(gr.sizeof_float)
            fg.connect(self.Make_Left , self.deemph_Left, (kludge, 0))
            fg.connect(self.Make_Right, self.deemph_Right, (kludge, 1))

           #send it to the audio system
            gr.hier_block.__init__(self,
                                   fg,
                                   self.fm_demod,       # head of the pipeline
                                   kludge)              # tail of the pipeline
        else:
            fg.connect (self.fm_demod, self.audio_filter)
            gr.hier_block.__init__(self,
                                   fg,
                                   self.fm_demod,       # head of the pipeline
                                   self.audio_filter)   # tail of the pipeline
예제 #28
0
    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"
예제 #29
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
예제 #30
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
예제 #31
0
	def __init__(self, mode, debug=False):
		"""
		OFDM time and coarse frequency synchronisation for DAB

		@param mode DAB mode (1-4)
		@param debug if True: write data streams out to files
		"""

		if mode<1 or mode>4:
			raise ValueError, "Invalid DAB mode: "+str(mode)+" (modes 1-4 exist)"

		# get the correct DAB parameters
		dp = parameters.dab_parameters(mode)
		rp = parameters.receiver_parameters(mode)
		
		gr.hier_block2.__init__(self,"ofdm_sync_dab",
		                        gr.io_signature(1, 1, gr.sizeof_gr_complex), # input signature
					gr.io_signature2(2, 2, gr.sizeof_gr_complex, gr.sizeof_char)) # output signature

		# workaround for a problem that prevents connecting more than one block directly (see trac ticket #161)
		self.input = gr.kludge_copy(gr.sizeof_gr_complex)
		self.connect(self, self.input)

		#
		# null-symbol detection
		#
		# (outsourced to detect_zero.py)
		
		self.ns_detect = detect_null.detect_null(dp.ns_length, debug)
		self.connect(self.input, self.ns_detect)

		#
		# fine frequency synchronisation
		#

		# the code for fine frequency synchronisation is adapted from
		# ofdm_sync_ml.py; it abuses the cyclic prefix to find the fine
		# frequency error, as suggested in "ML Estimation of Timing and
		# Frequency Offset in OFDM Systems", by Jan-Jaap van de Beek,
		# Magnus Sandell, Per Ola Börjesson, see
		# http://www.sm.luth.se/csee/sp/research/report/bsb96r.html

		self.ffs_delay = gr.delay(gr.sizeof_gr_complex, dp.fft_length)
		self.ffs_conj = gr.conjugate_cc()
		self.ffs_mult = gr.multiply_cc()
		# self.ffs_moving_sum = gr.fir_filter_ccf(1, [1]*dp.cp_length)
		self.ffs_moving_sum = dab_swig.moving_sum_cc(dp.cp_length)
		self.ffs_angle = gr.complex_to_arg()
		self.ffs_angle_scale = gr.multiply_const_ff(1./dp.fft_length)
		self.ffs_delay_sample_and_hold = gr.delay(gr.sizeof_char, dp.symbol_length) # sample the value at the end of the symbol ..
		self.ffs_sample_and_hold = gr.sample_and_hold_ff()
		self.ffs_delay_input_for_correction = gr.delay(gr.sizeof_gr_complex, dp.symbol_length) # by delaying the input, we can use the ff offset estimation from the first symbol to correct the first symbol itself
		self.ffs_nco = gr.frequency_modulator_fc(1) # ffs_sample_and_hold directly outputs phase error per sample
		self.ffs_mixer = gr.multiply_cc()

		# calculate fine frequency error
		self.connect(self.input, self.ffs_conj, self.ffs_mult)
		self.connect(self.input, self.ffs_delay, (self.ffs_mult, 1))
		self.connect(self.ffs_mult, self.ffs_moving_sum, self.ffs_angle)
		# only use the value from the first half of the first symbol
		self.connect(self.ffs_angle, self.ffs_angle_scale, (self.ffs_sample_and_hold, 0))
		self.connect(self.ns_detect, self.ffs_delay_sample_and_hold, (self.ffs_sample_and_hold, 1))
		# do the correction
		self.connect(self.ffs_sample_and_hold, self.ffs_nco, (self.ffs_mixer, 0))
		self.connect(self.input, self.ffs_delay_input_for_correction, (self.ffs_mixer, 1))

		# output - corrected signal and start of DAB frames
		self.connect(self.ffs_mixer, (self, 0))
		self.connect(self.ffs_delay_sample_and_hold, (self, 1))

		if debug:
			self.connect(self.ffs_angle, gr.file_sink(gr.sizeof_float, "debug/ofdm_sync_dab_ffs_angle.dat"))
			self.connect(self.ffs_sample_and_hold, gr.multiply_const_ff(1./(dp.T*2*pi)), gr.file_sink(gr.sizeof_float, "debug/ofdm_sync_dab_fine_freq_err_f.dat"))
			self.connect(self.ffs_mixer, gr.file_sink(gr.sizeof_gr_complex, "debug/ofdm_sync_dab_fine_freq_corrected_c.dat"))
예제 #32
0
  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()
예제 #33
0
  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"
예제 #34
0
    def __init__(self, dab_params, rx_params, debug=False):
        """
		OFDM time and coarse frequency synchronisation for DAB

		@param mode DAB mode (1-4)
		@param debug if True: write data streams out to files
		"""

        dp = dab_params
        rp = rx_params

        gr.hier_block2.__init__(
            self,
            "ofdm_sync_dab",
            gr.io_signature(1, 1, gr.sizeof_gr_complex),  # input signature
            gr.io_signature2(2, 2, gr.sizeof_gr_complex,
                             gr.sizeof_char))  # output signature

        # workaround for a problem that prevents connecting more than one block directly (see trac ticket #161)
        self.input = gr.kludge_copy(gr.sizeof_gr_complex)
        self.connect(self, self.input)

        #
        # null-symbol detection
        #
        # (outsourced to detect_zero.py)

        self.ns_detect = detect_null.detect_null(dp.ns_length, debug)
        self.connect(self.input, self.ns_detect)

        #
        # fine frequency synchronisation
        #

        # the code for fine frequency synchronisation is adapted from
        # ofdm_sync_ml.py; it abuses the cyclic prefix to find the fine
        # frequency error, as suggested in "ML Estimation of Timing and
        # Frequency Offset in OFDM Systems", by Jan-Jaap van de Beek,
        # Magnus Sandell, Per Ola Börjesson, see
        # http://www.sm.luth.se/csee/sp/research/report/bsb96r.html

        self.ffs_delay = blocks.delay(gr.sizeof_gr_complex, dp.fft_length)
        self.ffs_conj = blocks.conjugate_cc()
        self.ffs_mult = blocks.multiply_cc()
        self.ffs_moving_sum = dab_swig.moving_sum_cc(dp.cp_length)
        self.ffs_arg = blocks.complex_to_arg()
        self.ffs_sample_and_average = dab_swig.ofdm_ffs_sample(
            dp.symbol_length, dp.fft_length, rp.symbols_for_ffs_estimation,
            rp.ffs_alpha, dp.sample_rate)
        if rp.correct_ffe:
            self.ffs_delay_input_for_correction = blocks.delay(
                gr.sizeof_gr_complex,
                dp.symbol_length * rp.symbols_for_ffs_estimation
            )  # by delaying the input, we can use the ff offset estimation from the first symbol to correct the first symbol itself
            self.ffs_delay_frame_start = blocks.delay(
                gr.sizeof_char,
                dp.symbol_length * rp.symbols_for_ffs_estimation
            )  # sample the value at the end of the symbol ..
            self.ffs_nco = analog.frequency_modulator_fc(
                1
            )  # ffs_sample_and_hold directly outputs phase error per sample
            self.ffs_mixer = blocks.multiply_cc()

        # calculate fine frequency error
        self.connect(self.input, self.ffs_conj, self.ffs_mult)
        self.connect(self.input, self.ffs_delay, (self.ffs_mult, 1))
        self.connect(self.ffs_mult, self.ffs_moving_sum, self.ffs_arg,
                     (self.ffs_sample_and_average, 0))
        self.connect(self.ns_detect, (self.ffs_sample_and_average, 1))

        if rp.correct_ffe:
            # do the correction
            self.connect(self.ffs_sample_and_average, self.ffs_nco,
                         (self.ffs_mixer, 0))
            self.connect(self.input, self.ffs_delay_input_for_correction,
                         (self.ffs_mixer, 1))
            # output - corrected signal and start of DAB frames
            self.connect(self.ffs_mixer, (self, 0))
            self.connect(self.ns_detect, self.ffs_delay_frame_start, (self, 1))
        else:
            # just patch the signal through
            self.connect(self.ffs_sample_and_average,
                         blocks.null_sink(gr.sizeof_float))
            self.connect(self.input, (self, 0))
            # frame start still needed ..
            self.connect(self.ns_detect, (self, 1))

        if debug:
            self.connect(
                self.ffs_sample_and_average,
                blocks.multiply_const_ff(1. / (dp.T * 2 * pi)),
                gr.file_sink(gr.sizeof_float,
                             "debug/ofdm_sync_dab_fine_freq_err_f.dat"))
            self.connect(
                self.ffs_mixer,
                blocks.file_sink(
                    gr.sizeof_gr_complex,
                    "debug/ofdm_sync_dab_fine_freq_corrected_c.dat"))
예제 #35
0
    def __init__(self, mode, debug=False):
        """
		OFDM time and coarse frequency synchronisation for DAB

		@param mode DAB mode (1-4)
		@param debug if True: write data streams out to files
		"""

        if mode < 1 or mode > 4:
            raise ValueError, "Invalid DAB mode: " + str(
                mode) + " (modes 1-4 exist)"

        # get the correct DAB parameters
        dp = parameters.dab_parameters(mode)
        rp = parameters.receiver_parameters(mode)

        gr.hier_block2.__init__(
            self,
            "ofdm_sync_dab",
            gr.io_signature(1, 1, gr.sizeof_gr_complex),  # input signature
            gr.io_signature2(2, 2, gr.sizeof_gr_complex,
                             gr.sizeof_char))  # output signature

        # workaround for a problem that prevents connecting more than one block directly (see trac ticket #161)
        self.input = gr.kludge_copy(gr.sizeof_gr_complex)
        self.connect(self, self.input)

        #
        # null-symbol detection
        #
        # (outsourced to detect_zero.py)

        self.ns_detect = detect_null.detect_null(dp.ns_length, debug)
        self.connect(self.input, self.ns_detect)

        #
        # fine frequency synchronisation
        #

        # the code for fine frequency synchronisation is adapted from
        # ofdm_sync_ml.py; it abuses the cyclic prefix to find the fine
        # frequency error, as suggested in "ML Estimation of Timing and
        # Frequency Offset in OFDM Systems", by Jan-Jaap van de Beek,
        # Magnus Sandell, Per Ola Börjesson, see
        # http://www.sm.luth.se/csee/sp/research/report/bsb96r.html

        self.ffs_delay = gr.delay(gr.sizeof_gr_complex, dp.fft_length)
        self.ffs_conj = gr.conjugate_cc()
        self.ffs_mult = gr.multiply_cc()
        # self.ffs_moving_sum = gr.fir_filter_ccf(1, [1]*dp.cp_length)
        self.ffs_moving_sum = dab_swig.moving_sum_cc(dp.cp_length)
        self.ffs_angle = gr.complex_to_arg()
        self.ffs_angle_scale = gr.multiply_const_ff(1. / dp.fft_length)
        self.ffs_delay_sample_and_hold = gr.delay(
            gr.sizeof_char,
            dp.symbol_length)  # sample the value at the end of the symbol ..
        self.ffs_sample_and_hold = gr.sample_and_hold_ff()
        self.ffs_delay_input_for_correction = gr.delay(
            gr.sizeof_gr_complex, dp.symbol_length
        )  # by delaying the input, we can use the ff offset estimation from the first symbol to correct the first symbol itself
        self.ffs_nco = gr.frequency_modulator_fc(
            1)  # ffs_sample_and_hold directly outputs phase error per sample
        self.ffs_mixer = gr.multiply_cc()

        # calculate fine frequency error
        self.connect(self.input, self.ffs_conj, self.ffs_mult)
        self.connect(self.input, self.ffs_delay, (self.ffs_mult, 1))
        self.connect(self.ffs_mult, self.ffs_moving_sum, self.ffs_angle)
        # only use the value from the first half of the first symbol
        self.connect(self.ffs_angle, self.ffs_angle_scale,
                     (self.ffs_sample_and_hold, 0))
        self.connect(self.ns_detect, self.ffs_delay_sample_and_hold,
                     (self.ffs_sample_and_hold, 1))
        # do the correction
        self.connect(self.ffs_sample_and_hold, self.ffs_nco,
                     (self.ffs_mixer, 0))
        self.connect(self.input, self.ffs_delay_input_for_correction,
                     (self.ffs_mixer, 1))

        # output - corrected signal and start of DAB frames
        self.connect(self.ffs_mixer, (self, 0))
        self.connect(self.ffs_delay_sample_and_hold, (self, 1))

        if debug:
            self.connect(
                self.ffs_angle,
                gr.file_sink(gr.sizeof_float,
                             "debug/ofdm_sync_dab_ffs_angle.dat"))
            self.connect(
                self.ffs_sample_and_hold,
                gr.multiply_const_ff(1. / (dp.T * 2 * pi)),
                gr.file_sink(gr.sizeof_float,
                             "debug/ofdm_sync_dab_fine_freq_err_f.dat"))
            self.connect(
                self.ffs_mixer,
                gr.file_sink(gr.sizeof_gr_complex,
                             "debug/ofdm_sync_dab_fine_freq_corrected_c.dat"))
예제 #36
0
파일: ofdm.py 프로젝트: ariendj/gr-dab
	def __init__(self, dab_params, rx_params, verbose=False, debug=False):
		"""
		Hierarchical block for OFDM demodulation

		@param dab_params DAB parameter object (dab.parameters.dab_parameters)
		@param rx_params RX parameter object (dab.parameters.receiver_parameters)
		@param debug enables debug output to files
		@param verbose whether to produce verbose messages
		"""

		self.dp = dp = dab_params
		self.rp = rp = rx_params
		self.verbose = verbose

		if self.rp.softbits:
			gr.hier_block2.__init__(self,"ofdm_demod",
						gr.io_signature (1, 1, gr.sizeof_gr_complex), # input signature
						gr.io_signature2(2, 2, gr.sizeof_float*self.dp.num_carriers*2, gr.sizeof_char)) # output signature
		else:
			gr.hier_block2.__init__(self,"ofdm_demod",
						gr.io_signature (1, 1, gr.sizeof_gr_complex), # input signature
						gr.io_signature2(2, 2, gr.sizeof_char*self.dp.num_carriers/4, gr.sizeof_char)) # output signature

		

		# workaround for a problem that prevents connecting more than one block directly (see trac ticket #161)
		self.input = gr.kludge_copy(gr.sizeof_gr_complex)
		self.connect(self, self.input)
		
		# input filtering
		if self.rp.input_fft_filter: 
			if verbose: print "--> RX filter enabled"
			lowpass_taps = gr.firdes_low_pass(1.0,                     # gain
							  dp.sample_rate,          # sampling rate
							  rp.filt_bw,              # cutoff frequency
							  rp.filt_tb,              # width of transition band
							  gr.firdes.WIN_HAMMING)   # Hamming window
			self.fft_filter = gr.fft_filter_ccc(1, lowpass_taps)
		

		# correct sample rate offset, if enabled
		if self.rp.autocorrect_sample_rate:
			if verbose: print "--> dynamic sample rate correction enabled"
			self.rate_detect_ns = detect_null.detect_null(dp.ns_length, False)
			self.rate_estimator = dab_swig.estimate_sample_rate_bf(dp.sample_rate, dp.frame_length)
			self.rate_prober = gr.probe_signal_f()
			self.connect(self.input, self.rate_detect_ns, self.rate_estimator, self.rate_prober)
			# self.resample = gr.fractional_interpolator_cc(0, 1)
			self.resample = dab_swig.fractional_interpolator_triggered_update_cc(0,1)
			self.connect(self.rate_detect_ns, (self.resample,1))
			self.updater = Timer(0.1,self.update_correction)
			# self.updater = threading.Thread(target=self.update_correction)
			self.run_interpolater_update_thread = True
			self.updater.setDaemon(True)
			self.updater.start()
		else:
			self.run_interpolater_update_thread = False
			if self.rp.sample_rate_correction_factor != 1:
				if verbose: print "--> static sample rate correction enabled"
				self.resample = gr.fractional_interpolator_cc(0, self.rp.sample_rate_correction_factor)

		# timing and fine frequency synchronisation
		self.sync = ofdm_sync_dab2.ofdm_sync_dab(self.dp, self.rp, debug)

		# ofdm symbol sampler
		self.sampler = dab_swig.ofdm_sampler(dp.fft_length, dp.cp_length, dp.symbols_per_frame, rp.cp_gap)
		
		# fft for symbol vectors
		self.fft = gr.fft_vcc(dp.fft_length, True, [], True)

		# coarse frequency synchronisation
		self.cfs = dab_swig.ofdm_coarse_frequency_correct(dp.fft_length, dp.num_carriers, dp.cp_length)

		# diff phasor
		self.phase_diff = dab_swig.diff_phasor_vcc(dp.num_carriers)

		# remove pilot symbol
		self.remove_pilot = dab_swig.ofdm_remove_first_symbol_vcc(dp.num_carriers)

		# magnitude equalisation
		if self.rp.equalize_magnitude:
			if verbose: print "--> magnitude equalization enabled"
			self.equalizer = dab_swig.magnitude_equalizer_vcc(dp.num_carriers, rp.symbols_for_magnitude_equalization)

		# frequency deinterleaving
		self.deinterleave = dab_swig.frequency_interleaver_vcc(dp.frequency_deinterleaving_sequence_array)
		
		# symbol demapping
		self.demapper = dab_swig.qpsk_demapper_vcb(dp.num_carriers)

		#
		# connect everything
		#

		if self.rp.autocorrect_sample_rate or self.rp.sample_rate_correction_factor != 1:
			self.connect(self.input, self.resample)
			self.input2 = self.resample
		else:
			self.input2 = self.input
		if self.rp.input_fft_filter:
			self.connect(self.input2, self.fft_filter, self.sync)
		else:
			self.connect(self.input2, self.sync)

		# data stream
		self.connect((self.sync, 0), (self.sampler, 0), self.fft, (self.cfs, 0), self.phase_diff, (self.remove_pilot,0))
		if self.rp.equalize_magnitude:
			self.connect((self.remove_pilot,0), (self.equalizer,0), self.deinterleave)
		else:
			self.connect((self.remove_pilot,0), self.deinterleave)
		if self.rp.softbits:
			if verbose: print "--> using soft bits"
			self.softbit_interleaver = dab_swig.complex_to_interleaved_float_vcf(self.dp.num_carriers)
			self.connect(self.deinterleave, self.softbit_interleaver, (self,0))
		else:
			self.connect(self.deinterleave, self.demapper, (self,0))

		# control stream
		self.connect((self.sync, 1), (self.sampler, 1), (self.cfs, 1), (self.remove_pilot,1))
		if self.rp.equalize_magnitude:
			self.connect((self.remove_pilot,1), (self.equalizer,1), (self,1))
		else:
			self.connect((self.remove_pilot,1), (self,1))
			
		# calculate an estimate of the SNR
		self.phase_var_decim   = gr.keep_one_in_n(gr.sizeof_gr_complex*self.dp.num_carriers, self.rp.phase_var_estimate_downsample)
		self.phase_var_arg     = gr.complex_to_arg(dp.num_carriers)
		self.phase_var_v2s     = gr.vector_to_stream(gr.sizeof_float, dp.num_carriers)
		self.phase_var_mod     = dab_swig.modulo_ff(pi/2)
		self.phase_var_avg_mod = gr.iir_filter_ffd([rp.phase_var_estimate_alpha], [0,1-rp.phase_var_estimate_alpha]) 
		self.phase_var_sub_avg = gr.sub_ff()
		self.phase_var_sqr     = gr.multiply_ff()
		self.phase_var_avg     = gr.iir_filter_ffd([rp.phase_var_estimate_alpha], [0,1-rp.phase_var_estimate_alpha]) 
		self.probe_phase_var   = gr.probe_signal_f()
		self.connect((self.remove_pilot,0), self.phase_var_decim, self.phase_var_arg, self.phase_var_v2s, self.phase_var_mod, (self.phase_var_sub_avg,0), (self.phase_var_sqr,0))
		self.connect(self.phase_var_mod, self.phase_var_avg_mod, (self.phase_var_sub_avg,1))
		self.connect(self.phase_var_sub_avg, (self.phase_var_sqr,1))
		self.connect(self.phase_var_sqr, self.phase_var_avg, self.probe_phase_var)

		# measure processing rate
		self.measure_rate = dab_swig.measure_processing_rate(gr.sizeof_gr_complex, 2000000) 
		self.connect(self.input, self.measure_rate)

		# debugging
		if debug:
			self.connect(self.fft, gr.file_sink(gr.sizeof_gr_complex*dp.fft_length, "debug/ofdm_after_fft.dat"))
			self.connect((self.cfs,0), gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_after_cfs.dat"))
			self.connect(self.phase_diff, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_diff_phasor.dat"))
			self.connect((self.remove_pilot,0), gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_pilot_removed.dat"))
			self.connect((self.remove_pilot,1), gr.file_sink(gr.sizeof_char, "debug/ofdm_after_cfs_trigger.dat"))
			self.connect(self.deinterleave, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_deinterleaved.dat"))
			if self.rp.equalize_magnitude:
				self.connect(self.equalizer, gr.file_sink(gr.sizeof_gr_complex*dp.num_carriers, "debug/ofdm_equalizer.dat"))
			if self.rp.softbits:
				self.connect(self.softbit_interleaver, gr.file_sink(gr.sizeof_float*dp.num_carriers*2, "debug/softbits.dat"))
예제 #37
0
	def __init__(self, dab_params, rx_params, debug=False):
		"""
		OFDM time and coarse frequency synchronisation for DAB

		@param mode DAB mode (1-4)
		@param debug if True: write data streams out to files
		"""

		dp = dab_params
		rp = rx_params
		
		gr.hier_block2.__init__(self,"ofdm_sync_dab",
		                        gr.io_signature(1, 1, gr.sizeof_gr_complex), # input signature
					gr.io_signature2(2, 2, gr.sizeof_gr_complex, gr.sizeof_char)) # output signature

		# workaround for a problem that prevents connecting more than one block directly (see trac ticket #161)
		self.input = gr.kludge_copy(gr.sizeof_gr_complex)
		self.connect(self, self.input)

		#
		# null-symbol detection
		#
		# (outsourced to detect_zero.py)
		
		self.ns_detect = detect_null.detect_null(dp.ns_length, debug)
		self.connect(self.input, self.ns_detect)

		#
		# fine frequency synchronisation
		#

		# the code for fine frequency synchronisation is adapted from
		# ofdm_sync_ml.py; it abuses the cyclic prefix to find the fine
		# frequency error, as suggested in "ML Estimation of Timing and
		# Frequency Offset in OFDM Systems", by Jan-Jaap van de Beek,
		# Magnus Sandell, Per Ola Börjesson, see
		# http://www.sm.luth.se/csee/sp/research/report/bsb96r.html

		self.ffs_delay = gr.delay(gr.sizeof_gr_complex, dp.fft_length)
		self.ffs_conj = gr.conjugate_cc()
		self.ffs_mult = gr.multiply_cc()
		self.ffs_moving_sum = dab_swig.moving_sum_cc(dp.cp_length)
		self.ffs_arg = gr.complex_to_arg()
		self.ffs_sample_and_average = dab_swig.ofdm_ffs_sample(dp.symbol_length, dp.fft_length, rp.symbols_for_ffs_estimation, rp.ffs_alpha, dp.sample_rate)
		if rp.correct_ffe:
			self.ffs_delay_input_for_correction = gr.delay(gr.sizeof_gr_complex, dp.symbol_length*rp.symbols_for_ffs_estimation) # by delaying the input, we can use the ff offset estimation from the first symbol to correct the first symbol itself
			self.ffs_delay_frame_start = gr.delay(gr.sizeof_char, dp.symbol_length*rp.symbols_for_ffs_estimation) # sample the value at the end of the symbol ..
			self.ffs_nco = gr.frequency_modulator_fc(1) # ffs_sample_and_hold directly outputs phase error per sample
			self.ffs_mixer = gr.multiply_cc()

		# calculate fine frequency error
		self.connect(self.input, self.ffs_conj, self.ffs_mult)
		self.connect(self.input, self.ffs_delay, (self.ffs_mult, 1))
		self.connect(self.ffs_mult, self.ffs_moving_sum, self.ffs_arg, (self.ffs_sample_and_average, 0))
		self.connect(self.ns_detect, (self.ffs_sample_and_average, 1))

		if rp.correct_ffe: 
			# do the correction
			self.connect(self.ffs_sample_and_average, self.ffs_nco, (self.ffs_mixer, 0))
			self.connect(self.input, self.ffs_delay_input_for_correction, (self.ffs_mixer, 1))
			# output - corrected signal and start of DAB frames
			self.connect(self.ffs_mixer, (self, 0))
			self.connect(self.ns_detect, self.ffs_delay_frame_start, (self, 1))
		else: 
			# just patch the signal through
			self.connect(self.ffs_sample_and_average, gr.null_sink(gr.sizeof_float))
			self.connect(self.input, (self,0))
			# frame start still needed ..
			self.connect(self.ns_detect, (self,1))

		if debug:
			self.connect(self.ffs_sample_and_average, gr.multiply_const_ff(1./(dp.T*2*pi)), gr.file_sink(gr.sizeof_float, "debug/ofdm_sync_dab_fine_freq_err_f.dat"))
			self.connect(self.ffs_mixer, gr.file_sink(gr.sizeof_gr_complex, "debug/ofdm_sync_dab_fine_freq_corrected_c.dat"))
예제 #38
0
  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 )