diff options
author | Ben Reynwar <ben@reynwar.net> | 2013-05-19 02:55:33 -0700 |
---|---|---|
committer | Ben Reynwar <ben@reynwar.net> | 2013-05-19 02:55:33 -0700 |
commit | bb01988e75d50d82cbb44c1a49c86c1d08f05665 (patch) | |
tree | 0528dac14476d37f2cde7374a8fcb3428f879c69 /gr-digital/python | |
parent | e4f0319eced22c112f7e6a4cc45bc2036d285332 (diff) | |
parent | 0fa219774dcf9141ae91204f948c029b05673f3f (diff) |
Merged in next_docs branch.
Diffstat (limited to 'gr-digital/python')
-rw-r--r-- | gr-digital/python/digital/ofdm_txrx.py | 280 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_crc32_bb.py | 20 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_ofdm_carrier_allocator_cvc.py | 18 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_ofdm_chanest_vcvc.py | 23 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_ofdm_cyclic_prefixer.py | 4 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_ofdm_frame_equalizer_vcvc.py | 231 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_ofdm_serializer_vcc.py | 122 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_ofdm_sync_sc_cfb.py | 133 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_ofdm_txrx.py | 120 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_packet_headergenerator_bb.py | 24 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_packet_headerparser_b.py | 16 | ||||
-rw-r--r-- | gr-digital/python/digital/utils/tagged_streams.py | 4 |
12 files changed, 704 insertions, 291 deletions
diff --git a/gr-digital/python/digital/ofdm_txrx.py b/gr-digital/python/digital/ofdm_txrx.py index 37c4086cc3..8f1b4c5f02 100644 --- a/gr-digital/python/digital/ofdm_txrx.py +++ b/gr-digital/python/digital/ofdm_txrx.py @@ -1,5 +1,5 @@ # -# Copyright 2005-2007,2013 Free Software Foundation, Inc. +# Copyright 2013 Free Software Foundation, Inc. # # This file is part of GNU Radio # @@ -25,6 +25,9 @@ For simple configurations, no need to connect all the relevant OFDM blocks to form an OFDM Tx/Rx--simply use these. """ +# Reminder: All frequency-domain stuff is in shifted form, i.e. DC carrier +# in the middle! + import numpy from gnuradio import gr import digital_swig as digital @@ -44,20 +47,59 @@ except ImportError: _def_fft_len = 64 _def_cp_len = 16 _def_frame_length_tag_key = "frame_length" -_def_packet_length_tag_key = "frame_length" -_def_packet_num_tag_key = "" -_def_occupied_carriers=(range(1, 27) + range(38, 64),) -_def_pilot_carriers=((0,),) -_def_pilot_symbols=((100,),) +_def_packet_length_tag_key = "packet_length" +_def_packet_num_tag_key = "packet_num" +# Data and pilot carriers are same as in 802.11a +_def_occupied_carriers = (range(-26, -21) + range(-20, -7) + range(-6, 0) + range(1, 7) + range(8, 21) + range(22, 27),) +_def_pilot_carriers=((-21, -7, 7, 21,),) +_pilot_sym_scramble_seq = ( + 1,1,1,1, -1,-1,-1,1, -1,-1,-1,-1, 1,1,-1,1, -1,-1,1,1, -1,1,1,-1, 1,1,1,1, 1,1,-1,1, + 1,1,-1,1, 1,-1,-1,1, 1,1,-1,1, -1,-1,-1,1, -1,1,-1,-1, 1,-1,-1,1, 1,1,1,1, -1,-1,1,1, + -1,-1,1,-1, 1,-1,1,1, -1,-1,-1,1, 1,-1,-1,-1, -1,1,-1,-1, 1,-1,1,1, 1,1,-1,1, -1,1,-1,1, + -1,-1,-1,-1, -1,1,-1,1, 1,-1,1,-1, 1,1,1,-1, -1,1,-1,-1, -1,1,1,1, -1,-1,-1,-1, -1,-1,-1 +) +_def_pilot_symbols= tuple([(x, x, x, -x) for x in _pilot_sym_scramble_seq]) _seq_seed = 42 -def _make_sync_word(fft_len, occupied_carriers, constellation): - """ Makes a random sync sequence """ - occupied_carriers = list(occupied_carriers[0]) - occupied_carriers = [occupied_carriers[x] + fft_len if occupied_carriers[x] < 0 else occupied_carriers[x] for x in range(len(occupied_carriers))] + +def _get_active_carriers(fft_len, occupied_carriers, pilot_carriers): + active_carriers = list() + for carrier in list(occupied_carriers[0]) + list(pilot_carriers[0]): + if carrier < 0: + carrier += fft_len + active_carriers.append(carrier) + return active_carriers + +def _make_sync_word1(fft_len, occupied_carriers, pilot_carriers): + """ Creates a random sync sequence for fine frequency offset and timing + estimation. This is the first of typically two sync preamble symbols + for the Schmidl & Cox sync algorithm. + The relevant feature of this symbols is that every second sub-carrier + is zero. In the time domain, this results in two identical halves of + the OFDM symbols. + Symbols are always BPSK symbols. Carriers are scaled by sqrt(2) to keep + total energy constant. + Carrier 0 (DC carrier) is always zero. If used, carrier 1 is non-zero. + This means the sync algorithm has to check on odd carriers! + """ + active_carriers = _get_active_carriers(fft_len, occupied_carriers, pilot_carriers) numpy.random.seed(_seq_seed) - sync_sequence = [constellation.map_to_points_v(numpy.random.randint(constellation.arity()))[0] * numpy.sqrt(2) if x in occupied_carriers and x % 3 else 0 for x in range(fft_len)] - return sync_sequence + bpsk = {0: numpy.sqrt(2), 1: -numpy.sqrt(2)} + sw1 = [bpsk[numpy.random.randint(2)] if x in active_carriers and x % 2 else 0 for x in range(fft_len)] + return numpy.fft.fftshift(sw1) + +def _make_sync_word2(fft_len, occupied_carriers, pilot_carriers): + """ Creates a random sync sequence for coarse frequency offset and channel + estimation. This is the second of typically two sync preamble symbols + for the Schmidl & Cox sync algorithm. + Symbols are always BPSK symbols. + """ + active_carriers = _get_active_carriers(fft_len, occupied_carriers, pilot_carriers) + numpy.random.seed(_seq_seed) + bpsk = {0: 1, 1: -1} + sw2 = [bpsk[numpy.random.randint(2)] if x in active_carriers else 0 for x in range(fft_len)] + sw2[0] = 0j + return numpy.fft.fftshift(sw2) def _get_constellation(bps): """ Returns a modulator block for a given number of bits per symbol """ @@ -73,22 +115,28 @@ def _get_constellation(bps): exit(1) class ofdm_tx(gr.hier_block2): - """ - Hierarchical block for OFDM modulation. + """ Hierarchical block for OFDM modulation. The input is a byte stream (unsigned char) and the output is the complex modulated signal at baseband. Args: fft_len: The length of FFT (integer). - cp_len: The length of cyclic prefix (integer). - occupied_carriers: ?? - pilot_carriers: ?? - pilot_symbols: ?? - length_tag_key: The name of the tag giving packet length. + cp_len: The length of cyclic prefix in total samples (integer). + packet_length_tag_key: The name of the tag giving packet length at the input. + occupied_carriers: A vector of vectors describing which OFDM carriers are occupied. + pilot_carriers: A vector of vectors describing which OFDM carriers are occupied with pilot symbols. + pilot_symbols: The pilot symbols. + bps_header: Bits per symbol (header). + bps_payload: Bits per symbol (payload). + sync_word1: The first sync preamble symbol. This has to be with zeros on alternating carriers. + Used for fine and coarse frequency offset and timing estimation. + sync_word2: The second sync preamble symbol. This has to be filled entirely. Also used for + coarse frequency offset and channel estimation. + rolloff: The rolloff length in samples. Must be smaller than the CP. """ def __init__(self, fft_len=_def_fft_len, cp_len=_def_cp_len, - frame_length_tag_key=_def_frame_length_tag_key, + packet_length_tag_key=_def_packet_length_tag_key, occupied_carriers=_def_occupied_carriers, pilot_carriers=_def_pilot_carriers, pilot_symbols=_def_pilot_symbols, @@ -96,84 +144,112 @@ class ofdm_tx(gr.hier_block2): bps_payload=1, sync_word1=None, sync_word2=None, - rolloff=0 + rolloff=0, + debug_log=False ): gr.hier_block2.__init__(self, "ofdm_tx", gr.io_signature(1, 1, gr.sizeof_char), gr.io_signature(1, 1, gr.sizeof_gr_complex)) + ### Param init / sanity check ######################################## self.fft_len = fft_len self.cp_len = cp_len - self.frame_length_tag_key = frame_length_tag_key + self.packet_length_tag_key = packet_length_tag_key self.occupied_carriers = occupied_carriers self.pilot_carriers = pilot_carriers self.pilot_symbols = pilot_symbols self.bps_header = bps_header self.bps_payload = bps_payload n_sync_words = 1 - header_constellation = _get_constellation(bps_header) - header_mod = digital.chunks_to_symbols_bc(header_constellation.points()) self.sync_word1 = sync_word1 if sync_word1 is None: - self.sync_word1 = _make_sync_word(fft_len, occupied_carriers, header_constellation) + self.sync_word1 = _make_sync_word1(fft_len, occupied_carriers, pilot_carriers) else: if len(sync_word1) != self.fft_len: raise ValueError("Length of sync sequence(s) must be FFT length.") - self.sync_words = [sync_word1,] + self.sync_words = [self.sync_word1,] self.sync_word2 = () - if sync_word2 is not None: - if len(sync_word2) != fft_len: + if sync_word2 is None: + self.sync_word2 = _make_sync_word2(fft_len, occupied_carriers, pilot_carriers) + if len(self.sync_word2): + if len(self.sync_word2) != fft_len: raise ValueError("Length of sync sequence(s) must be FFT length.") - self.sync_word2 = sync_word2 + self.sync_word2 = list(self.sync_word2) n_sync_words = 2 self.sync_words.append(self.sync_word2) - crc = digital.crc32_bb(False, self.frame_length_tag_key) + ### Header modulation ################################################ + crc = digital.crc32_bb(False, self.packet_length_tag_key) + header_constellation = _get_constellation(bps_header) + header_mod = digital.chunks_to_symbols_bc(header_constellation.points()) formatter_object = digital.packet_header_ofdm( occupied_carriers=occupied_carriers, n_syms=1, - bits_per_sym=self.bps_header + bits_per_header_sym=self.bps_header, + bits_per_payload_sym=self.bps_payload ) - header_gen = digital.packet_headergenerator_bb(formatter_object.base(), self.frame_length_tag_key) - header_payload_mux = blocks.tagged_stream_mux(gr.sizeof_gr_complex*1, self.frame_length_tag_key) + header_gen = digital.packet_headergenerator_bb(formatter_object.base(), self.packet_length_tag_key) + header_payload_mux = blocks.tagged_stream_mux(gr.sizeof_gr_complex*1, self.packet_length_tag_key) self.connect(self, crc, header_gen, header_mod, (header_payload_mux, 0)) + if debug_log: + self.connect(header_gen, blocks.file_sink(1, 'tx-hdr.dat')) + ### Payload modulation ############################################### payload_constellation = _get_constellation(bps_payload) payload_mod = digital.chunks_to_symbols_bc(payload_constellation.points()) self.connect( crc, - blocks.repack_bits_bb(8, bps_payload, frame_length_tag_key), + blocks.repack_bits_bb( + 8, # Unpack 8 bits per byte + bps_payload, + self.packet_length_tag_key + ), payload_mod, (header_payload_mux, 1) ) + ### Create OFDM frame ################################################ allocator = digital.ofdm_carrier_allocator_cvc( self.fft_len, occupied_carriers=self.occupied_carriers, pilot_carriers=self.pilot_carriers, pilot_symbols=self.pilot_symbols, sync_words=self.sync_words, - len_tag_key=self.frame_length_tag_key + len_tag_key=self.packet_length_tag_key + ) + ffter = fft.fft_vcc( + self.fft_len, + False, # Inverse FFT + (), # No window + True # Shift ) - ffter = fft.fft_vcc(self.fft_len, False, (), True) cyclic_prefixer = digital.ofdm_cyclic_prefixer( self.fft_len, self.fft_len+self.cp_len, rolloff, - self.frame_length_tag_key + self.packet_length_tag_key ) self.connect(header_payload_mux, allocator, ffter, cyclic_prefixer, self) + if debug_log: + self.connect(allocator, blocks.file_sink(8*64, 'tx-post-allocator.dat')) + self.connect(cyclic_prefixer, blocks.file_sink(8, 'tx-signal.dat')) class ofdm_rx(gr.hier_block2): - """ - Hierarchical block for OFDM demodulation. + """ Hierarchical block for OFDM demodulation. - The input is a byte stream (unsigned char) and the - output is the complex modulated signal at baseband. + The input is a complex baseband signal (e.g. from a UHD source). + The detected packets are output as a stream of packed bits on the output. Args: fft_len: The length of FFT (integer). - cp_len: The length of cyclic prefix (integer). - occupied_carriers: ?? - pilot_carriers: ?? - pilot_symbols: ?? - length_tag_key: The name of the tag giving packet length. + cp_len: The length of cyclic prefix in total samples (integer). + frame_length_tag_key: Used internally to tag the length of the OFDM frame. + packet_length_tag_key: The name of the tag giving packet length at the input. + occupied_carriers: A vector of vectors describing which OFDM carriers are occupied. + pilot_carriers: A vector of vectors describing which OFDM carriers are occupied with pilot symbols. + pilot_symbols: The pilot symbols. + bps_header: Bits per symbol (header). + bps_payload: Bits per symbol (payload). + sync_word1: The first sync preamble symbol. This has to be with zeros on alternating carriers. + Used for fine and coarse frequency offset and timing estimation. + sync_word2: The second sync preamble symbol. This has to be filled entirely. Also used for + coarse frequency offset and channel estimation. """ def __init__(self, fft_len=_def_fft_len, cp_len=_def_cp_len, frame_length_tag_key=_def_frame_length_tag_key, @@ -185,11 +261,13 @@ class ofdm_rx(gr.hier_block2): bps_header=1, bps_payload=1, sync_word1=None, - sync_word2=None + sync_word2=None, + debug_log=False ): gr.hier_block2.__init__(self, "ofdm_rx", gr.io_signature(1, 1, gr.sizeof_gr_complex), gr.io_signature(1, 1, gr.sizeof_char)) + ### Param init / sanity check ######################################## self.fft_len = fft_len self.cp_len = cp_len self.frame_length_tag_key = frame_length_tag_key @@ -198,44 +276,60 @@ class ofdm_rx(gr.hier_block2): self.bps_header = bps_header self.bps_payload = bps_payload n_sync_words = 1 - header_constellation = _get_constellation(bps_header) if sync_word1 is None: - self.sync_word1 = _make_sync_word(fft_len, occupied_carriers, header_constellation) + self.sync_word1 = _make_sync_word1(fft_len, occupied_carriers, pilot_carriers) else: if len(sync_word1) != self.fft_len: raise ValueError("Length of sync sequence(s) must be FFT length.") self.sync_word1 = sync_word1 self.sync_word2 = () - if sync_word2 is not None: + if sync_word2 is None: + self.sync_word2 = _make_sync_word2(fft_len, occupied_carriers, pilot_carriers) + n_sync_words = 2 + elif len(sync_word2): if len(sync_word2) != fft_len: raise ValueError("Length of sync sequence(s) must be FFT length.") self.sync_word2 = sync_word2 n_sync_words = 2 - else: - sync_word2 = () - # Receiver path + ### Sync ############################################################ sync_detect = digital.ofdm_sync_sc_cfb(fft_len, cp_len) + delay = blocks.delay(gr.sizeof_gr_complex, fft_len+cp_len) oscillator = analog.frequency_modulator_fc(-2.0 / fft_len) - delay = gr.delay(gr.sizeof_gr_complex, fft_len+cp_len) - mixer = gr.multiply_cc() - hpd = digital.header_payload_demux(n_sync_words, fft_len, cp_len, - frame_length_tag_key, "", True) + mixer = blocks.multiply_cc() + hpd = digital.header_payload_demux( + n_sync_words+1, # Number of OFDM symbols before payload (sync + 1 sym header) + fft_len, cp_len, # FFT length, guard interval + frame_length_tag_key, # Frame length tag key + "", # We're not using trigger tags + True # One output item is one OFDM symbol (False would output complex scalars) + ) self.connect(self, sync_detect) - self.connect((sync_detect, 0), oscillator, (mixer, 0)) - self.connect(self, delay, (mixer, 1)) - self.connect(mixer, (hpd, 0)) + self.connect(self, delay, (mixer, 0), (hpd, 0)) + self.connect((sync_detect, 0), oscillator, (mixer, 1)) self.connect((sync_detect, 1), (hpd, 1)) - # Header demodulation - header_fft = fft.fft_vcc(self.fft_len, True, (), True) - chanest = digital.ofdm_chanest_vcvc(self.sync_word1, self.sync_word2, 1) - header_equalizer = digital.ofdm_equalizer_simpledfe( - fft_len, header_constellation.base(), - occupied_carriers, pilot_carriers, pilot_symbols - ) - header_eq = digital.ofdm_frame_equalizer_vcvc(header_equalizer.base(), frame_length_tag_key, True) - header_serializer = digital.ofdm_serializer_vcc(fft_len, occupied_carriers) + if debug_log: + self.connect((sync_detect, 0), blocks.file_sink(gr.sizeof_float, 'freq-offset.dat')) + self.connect((sync_detect, 1), blocks.file_sink(gr.sizeof_char, 'sync-detect.dat')) + ### Header demodulation ############################################## + header_fft = fft.fft_vcc(self.fft_len, True, (), True) + chanest = digital.ofdm_chanest_vcvc(self.sync_word1, self.sync_word2, 1) header_constellation = _get_constellation(bps_header) - header_demod = digital.constellation_decoder_cb(header_constellation.base()) + header_equalizer = digital.ofdm_equalizer_simpledfe( + fft_len, header_constellation.base(), + occupied_carriers, pilot_carriers, pilot_symbols, 0, 0 + ) + header_eq = digital.ofdm_frame_equalizer_vcvc( + header_equalizer.base(), + cp_len, + self.frame_length_tag_key, + True, + 1 # Header is 1 symbol long + ) + header_serializer = digital.ofdm_serializer_vcc( + fft_len, occupied_carriers, + self.frame_length_tag_key + ) + header_demod = digital.constellation_decoder_cb(header_constellation.base()) header_formatter = digital.packet_header_ofdm( occupied_carriers, 1, packet_length_tag_key, @@ -246,16 +340,46 @@ class ofdm_rx(gr.hier_block2): header_parser = digital.packet_headerparser_b(header_formatter.formatter()) self.connect((hpd, 0), header_fft, chanest, header_eq, header_serializer, header_demod, header_parser) self.msg_connect(header_parser, "header_data", hpd, "header_data") - # Payload demodulation + if debug_log: + self.connect((chanest, 1), blocks.file_sink(512, 'channel-estimate.dat')) + self.connect((chanest, 0), blocks.file_sink(512, 'post-hdr-chanest.dat')) + self.connect(header_eq, blocks.file_sink(512, 'post-hdr-eq.dat')) + self.connect(header_serializer, blocks.file_sink(8, 'post-hdr-serializer.dat')) + self.connect(header_demod, blocks.file_sink(1, 'post-hdr-demod.dat')) + self.connect(header_demod, blocks.tag_debug(1, 'post-hdr-demod.dat')) + ### Payload demod #################################################### payload_fft = fft.fft_vcc(self.fft_len, True, (), True) + payload_constellation = _get_constellation(bps_payload) payload_equalizer = digital.ofdm_equalizer_simpledfe( - fft_len, header_constellation.base(), - occupied_carriers, pilot_carriers, pilot_symbols, 1 + fft_len, payload_constellation.base(), + occupied_carriers, + pilot_carriers, + pilot_symbols, + 1 # Skip 1 symbol (that was already in the header) + ) + payload_eq = digital.ofdm_frame_equalizer_vcvc( + payload_equalizer.base(), + cp_len, + self.frame_length_tag_key + ) + payload_serializer = digital.ofdm_serializer_vcc( + fft_len, occupied_carriers, + self.frame_length_tag_key, + self.packet_length_tag_key, + 1 # Skip 1 symbol (that was already in the header) ) - payload_eq = digital.ofdm_frame_equalizer_vcvc(payload_equalizer.base(), frame_length_tag_key) - payload_serializer = digital.ofdm_serializer_vcc(fft_len, occupied_carriers) - payload_constellation = _get_constellation(bps_payload) payload_demod = digital.constellation_decoder_cb(payload_constellation.base()) - bit_packer = blocks.repack_bits_bb(bps_payload, 8, packet_length_tag_key, True) - self.connect((hpd, 1), payload_fft, payload_eq, payload_serializer, payload_demod, bit_packer, self) + repack = blocks.repack_bits_bb(bps_payload, 8, self.packet_length_tag_key, True) + crc = digital.crc32_bb(True, self.packet_length_tag_key) + self.connect((hpd, 1), payload_fft, payload_eq, payload_serializer, payload_demod, repack, crc, self) + if debug_log: + self.connect((hpd, 1), blocks.tag_debug(8*64, 'post-hpd')); + self.connect(payload_fft, blocks.file_sink(8*64, 'post-payload-fft.dat')) + self.connect(payload_eq, blocks.file_sink(8*64, 'post-payload-eq.dat')) + self.connect(payload_serializer, blocks.file_sink(8, 'post-payload-serializer.dat')) + self.connect(payload_demod, blocks.file_sink(1, 'post-payload-demod.dat')) + self.connect(repack, blocks.file_sink(1, 'post-payload-repack.dat')) + self.connect(crc, blocks.file_sink(1, 'post-payload-crc.dat')) + self.connect(crc, blocks.tag_debug(1, 'post-payload-crc')) + diff --git a/gr-digital/python/digital/qa_crc32_bb.py b/gr-digital/python/digital/qa_crc32_bb.py index 397834ffde..167470d420 100755 --- a/gr-digital/python/digital/qa_crc32_bb.py +++ b/gr-digital/python/digital/qa_crc32_bb.py @@ -34,7 +34,7 @@ class qa_crc32_bb (gr_unittest.TestCase): """ Make sure the output of a CRC set is 4 bytes longer than the input. """ data = range(16) tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(data)) @@ -51,7 +51,7 @@ class qa_crc32_bb (gr_unittest.TestCase): is the same as the input. """ data = (0, 1, 2, 3, 4, 5, 6, 7, 8) tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(data)) @@ -68,23 +68,23 @@ class qa_crc32_bb (gr_unittest.TestCase): tag_name = "length" pack_len = 8 packets = range(pack_len*2) - tag1 = gr.gr_tag_t() + tag1 = gr.tag_t() tag1.offset = 0 tag1.key = pmt.string_to_symbol(tag_name) tag1.value = pmt.from_long(pack_len) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = pack_len tag2.key = pmt.string_to_symbol(tag_name) tag2.value = pmt.from_long(pack_len) - testtag1 = gr.gr_tag_t() + testtag1 = gr.tag_t() testtag1.offset = 1 testtag1.key = pmt.string_to_symbol("tag1") testtag1.value = pmt.from_long(0) - testtag2 = gr.gr_tag_t() + testtag2 = gr.tag_t() testtag2.offset = pack_len testtag2.key = pmt.string_to_symbol("tag2") testtag2.value = pmt.from_long(0) - testtag3 = gr.gr_tag_t() + testtag3 = gr.tag_t() testtag3.offset = len(packets)-1 testtag3.key = pmt.string_to_symbol("tag3") testtag3.value = pmt.from_long(0) @@ -110,7 +110,7 @@ class qa_crc32_bb (gr_unittest.TestCase): """ Corrupt the data and make sure it fails CRC test. """ data = (0, 1, 2, 3, 4, 5, 6, 7) tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(data)) @@ -128,11 +128,11 @@ class qa_crc32_bb (gr_unittest.TestCase): """ Make sure tags on the CRC aren't lost. """ data = (0, 1, 2, 3, 4, 5, 6, 7, 8, 230, 166, 39, 8) tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(data)) - testtag = gr.gr_tag_t() + testtag = gr.tag_t() testtag.offset = len(data)-1 testtag.key = pmt.string_to_symbol('tag1') testtag.value = pmt.from_long(0) diff --git a/gr-digital/python/digital/qa_ofdm_carrier_allocator_cvc.py b/gr-digital/python/digital/qa_ofdm_carrier_allocator_cvc.py index 56cd7a617e..5c95e6e335 100755 --- a/gr-digital/python/digital/qa_ofdm_carrier_allocator_cvc.py +++ b/gr-digital/python/digital/qa_ofdm_carrier_allocator_cvc.py @@ -44,7 +44,7 @@ class qa_digital_carrier_allocator_cvc (gr_unittest.TestCase): expected_result = tuple(sync_word[0] + [1j, 0, 0, 1, 2, 3]) # ^ DC carrier tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(tx_symbols)) @@ -72,7 +72,7 @@ class qa_digital_carrier_allocator_cvc (gr_unittest.TestCase): expected_result = (1j, 0, 1, 2, 3) # ^ DC carrier tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(tx_symbols)) @@ -98,7 +98,7 @@ class qa_digital_carrier_allocator_cvc (gr_unittest.TestCase): pilot_carriers = ((3,),) expected_result = (1j, 0, 1, 0, 2, 3) tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(tx_symbols)) @@ -132,27 +132,27 @@ class qa_digital_carrier_allocator_cvc (gr_unittest.TestCase): 0, 13, 1j, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 2j, 0, 0) fft_len = 16 tag_name = "len" - tag1 = gr.gr_tag_t() + tag1 = gr.tag_t() tag1.offset = 0 tag1.key = pmt.string_to_symbol(tag_name) tag1.value = pmt.from_long(len(tx_symbols)) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = len(tx_symbols) tag2.key = pmt.string_to_symbol(tag_name) tag2.value = pmt.from_long(len(tx_symbols)) - testtag1 = gr.gr_tag_t() + testtag1 = gr.tag_t() testtag1.offset = 0 testtag1.key = pmt.string_to_symbol('tag1') testtag1.value = pmt.from_long(0) - testtag2 = gr.gr_tag_t() + testtag2 = gr.tag_t() testtag2.offset = 7 # On the 2nd OFDM symbol testtag2.key = pmt.string_to_symbol('tag2') testtag2.value = pmt.from_long(0) - testtag3 = gr.gr_tag_t() + testtag3 = gr.tag_t() testtag3.offset = len(tx_symbols)+1 # First OFDM symbol of packet 2 testtag3.key = pmt.string_to_symbol('tag3') testtag3.value = pmt.from_long(0) - testtag4 = gr.gr_tag_t() + testtag4 = gr.tag_t() testtag4.offset = 2*len(tx_symbols)-1 # Last OFDM symbol of packet 2 testtag4.key = pmt.string_to_symbol('tag4') testtag4.value = pmt.from_long(0) diff --git a/gr-digital/python/digital/qa_ofdm_chanest_vcvc.py b/gr-digital/python/digital/qa_ofdm_chanest_vcvc.py index c9e2bacd0d..e6b331b2a8 100755 --- a/gr-digital/python/digital/qa_ofdm_chanest_vcvc.py +++ b/gr-digital/python/digital/qa_ofdm_chanest_vcvc.py @@ -20,6 +20,7 @@ # import sys +import numpy import random import numpy @@ -60,11 +61,11 @@ class qa_ofdm_sync_eqinit_vcvc (gr_unittest.TestCase): tx_data = shift_tuple(sync_symbol1, carr_offset) + \ shift_tuple(sync_symbol2, carr_offset) + \ shift_tuple(data_symbol, carr_offset) - tag1 = gr.gr_tag_t() + tag1 = gr.tag_t() tag1.offset = 0 tag1.key = pmt.string_to_symbol("test_tag_1") tag1.value = pmt.from_long(23) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = 2 tag2.key = pmt.string_to_symbol("test_tag_2") tag2.value = pmt.from_long(42) @@ -128,7 +129,9 @@ class qa_ofdm_sync_eqinit_vcvc (gr_unittest.TestCase): chan = blocks.multiply_const_vcc(channel) chanest = digital.ofdm_chanest_vcvc(sync_symbol1, sync_symbol2, 1) sink = blocks.vector_sink_c(fft_len) + sink_chanest = blocks.vector_sink_c(fft_len) self.tb.connect(src, chan, chanest, sink) + self.tb.connect((chanest, 1), sink_chanest) self.tb.run() tags = sink.tags() self.assertEqual(shift_tuple(sink.data(), -carr_offset), tuple(numpy.multiply(data_symbol, channel))) @@ -137,6 +140,7 @@ class qa_ofdm_sync_eqinit_vcvc (gr_unittest.TestCase): self.assertEqual(pmt.to_long(tag.value), carr_offset) if pmt.symbol_to_string(tag.key) == 'ofdm_sync_chan_taps': self.assertEqual(pmt.c32vector_elements(tag.value), channel) + self.assertEqual(sink_chanest.data(), channel) def test_004_channel_no_carroffset_1sym (self): """ Add a channel, check if it's correctly estimated. @@ -146,13 +150,17 @@ class qa_ofdm_sync_eqinit_vcvc (gr_unittest.TestCase): sync_symbol = (0, 0, 0, 1, 0, 1, 0, -1, 0, 1, 0, -1, 0, 1, 0, 0) data_symbol = (0, 0, 0, 1, -1, 1, -1, 1, 0, 1, -1, -1, -1, 1, 0, 0) tx_data = sync_symbol + data_symbol - channel = (0, 0, 0, 2, 2, 2, 2.5, 3, 2.5, 2, 2.5, 3, 2, 1, 1, 0) + channel = (0, 0, 0, 2, 2, 2, 2, 3, 3, 2.5, 2.5, -3, -3, 1j, 1j, 0) + #channel = (0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0) src = blocks.vector_source_c(tx_data, False, fft_len) chan = blocks.multiply_const_vcc(channel) chanest = digital.ofdm_chanest_vcvc(sync_symbol, (), 1) sink = blocks.vector_sink_c(fft_len) + sink_chanest = blocks.vector_sink_c(fft_len) self.tb.connect(src, chan, chanest, sink) + self.tb.connect((chanest, 1), sink_chanest) self.tb.run() + self.assertEqual(sink_chanest.data(), channel) tags = sink.tags() for tag in tags: if pmt.symbol_to_string(tag.key) == 'ofdm_sync_carr_offset': @@ -193,6 +201,7 @@ class qa_ofdm_sync_eqinit_vcvc (gr_unittest.TestCase): data_symbol = (0, 0, 0, 1, -1, 1, -1, 1, 0, 1, -1, -1, -1, 1, 0, 0) # Channel 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 # Shifted (0, 0, 0, 0, 0, 1j, -1, 1, -1j, 1j, 0, 1, -1j, -1, -1j, 1) + chanest_exp = (0, 0, 0, 5, 6, 7, 8, 9, 0, 11, 12, 13, 14, 15, 0, 0) tx_data = shift_tuple(sync_symbol1, carr_offset) + \ shift_tuple(sync_symbol2, carr_offset) + \ shift_tuple(data_symbol, carr_offset) @@ -210,9 +219,7 @@ class qa_ofdm_sync_eqinit_vcvc (gr_unittest.TestCase): self.assertEqual(pmt.to_long(tag.value), carr_offset) if pmt.symbol_to_string(tag.key) == 'ofdm_sync_chan_taps': chan_est = pmt.c32vector_elements(tag.value) - for i in range(fft_len): - if shift_tuple(sync_symbol2, carr_offset)[i]: # Only here the channel can be estimated - self.assertEqual(chan_est[i], channel[i]) + self.assertEqual(chan_est, chanest_exp) self.assertEqual(sink.data(), tuple(numpy.multiply(shift_tuple(data_symbol, carr_offset), channel))) @@ -226,7 +233,7 @@ class qa_ofdm_sync_eqinit_vcvc (gr_unittest.TestCase): wgn_amplitude = 0.05 min_chan_ampl = 0.1 max_chan_ampl = 5 - n_iter = 20 + n_iter = 20 # The more the accurater def run_flow_graph(sync_sym1, sync_sym2, data_sym): top_block = gr.top_block() carr_offset = random.randint(-max_offset/2, max_offset/2) * 2 @@ -252,7 +259,7 @@ class qa_ofdm_sync_eqinit_vcvc (gr_unittest.TestCase): carr_offset_hat = pmt.to_long(tag.value) self.assertEqual(carr_offset, carr_offset_hat) if pmt.symbol_to_string(tag.key) == 'ofdm_sync_chan_taps': - channel_est = pmt.c32vector_elements(tag.value) + channel_est = shift_tuple(pmt.c32vector_elements(tag.value), carr_offset) shifted_carrier_mask = shift_tuple(carrier_mask, carr_offset) for i in range(fft_len): if shifted_carrier_mask[i] and channel_est[i]: diff --git a/gr-digital/python/digital/qa_ofdm_cyclic_prefixer.py b/gr-digital/python/digital/qa_ofdm_cyclic_prefixer.py index 82d4950883..5cb9fae777 100755 --- a/gr-digital/python/digital/qa_ofdm_cyclic_prefixer.py +++ b/gr-digital/python/digital/qa_ofdm_cyclic_prefixer.py @@ -65,11 +65,11 @@ class test_ofdm_cyclic_prefixer (gr_unittest.TestCase): tag_name = "length" expected_result = (7.0/2, 8, 1, 2, 3, 4, 5, 6, 7, 8, # 1.0/2 7.0/2+1.0/2, 8, 1, 2, 3, 4, 5, 6, 7, 8, 1.0/2) - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(2) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = 1 tag2.key = pmt.string_to_symbol("random_tag") tag2.value = pmt.from_long(42) diff --git a/gr-digital/python/digital/qa_ofdm_frame_equalizer_vcvc.py b/gr-digital/python/digital/qa_ofdm_frame_equalizer_vcvc.py index 3b3b896b83..1cdb8ed9a4 100755 --- a/gr-digital/python/digital/qa_ofdm_frame_equalizer_vcvc.py +++ b/gr-digital/python/digital/qa_ofdm_frame_equalizer_vcvc.py @@ -33,32 +33,156 @@ class qa_ofdm_frame_equalizer_vcvc (gr_unittest.TestCase): self.tb = None def test_001_simple (self): - """ Very simple functionality testing """ + """ Very simple functionality testing: + - static equalizer + - init channel state with all ones + - transmit all ones + - make sure we rx all ones + - Tag check: put in frame length tag and one other random tag, + make sure they're propagated + """ fft_len = 8 equalizer = digital.ofdm_equalizer_static(fft_len) n_syms = 3 len_tag_key = "frame_len" tx_data = (1,) * fft_len * n_syms - len_tag = gr.gr_tag_t() + len_tag = gr.tag_t() len_tag.offset = 0 len_tag.key = pmt.string_to_symbol(len_tag_key) len_tag.value = pmt.from_long(n_syms) - chan_tag = gr.gr_tag_t() + chan_tag = gr.tag_t() chan_tag.offset = 0 chan_tag.key = pmt.string_to_symbol("ofdm_sync_chan_taps") chan_tag.value = pmt.init_c32vector(fft_len, (1,) * fft_len) - src = blocks.vector_source_c(tx_data, False, fft_len, (len_tag, chan_tag)) - eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), len_tag_key) + random_tag = gr.tag_t() + random_tag.offset = 1 + random_tag.key = pmt.string_to_symbol("foo") + random_tag.value = pmt.from_long(42) + src = blocks.vector_source_c(tx_data, False, fft_len, (len_tag, chan_tag, random_tag)) + eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), 0, len_tag_key) sink = blocks.vector_sink_c(fft_len) self.tb.connect(src, eq, sink) self.tb.run () # Check data self.assertEqual(tx_data, sink.data()) + # Check tags + tag_dict = dict() for tag in sink.tags(): - self.assertEqual(pmt.symbol_to_string(tag.key), len_tag_key) - self.assertEqual(pmt.to_long(tag.value), n_syms) + ptag = gr.tag_to_python(tag) + tag_dict[ptag.key] = ptag.value + expected_dict = { + 'frame_len': n_syms, + 'foo': 42 + } + self.assertEqual(tag_dict, expected_dict) + + def test_001b_simple_skip_nothing (self): + """ + Same as before, but put a skip-header in there + """ + fft_len = 8 + equalizer = digital.ofdm_equalizer_static(fft_len, symbols_skipped=1) + n_syms = 3 + len_tag_key = "frame_len" + tx_data = (1,) * fft_len * n_syms + len_tag = gr.tag_t() + len_tag.offset = 0 + len_tag.key = pmt.string_to_symbol(len_tag_key) + len_tag.value = pmt.from_long(n_syms) + chan_tag = gr.tag_t() + chan_tag.offset = 0 + chan_tag.key = pmt.string_to_symbol("ofdm_sync_chan_taps") + chan_tag.value = pmt.init_c32vector(fft_len, (1,) * fft_len) + src = blocks.vector_source_c(tx_data, False, fft_len, (len_tag, chan_tag)) + eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), 0, len_tag_key) + sink = blocks.vector_sink_c(fft_len) + self.tb.connect(src, eq, sink) + self.tb.run () + # Check data + self.assertEqual(tx_data, sink.data()) + + def test_001c_carrier_offset_no_cp (self): + """ + Same as before, but put a carrier offset in there + """ + fft_len = 8 + cp_len = 0 + n_syms = 1 + carr_offset = 1 + occupied_carriers = ((-2, -1, 1, 2),) + tx_data = ( + 0, 0, 0, -1j, -1j, 0, -1j, -1j, + ) + # The rx'd signal is shifted + rx_expected = (0, 0, 1, 1, 0, 1, 1, 0) * n_syms + equalizer = digital.ofdm_equalizer_static(fft_len, occupied_carriers) + len_tag_key = "frame_len" + len_tag = gr.tag_t() + len_tag.offset = 0 + len_tag.key = pmt.string_to_symbol(len_tag_key) + len_tag.value = pmt.from_long(n_syms) + chan_tag = gr.tag_t() + chan_tag.offset = 0 + chan_tag.key = pmt.string_to_symbol("ofdm_sync_chan_taps") + # Note: this is shifted to the correct position! + chan_tag.value = pmt.init_c32vector(fft_len, (0, 0, -1j, -1j, 0, -1j, -1j, 0)) + offset_tag = gr.tag_t() + offset_tag.offset = 0 + offset_tag.key = pmt.string_to_symbol("ofdm_sync_carr_offset") + offset_tag.value = pmt.from_long(carr_offset) + src = blocks.vector_source_c(tx_data, False, fft_len, (len_tag, chan_tag, offset_tag)) + eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), cp_len, len_tag_key) + sink = blocks.vector_sink_c(fft_len) + self.tb.connect(src, eq, sink) + self.tb.run () + # Check data + self.assertComplexTuplesAlmostEqual(rx_expected, sink.data(), places=4) + + def test_001c_carrier_offset_cp (self): + """ + Same as before, but put a carrier offset in there and a CP + """ + fft_len = 8 + cp_len = 2 + n_syms = 3 + # cp_len/fft_len == 1/4, therefore, the phase is rotated by + # carr_offset * \pi/2 in every symbol + occupied_carriers = ((-2, -1, 1, 2),) + carr_offset = -1 + tx_data = ( + 0,-1j,-1j, 0,-1j,-1j, 0, 0, + 0, -1, -1, 0, -1, -1, 0, 0, + 0, 1j, 1j, 0, 1j, 1j, 0, 0, + ) + # Rx'd signal is corrected + rx_expected = (0, 0, 1, 1, 0, 1, 1, 0) * n_syms + equalizer = digital.ofdm_equalizer_static(fft_len, occupied_carriers) + len_tag_key = "frame_len" + len_tag = gr.tag_t() + len_tag.offset = 0 + len_tag.key = pmt.string_to_symbol(len_tag_key) + len_tag.value = pmt.from_long(n_syms) + chan_tag = gr.tag_t() + chan_tag.offset = 0 + chan_tag.key = pmt.string_to_symbol("ofdm_sync_chan_taps") + chan_tag.value = pmt.init_c32vector(fft_len, (0, 0, 1, 1, 0, 1, 1, 0)) + offset_tag = gr.tag_t() + offset_tag.offset = 0 + offset_tag.key = pmt.string_to_symbol("ofdm_sync_carr_offset") + offset_tag.value = pmt.from_long(carr_offset) + src = blocks.vector_source_c(tx_data, False, fft_len, (len_tag, chan_tag, offset_tag)) + eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), cp_len, len_tag_key) + sink = blocks.vector_sink_c(fft_len) + self.tb.connect(src, eq, sink) + self.tb.run () + # Check data + self.assertComplexTuplesAlmostEqual(rx_expected, sink.data(), places=4) def test_002_static (self): + """ + - Add a simple channel + - Make symbols QPSK + """ fft_len = 8 # 4 5 6 7 0 1 2 3 tx_data = [-1, -1, 1, 2, -1, 3, 0, -1, # 0 @@ -75,35 +199,97 @@ class qa_ofdm_frame_equalizer_vcvc (gr_unittest.TestCase): equalizer = digital.ofdm_equalizer_static(fft_len, occupied_carriers, pilot_carriers, pilot_symbols) channel = [ 0, 0, 1, 1, 0, 1, 1, 0, - 0, 0, 1, 1, 0, 1, 1, 0, # These coefficients will be rotated slightly... + 0, 0, 1, 1, 0, 1, 1, 0, # These coefficients will be rotated slightly (but less than \pi/2) 0, 0, 1j, 1j, 0, 1j, 1j, 0, # Go crazy here! - 0, 0, 1j, 1j, 0, 1j, 1j, 0 # ...and again here. + 0, 0, 1j, 1j, 0, 1j, 1j, 0 + ] + channel = [ + 0, 0, 1, 1, 0, 1, 1, 0, + 0, 0, 1, 1, 0, 1, 1, 0, # These coefficients will be rotated slightly (but less than \pi/2) + 0, 0, 1j, 1j, 0, 1j, 1j, 0, # Go crazy here! + 0, 0, 1j, 1j, 0, 1j, 1j, 0 ] for idx in range(fft_len, 2*fft_len): channel[idx] = channel[idx-fft_len] * numpy.exp(1j * .1 * numpy.pi * (numpy.random.rand()-.5)) - idx2 = idx+2*fft_len - channel[idx2] = channel[idx2] * numpy.exp(1j * 0 * numpy.pi * (numpy.random.rand()-.5)) len_tag_key = "frame_len" - len_tag = gr.gr_tag_t() + len_tag = gr.tag_t() len_tag.offset = 0 len_tag.key = pmt.string_to_symbol(len_tag_key) len_tag.value = pmt.from_long(4) - chan_tag = gr.gr_tag_t() + chan_tag = gr.tag_t() chan_tag.offset = 0 chan_tag.key = pmt.string_to_symbol("ofdm_sync_chan_taps") chan_tag.value = pmt.init_c32vector(fft_len, channel[:fft_len]) src = blocks.vector_source_c(numpy.multiply(tx_signal, channel), False, fft_len, (len_tag, chan_tag)) - eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), len_tag_key, True) sink = blocks.vector_sink_c(fft_len) + eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), 0, len_tag_key, True) self.tb.connect(src, eq, sink) self.tb.run () rx_data = [cnst.decision_maker_v((x,)) if x != 0 else -1 for x in sink.data()] + # Check data self.assertEqual(tx_data, rx_data) + # Check tags + tag_dict = dict() for tag in sink.tags(): - if pmt.symbol_to_string(tag.key) == len_tag_key: - self.assertEqual(pmt.to_long(tag.value), 4) - if pmt.symbol_to_string(tag.key) == "ofdm_sync_chan_taps": - self.assertEqual(list(pmt.c32vector_elements(tag.value)), channel[-fft_len:]) + ptag = gr.tag_to_python(tag) + tag_dict[ptag.key] = ptag.value + if ptag.key == 'ofdm_sync_chan_taps': + tag_dict[ptag.key] = list(pmt.c32vector_elements(tag.value)) + else: + tag_dict[ptag.key] = pmt.to_python(tag.value) + expected_dict = { + 'frame_len': 4, + 'ofdm_sync_chan_taps': channel[-fft_len:] + } + self.assertEqual(tag_dict, expected_dict) + + def test_002_static_wo_tags (self): + """ Same as before, but the input stream has no tag. + We specify the frame size in the constructor. + We also specify a tag key, so the output stream *should* have + a length tag. + """ + fft_len = 8 + n_syms = 4 + # 4 5 6 7 0 1 2 3 + tx_data = [-1, -1, 1, 2, -1, 3, 0, -1, # 0 + -1, -1, 0, 2, -1, 2, 0, -1, # 8 + -1, -1, 3, 0, -1, 1, 0, -1, # 16 (Pilot symbols) + -1, -1, 1, 1, -1, 0, 2, -1] # 24 + cnst = digital.constellation_qpsk() + tx_signal = [cnst.map_to_points_v(x)[0] if x != -1 else 0 for x in tx_data] + occupied_carriers = ((1, 2, 6, 7),) + pilot_carriers = ((), (), (1, 2, 6, 7), ()) + pilot_symbols = ( + [], [], [cnst.map_to_points_v(x)[0] for x in (1, 0, 3, 0)], [] + ) + equalizer = digital.ofdm_equalizer_static(fft_len, occupied_carriers, pilot_carriers, pilot_symbols) + channel = [ + 0, 0, 1, 1, 0, 1, 1, 0, + 0, 0, 1, 1, 0, 1, 1, 0, # These coefficients will be rotated slightly (below)... + 0, 0, 1j, 1j, 0, 1j, 1j, 0, # Go crazy here! + 0, 0, 1j, 1j, 0, 1j, 1j, 0 # ...and again here. + ] + for idx in range(fft_len, 2*fft_len): + channel[idx] = channel[idx-fft_len] * numpy.exp(1j * .1 * numpy.pi * (numpy.random.rand()-.5)) + idx2 = idx+2*fft_len + channel[idx2] = channel[idx2] * numpy.exp(1j * 0 * numpy.pi * (numpy.random.rand()-.5)) + src = gr.vector_source_c(numpy.multiply(tx_signal, channel), False, fft_len) + # We do specify a length tag, it should then appear at the output + eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), 0, "frame_len", False, n_syms) + sink = blocks.vector_sink_c(fft_len) + self.tb.connect(src, eq, sink) + self.tb.run () + rx_data = [cnst.decision_maker_v((x,)) if x != 0 else -1 for x in sink.data()] + self.assertEqual(tx_data, rx_data) + # Check len tag + tags = sink.tags() + len_tag = dict() + for tag in tags: + ptag = gr.tag_to_python(tag) + if ptag.key == 'frame_len': + len_tag[ptag.key] = ptag.value + self.assertEqual(len_tag, {'frame_len': 4}) def test_002_static_wo_tags (self): fft_len = 8 @@ -131,14 +317,15 @@ class qa_ofdm_frame_equalizer_vcvc (gr_unittest.TestCase): idx2 = idx+2*fft_len channel[idx2] = channel[idx2] * numpy.exp(1j * 0 * numpy.pi * (numpy.random.rand()-.5)) src = blocks.vector_source_c(numpy.multiply(tx_signal, channel), False, fft_len) - eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), "", False, 4) sink = blocks.vector_sink_c(fft_len) + eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), 0, "", False, 4) self.tb.connect(src, eq, sink) self.tb.run () rx_data = [cnst.decision_maker_v((x,)) if x != 0 else -1 for x in sink.data()] self.assertEqual(tx_data, rx_data) def test_002_simpledfe (self): + """ Use the simple DFE equalizer. """ fft_len = 8 # 4 5 6 7 0 1 2 3 tx_data = [-1, -1, 1, 2, -1, 3, 0, -1, # 0 @@ -166,16 +353,16 @@ class qa_ofdm_frame_equalizer_vcvc (gr_unittest.TestCase): idx2 = idx+2*fft_len channel[idx2] = channel[idx2] * numpy.exp(1j * 0 * numpy.pi * (numpy.random.rand()-.5)) len_tag_key = "frame_len" - len_tag = gr.gr_tag_t() + len_tag = gr.tag_t() len_tag.offset = 0 len_tag.key = pmt.string_to_symbol(len_tag_key) len_tag.value = pmt.from_long(4) - chan_tag = gr.gr_tag_t() + chan_tag = gr.tag_t() chan_tag.offset = 0 chan_tag.key = pmt.string_to_symbol("ofdm_sync_chan_taps") chan_tag.value = pmt.init_c32vector(fft_len, channel[:fft_len]) src = blocks.vector_source_c(numpy.multiply(tx_signal, channel), False, fft_len, (len_tag, chan_tag)) - eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), len_tag_key, True) + eq = digital.ofdm_frame_equalizer_vcvc(equalizer.base(), 0, len_tag_key, True) sink = blocks.vector_sink_c(fft_len) self.tb.connect(src, eq, sink) self.tb.run () diff --git a/gr-digital/python/digital/qa_ofdm_serializer_vcc.py b/gr-digital/python/digital/qa_ofdm_serializer_vcc.py index 8a5e8f4893..fbef2b1f27 100755 --- a/gr-digital/python/digital/qa_ofdm_serializer_vcc.py +++ b/gr-digital/python/digital/qa_ofdm_serializer_vcc.py @@ -22,7 +22,7 @@ import numpy -from gnuradio import gr, gr_unittest, blocks. fft, analog, digital +from gnuradio import gr, gr_unittest, blocks, fft, analog, digital import pmt class qa_ofdm_serializer_vcc (gr_unittest.TestCase): @@ -36,7 +36,6 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): def test_001_simple (self): """ Standard test """ fft_len = 16 - tx_symbols = range(1, 16); tx_symbols = (0, 1, 1j, 2, 3, 0, 0, 0, 0, 0, 0, 4, 5, 2j, 6, 0, 0, 7, 8, 3j, 9, 0, 0, 0, 0, 0, 0, 10, 4j, 11, 12, 0, 0, 13, 1j, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 2j, 0, 0) @@ -44,12 +43,39 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): occupied_carriers = ((1, 3, 4, 11, 12, 14), (1, 2, 4, 11, 13, 14),) n_syms = len(tx_symbols)/fft_len tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() + tag.offset = 0 + tag.key = pmt.string_to_symbol(tag_name) + tag.value = pmt.from_long(n_syms) + src = blocks.vector_source_c(tx_symbols, False, fft_len, (tag,)) + serializer = digital.ofdm_serializer_vcc(fft_len, occupied_carriers, tag_name, "", 0, "", False) + sink = blocks.vector_sink_c() + self.tb.connect(src, serializer, sink) + self.tb.run () + self.assertEqual(sink.data(), expected_result) + self.assertEqual(len(sink.tags()), 1) + result_tag = sink.tags()[0] + self.assertEqual(pmt.symbol_to_string(result_tag.key), tag_name) + self.assertEqual(pmt.to_long(result_tag.value), n_syms * len(occupied_carriers[0])) + + def test_001b_shifted (self): + """ Same as before, but shifted, because that's the normal mode in OFDM Rx """ + fft_len = 16 + tx_symbols = ( + 0, 0, 0, 0, 0, 0, 1, 2, 0, 3, 4, 5, 0, 0, 0, 0, + 0, 0, 0, 0, 6, 1j, 7, 8, 0, 9, 10, 1j, 11, 0, 0, 0, + 0, 0, 0, 0, 0, 12, 13, 14, 0, 15, 16, 17, 0, 0, 0, 0, + ) + expected_result = tuple(range(18)) + occupied_carriers = ((13, 14, 15, 1, 2, 3), (-4, -2, -1, 1, 2, 4),) + n_syms = len(tx_symbols)/fft_len + tag_name = "len" + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(n_syms) src = blocks.vector_source_c(tx_symbols, False, fft_len, (tag,)) - serializer = digital.ofdm_serializer_vcc(fft_len, occupied_carriers, tag_name, "", 0, False) + serializer = digital.ofdm_serializer_vcc(fft_len, occupied_carriers, tag_name) sink = blocks.vector_sink_c() self.tb.connect(src, serializer, sink) self.tb.run () @@ -71,17 +97,24 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): occupied_carriers = ((1, 3, 4, 11, 12, 14), (1, 2, 4, 11, 13, 14),) n_syms = len(tx_symbols)/fft_len tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(n_syms) - offsettag = gr.gr_tag_t() + offsettag = gr.tag_t() offsettag.offset = 0 offsettag.key = pmt.string_to_symbol("ofdm_sync_carr_offset") offsettag.value = pmt.from_long(carr_offset) src = blocks.vector_source_c(tx_symbols, False, fft_len, (tag, offsettag)) - serializer = digital.ofdm_serializer_vcc(fft_len, occupied_carriers, tag_name, "", 0, False) sink = blocks.vector_sink_c() + serializer = digital.ofdm_serializer_vcc( + fft_len, + occupied_carriers, + tag_name, + "", 0, + "ofdm_sync_carr_offset", + False + ) self.tb.connect(src, serializer, sink) self.tb.run () self.assertEqual(sink.data(), expected_result) @@ -93,29 +126,39 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): def test_003_connect (self): """ Connect carrier_allocator to ofdm_serializer, make sure output==input """ - fft_len = 32 - n_syms = 10 + fft_len = 8 + n_syms = 1 occupied_carriers = ((1, 2, 6, 7),) pilot_carriers = ((3,),(5,)) pilot_symbols = ((1j,),(-1j,)) - sync_word = (range(fft_len),) - tx_data = tuple([numpy.random.randint(0, 10) for x in range(4 * n_syms)]) + #tx_data = tuple([numpy.random.randint(0, 10) for x in range(4 * n_syms)]) + tx_data = (1, 2, 3, 4) tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(tx_data)) src = blocks.vector_source_c(tx_data, False, 1, (tag,)) - alloc = digital.ofdm_carrier_allocator_cvc(fft_len, - occupied_carriers, - pilot_carriers, - pilot_symbols, sync_word, - tag_name) - serializer = digital.ofdm_serializer_vcc(alloc) + alloc = digital.ofdm_carrier_allocator_cvc( + fft_len, + occupied_carriers, + pilot_carriers, + pilot_symbols, + (), # No sync word + tag_name, + True # Output is shifted (default) + ) + serializer = digital.ofdm_serializer_vcc( + alloc, + "", # Len tag key + 0, # Symbols skipped + "", # Carrier offset key + True # Input is shifted (default) + ) sink = blocks.vector_sink_c() self.tb.connect(src, alloc, serializer, sink) self.tb.run () - self.assertEqual(sink.data()[4:], tx_data) + self.assertEqual(sink.data(), tx_data) def test_004_connect (self): """ @@ -126,21 +169,19 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): - Frequency offset is -2 carriers """ fft_len = 8 - n_syms = 2 + n_syms = 1 carr_offset = -2 - freq_offset = 2 * numpy.pi * carr_offset / fft_len # If the sampling rate == 1 - occupied_carriers = ((1, 2, -2, -1),) - pilot_carriers = ((3,),(5,)) + freq_offset = 1.0 / fft_len * carr_offset # Normalized frequency + occupied_carriers = ((-2, -1, 1, 2),) + pilot_carriers = ((-3,),(3,)) pilot_symbols = ((1j,),(-1j,)) - sync_word = (range(fft_len),) - tx_data = tuple([numpy.random.randint(0, 10) for x in range(4 * n_syms)]) - #tx_data = (1,) * occupied_carriers[0] * n_syms + tx_data = (1, 2, 3, 4) tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(len(tx_data)) - offsettag = gr.gr_tag_t() + offsettag = gr.tag_t() offsettag.offset = 0 offsettag.key = pmt.string_to_symbol("ofdm_sync_carr_offset") offsettag.value = pmt.from_long(carr_offset) @@ -148,13 +189,17 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): alloc = digital.ofdm_carrier_allocator_cvc(fft_len, occupied_carriers, pilot_carriers, - pilot_symbols, sync_word, + pilot_symbols, (), tag_name) - tx_ifft = fft.fft_vcc(fft_len, False, ()) - offset_sig = analog.sig_source_c(1.0, analog.GR_COS_WAVE, freq_offset, 1.0) + tx_ifft = fft.fft_vcc(fft_len, False, (1.0/fft_len,)*fft_len, True) + oscillator = analog.sig_source_c(1.0, analog.GR_COS_WAVE, freq_offset, 1.0/fft_len) mixer = blocks.multiply_cc() rx_fft = fft.fft_vcc(fft_len, True, (), True) - serializer = digital.ofdm_serializer_vcc(alloc) + sink2 = blocks.vector_sink_c(fft_len) + self.tb.connect(rx_fft, sink2) + serializer = digital.ofdm_serializer_vcc( + alloc, "", 0, "ofdm_sync_carr_offset", True + ) sink = blocks.vector_sink_c() self.tb.connect( src, alloc, tx_ifft, @@ -163,10 +208,9 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): blocks.stream_to_vector(gr.sizeof_gr_complex, fft_len), rx_fft, serializer, sink ) - self.tb.connect(offset_sig, (mixer, 1)) + self.tb.connect(oscillator, (mixer, 1)) self.tb.run () - # FIXME check this - #self.assertEqual(sink.data(), tx_data) + self.assertComplexTuplesAlmostEqual(sink.data()[-len(occupied_carriers[0]):], tx_data, places=4) def test_005_packet_len_tag (self): """ Standard test """ @@ -179,16 +223,16 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): occupied_carriers = ((1, 3, 4, 11, 12, 14), (1, 2, 4, 11, 13, 14),) n_syms = len(tx_symbols)/fft_len tag_name = "len" - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = 0 tag.key = pmt.string_to_symbol(tag_name) tag.value = pmt.from_long(n_syms) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = 0 tag2.key = pmt.string_to_symbol("packet_len") tag2.value = pmt.from_long(len(expected_result)) src = blocks.vector_source_c(tx_symbols, False, fft_len, (tag, tag2)) - serializer = digital.ofdm_serializer_vcc(fft_len, occupied_carriers, tag_name, "packet_len", 0, False) + serializer = digital.ofdm_serializer_vcc(fft_len, occupied_carriers, tag_name, "packet_len", 0, "", False) sink = blocks.vector_sink_c() self.tb.connect(src, serializer, sink) self.tb.run () @@ -201,7 +245,7 @@ class qa_ofdm_serializer_vcc (gr_unittest.TestCase): def test_099 (self): """ Make sure it fails if it should """ fft_len = 16 - occupied_carriers = ((1, 3, 4, 11, 12, 17),) + occupied_carriers = ((1, 3, 4, 11, 12, 112),) tag_name = "len" self.assertRaises(RuntimeError, digital.ofdm_serializer_vcc, fft_len, occupied_carriers, tag_name) diff --git a/gr-digital/python/digital/qa_ofdm_sync_sc_cfb.py b/gr-digital/python/digital/qa_ofdm_sync_sc_cfb.py index 3e811767cd..ccb6dff439 100755 --- a/gr-digital/python/digital/qa_ofdm_sync_sc_cfb.py +++ b/gr-digital/python/digital/qa_ofdm_sync_sc_cfb.py @@ -23,19 +23,10 @@ import numpy import random -from gnuradio import gr, gr_unittest, blocks, analog - -try: - # This will work when feature #505 is added. - from gnuradio import digital - from gnuradio.digital.utils import tagged_streams - from gnuradio.digital.ofdm_txrx import ofdm_tx -except ImportError: - # Until then this will work. - import digital_swig as digital - from utils import tagged_streams - from ofdm_txrx import ofdm_tx - +from gnuradio import gr, gr_unittest, blocks, analog, channels +from gnuradio import digital +from gnuradio.digital.utils import tagged_streams +from gnuradio.digital.ofdm_txrx import ofdm_tx class qa_ofdm_sync_sc_cfb (gr_unittest.TestCase): @@ -76,26 +67,23 @@ class qa_ofdm_sync_sc_cfb (gr_unittest.TestCase): self.assertEqual(numpy.sum(sig1_detect), 1) self.assertEqual(numpy.sum(sig2_detect), 1) - def test_002_freq (self): """ Add a fine frequency offset and see if that get's detected properly """ fft_len = 32 cp_len = 4 - freq_offset = 0.1 # Must stay < 2*pi/fft_len = 0.196 (otherwise, it's coarse) + # This frequency offset is normalized to rads, i.e. \pi == f_s/2 + max_freq_offset = 2*numpy.pi/fft_len # Otherwise, it's coarse + freq_offset = ((2 * random.random()) - 1) * max_freq_offset sig_len = (fft_len + cp_len) * 10 sync_symbol = [(random.randint(0, 1)*2)-1 for x in range(fft_len/2)] * 2 tx_signal = sync_symbol[-cp_len:] + \ sync_symbol + \ [(random.randint(0, 1)*2)-1 for x in range(sig_len)] - mult = blocks.multiply_cc() - add = blocks.add_cc() - sync = digital.ofdm_sync_sc_cfb(fft_len, cp_len) + sync = digital.ofdm_sync_sc_cfb(fft_len, cp_len, True) sink_freq = blocks.vector_sink_f() sink_detect = blocks.vector_sink_b() - self.tb.connect(blocks.vector_source_c(tx_signal), (mult, 0), (add, 0)) - self.tb.connect(analog.sig_source_c(2 * numpy.pi, analog.GR_SIN_WAVE, freq_offset, 1.0), (mult, 1)) - self.tb.connect(analog.noise_source_c(analog.GR_GAUSSIAN, .01), (add, 1)) - self.tb.connect(add, sync) + channel = channels.channel_model(0.005, freq_offset / 2.0 / numpy.pi) + self.tb.connect(blocks.vector_source_c(tx_signal), channel, sync) self.tb.connect((sync, 0), sink_freq) self.tb.connect((sync, 1), sink_detect) self.tb.run() @@ -103,7 +91,6 @@ class qa_ofdm_sync_sc_cfb (gr_unittest.TestCase): est_freq_offset = 2 * phi_hat / fft_len self.assertAlmostEqual(est_freq_offset, freq_offset, places=2) - def test_003_multiburst (self): """ Send several bursts, see if the number of detects is correct. Burst lengths and content are random. @@ -122,75 +109,55 @@ class qa_ofdm_sync_sc_cfb (gr_unittest.TestCase): sync = digital.ofdm_sync_sc_cfb(fft_len, cp_len) sink_freq = blocks.vector_sink_f() sink_detect = blocks.vector_sink_b() - self.tb.connect(blocks.vector_source_c(tx_signal), (add, 0)) - self.tb.connect(analog.noise_source_c(analog.GR_GAUSSIAN, .005), (add, 1)) - self.tb.connect(add, sync) + channel = channels.channel_model(0.005) + self.tb.connect(blocks.vector_source_c(tx_signal), channel, sync) self.tb.connect((sync, 0), sink_freq) self.tb.connect((sync, 1), sink_detect) self.tb.run() - self.assertEqual(numpy.sum(sink_detect.data()), n_bursts, + n_bursts_detected = numpy.sum(sink_detect.data()) + # We allow for one false alarm or missed burst + self.assertTrue(abs(n_bursts_detected - n_bursts) <= 1, msg="""Because of statistics, it is possible (though unlikely) that the number of detected bursts differs slightly. If the number of detects is off by one or two, run the test again and see what happen. Detection error was: %d """ % (numpy.sum(sink_detect.data()) - n_bursts) ) - # FIXME ofdm_mod is currently not working - #def test_004_ofdm_packets (self): - #""" - #Send several bursts, see if the number of detects is correct. - #Burst lengths and content are random. - #""" - #n_bursts = 42 - #fft_len = 64 - #cp_len = 12 - #tx_signal = [] - #packets = [] - #tagname = "length" - #min_packet_length = 100 - #max_packet_length = 100 - #sync_sequence = [random.randint(0, 1)*2-1 for x in range(fft_len/2)] - #for i in xrange(n_bursts): - #packet_length = random.randint(min_packet_length, - #max_packet_length+1) - #packet = [random.randint(0, 255) for i in range(packet_length)] - #packets.append(packet) - #data, tags = tagged_streams.packets_to_vectors( - #packets, tagname, vlen=1) - #total_length = len(data) - - #src = blocks.vector_source_b(data, False, 1, tags) - #mod = ofdm_tx( - #fft_len=fft_len, - #cp_len=cp_len, - #length_tag_name=tagname, - #occupied_carriers=(range(1, 27) + range(38, 64),), - #pilot_carriers=((0,),), - #pilot_symbols=((100,),), - #) - #rate_in = 16000 - #rate_out = 48000 - #ratio = float(rate_out) / rate_in - #throttle1 = gr.throttle(gr.sizeof_gr_complex, rate_in) - #sink_countbursts = gr.vector_sink_c() - #head = gr.head(gr.sizeof_gr_complex, int(total_length * ratio*2)) - #add = gr.add_cc() - #sync = digital.ofdm_sync_sc_cfb(fft_len, cp_len) - #sink_freq = blocks.vector_sink_f() - #sink_detect = blocks.vector_sink_b() - #noise_level = 0.01 - #noise = gr.noise_source_c(gr.GR_GAUSSIAN, noise_level) - #self.tb.connect(src, mod, blocks.null_sink(gr.sizeof_gr_complex)) - #self.tb.connect(insert_zeros, sink_countbursts) - #self.tb.connect(noise, (add, 1)) - #self.tb.connect(add, sync) - #self.tb.connect((sync, 0), sink_freq) - #self.tb.connect((sync, 1), sink_detect) - #self.tb.run() - #count_data = sink_countbursts.data() - #count_tags = sink_countbursts.tags() - #burstcount = tagged_streams.count_bursts(count_data, count_tags, tagname) - #self.assertEqual(numpy.sum(sink_detect.data()), burstcount) + def test_004_ofdm_packets (self): + """ + Send several bursts using ofdm_tx, see if the number of detects is correct. + Burst lengths and content are random. + """ + n_bursts = 42 + fft_len = 64 + cp_len = 16 + # Here, coarse freq offset is allowed + max_freq_offset = 2*numpy.pi/fft_len * 4 + freq_offset = ((2 * random.random()) - 1) * max_freq_offset + tx_signal = [] + packets = [] + tagname = "packet_length" + min_packet_length = 10 + max_packet_length = 50 + sync_sequence = [random.randint(0, 1)*2-1 for x in range(fft_len/2)] + for i in xrange(n_bursts): + packet_length = random.randint(min_packet_length, + max_packet_length+1) + packet = [random.randint(0, 255) for i in range(packet_length)] + packets.append(packet) + data, tags = tagged_streams.packets_to_vectors(packets, tagname, vlen=1) + total_length = len(data) + src = blocks.vector_source_b(data, False, 1, tags) + mod = ofdm_tx(packet_length_tag_key=tagname) + sync = digital.ofdm_sync_sc_cfb(fft_len, cp_len) + sink_freq = blocks.vector_sink_f() + sink_detect = blocks.vector_sink_b() + noise_level = 0.005 + channel = channels.channel_model(noise_level, freq_offset / 2 / numpy.pi) + self.tb.connect(src, mod, channel, sync, sink_freq) + self.tb.connect((sync, 1), sink_detect) + self.tb.run() + self.assertEqual(numpy.sum(sink_detect.data()), n_bursts) if __name__ == '__main__': diff --git a/gr-digital/python/digital/qa_ofdm_txrx.py b/gr-digital/python/digital/qa_ofdm_txrx.py index 681041d47d..adf93ee356 100755 --- a/gr-digital/python/digital/qa_ofdm_txrx.py +++ b/gr-digital/python/digital/qa_ofdm_txrx.py @@ -21,12 +21,44 @@ # import random - import numpy -from gnuradio import gr, gr_unittest, digital, blocks + +from gnuradio import gr, gr_unittest, digital, blocks, channels from gnuradio.digital.ofdm_txrx import ofdm_tx, ofdm_rx from gnuradio.digital.utils import tagged_streams +# Set this to true if you need to write out data +LOG_DEBUG_INFO=False + +class ofdm_tx_fg (gr.top_block): + def __init__(self, data, len_tag_key): + gr.top_block.__init__(self, "ofdm_tx") + tx_data, tags = tagged_streams.packets_to_vectors((data,), len_tag_key) + src = blocks.vector_source_b(data, False, 1, tags) + self.tx = ofdm_tx(packet_length_tag_key=len_tag_key, debug_log=LOG_DEBUG_INFO) + self.sink = blocks.vector_sink_c() + self.connect(src, self.tx, self.sink) + + def get_tx_samples(self): + return self.sink.data() + +class ofdm_rx_fg (gr.top_block): + def __init__(self, samples, len_tag_key, channel=None, prepend_zeros=100): + gr.top_block.__init__(self, "ofdm_rx") + if prepend_zeros: + samples = (0,) * prepend_zeros + tuple(samples) + src = blocks.vector_source_c(tuple(samples) + (0,) * 1000) + self.rx = ofdm_rx(frame_length_tag_key=len_tag_key, debug_log=LOG_DEBUG_INFO) + if channel is not None: + self.connect(src, channel, self.rx) + else: + self.connect(src, self.rx) + self.sink = blocks.vector_sink_b() + self.connect(self.rx, self.sink) + + def get_rx_bytes(self): + return self.sink.data() + class test_ofdm_txrx (gr_unittest.TestCase): def setUp (self): @@ -35,26 +67,72 @@ class test_ofdm_txrx (gr_unittest.TestCase): def tearDown (self): self.tb = None - def test_001 (self): - pass - #len_tag_key = 'frame_len' - #n_bytes = 100 - #test_data = [random.randint(0, 255) for x in range(n_bytes)] - #tx_data, tags = tagged_streams.packets_to_vectors((test_data,), len_tag_key) - #src = blocks.vector_source_b(test_data, False, 1, tags) - #tx = ofdm_tx(frame_length_tag_key=len_tag_key) - #rx = ofdm_rx(frame_length_tag_key=len_tag_key) - #self.assertEqual(tx.sync_word1, rx.sync_word1) - #self.assertEqual(tx.sync_word2, rx.sync_word2) - #delay = blocks.delay(gr.sizeof_gr_complex, 100) - #noise = analog.noise_source_c(analog.GR_GAUSSIAN, 0.05) - #add = blocks.add_cc() - #sink = blocks.vector_sink_b() - ##self.tb.connect(src, tx, add, rx, sink) - ##self.tb.connect(noise, (add, 1)) - #self.tb.connect(src, tx, blocks.null_sink(gr.sizeof_gr_complex)) - #self.tb.run() + def test_001_tx (self): + """ Just make sure the Tx works in general """ + len_tag_key = 'frame_len' + n_bytes = 52 + n_samples_expected = (numpy.ceil(1.0 * (n_bytes + 4) / 6) + 3) * 80 + test_data = [random.randint(0, 255) for x in range(n_bytes)] + tx_data, tags = tagged_streams.packets_to_vectors((test_data,), len_tag_key) + src = blocks.vector_source_b(test_data, False, 1, tags) + tx = ofdm_tx(packet_length_tag_key=len_tag_key) + tx_fg = ofdm_tx_fg(test_data, len_tag_key) + tx_fg.run() + self.assertEqual(len(tx_fg.get_tx_samples()), n_samples_expected) + + def test_002_rx_only_noise(self): + """ Run the RX with only noise, check it doesn't crash + or return a burst. """ + len_tag_key = 'frame_len' + samples = (0,) * 1000 + channel = channels.channel_model(0.1) + rx_fg = ofdm_rx_fg(samples, len_tag_key, channel) + rx_fg.run() + self.assertEqual(len(rx_fg.get_rx_bytes()), 0) + + def test_003_tx1packet(self): + """ Transmit one packet, with slight AWGN and slight frequency + timing offset. + Check packet is received and no bit errors have occurred. """ + len_tag_key = 'frame_len' + n_bytes = 21 + fft_len = 64 + test_data = tuple([random.randint(0, 255) for x in range(n_bytes)]) + # 1.0/fft_len is one sub-carrier, a fine freq offset stays below that + freq_offset = 1.0 / fft_len * 0.7 + #channel = channels.channel_model(0.01, freq_offset) + channel = None + # Tx + tx_fg = ofdm_tx_fg(test_data, len_tag_key) + tx_fg.run() + tx_samples = tx_fg.get_tx_samples() + # Rx + rx_fg = ofdm_rx_fg(tx_samples, len_tag_key, channel, prepend_zeros=100) + rx_fg.run() + rx_data = rx_fg.get_rx_bytes() + self.assertEqual(tuple(tx_fg.tx.sync_word1), tuple(rx_fg.rx.sync_word1)) + self.assertEqual(tuple(tx_fg.tx.sync_word2), tuple(rx_fg.rx.sync_word2)) + self.assertEqual(test_data, rx_data) + def test_004_tx1packet_large_fO(self): + """ Transmit one packet, with slight AWGN and large frequency offset. + Check packet is received and no bit errors have occurred. """ + fft_len = 64 + len_tag_key = 'frame_len' + n_bytes = 21 + test_data = tuple([random.randint(0, 255) for x in range(n_bytes)]) + #test_data = tuple([255 for x in range(n_bytes)]) + # 1.0/fft_len is one sub-carrier + frequency_offset = 1.0 / fft_len * 2.5 + channel = channels.channel_model(0.00001, frequency_offset) + # Tx + tx_fg = ofdm_tx_fg(test_data, len_tag_key) + tx_fg.run() + tx_samples = tx_fg.get_tx_samples() + # Rx + rx_fg = ofdm_rx_fg(tx_samples, len_tag_key, channel, prepend_zeros=100) + rx_fg.run() + rx_data = rx_fg.get_rx_bytes() + self.assertEqual(test_data, rx_data) if __name__ == '__main__': gr_unittest.run(test_ofdm_txrx, "test_ofdm_txrx.xml") diff --git a/gr-digital/python/digital/qa_packet_headergenerator_bb.py b/gr-digital/python/digital/qa_packet_headergenerator_bb.py index df4512b2a0..3697bd1eb7 100755 --- a/gr-digital/python/digital/qa_packet_headergenerator_bb.py +++ b/gr-digital/python/digital/qa_packet_headergenerator_bb.py @@ -34,15 +34,15 @@ class qa_packet_headergenerator_bb (gr_unittest.TestCase): # 3 PDUs: | | | data = (1, 2, 3, 4, 1, 2) + tuple(range(25)) tagname = "packet_len" - tag1 = gr.gr_tag_t() + tag1 = gr.tag_t() tag1.offset = 0 tag1.key = pmt.string_to_symbol(tagname) tag1.value = pmt.from_long(4) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = 4 tag2.key = pmt.string_to_symbol(tagname) tag2.value = pmt.from_long(2) - tag3 = gr.gr_tag_t() + tag3 = gr.tag_t() tag3.offset = 6 tag3.key = pmt.string_to_symbol(tagname) tag3.value = pmt.from_long(25) @@ -63,15 +63,15 @@ class qa_packet_headergenerator_bb (gr_unittest.TestCase): # 3 PDUs: | | | | data = (1, 2, 3, 4, 1, 2, 1, 2, 3, 4) tagname = "packet_len" - tag1 = gr.gr_tag_t() + tag1 = gr.tag_t() tag1.offset = 0 tag1.key = pmt.string_to_symbol(tagname) tag1.value = pmt.from_long(4) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = 4 tag2.key = pmt.string_to_symbol(tagname) tag2.value = pmt.from_long(2) - tag3 = gr.gr_tag_t() + tag3 = gr.tag_t() tag3.offset = 6 tag3.key = pmt.string_to_symbol(tagname) tag3.value = pmt.from_long(4) @@ -93,15 +93,15 @@ class qa_packet_headergenerator_bb (gr_unittest.TestCase): # 3 PDUs: | | | | data = (1, 2, 3, 4, 1, 2, 1, 2, 3, 4) tagname = "packet_len" - tag1 = gr.gr_tag_t() + tag1 = gr.tag_t() tag1.offset = 0 tag1.key = pmt.string_to_symbol(tagname) tag1.value = pmt.from_long(4) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = 4 tag2.key = pmt.string_to_symbol(tagname) tag2.value = pmt.from_long(2) - tag3 = gr.gr_tag_t() + tag3 = gr.tag_t() tag3.offset = 6 tag3.key = pmt.string_to_symbol(tagname) tag3.value = pmt.from_long(4) @@ -123,15 +123,15 @@ class qa_packet_headergenerator_bb (gr_unittest.TestCase): # 3 PDUs: | | | | data = (1, 2, 3, 4, 1, 2, 1, 2, 3, 4) tagname = "packet_len" - tag1 = gr.gr_tag_t() + tag1 = gr.tag_t() tag1.offset = 0 tag1.key = pmt.string_to_symbol(tagname) tag1.value = pmt.from_long(4) - tag2 = gr.gr_tag_t() + tag2 = gr.tag_t() tag2.offset = 4 tag2.key = pmt.string_to_symbol(tagname) tag2.value = pmt.from_long(2) - tag3 = gr.gr_tag_t() + tag3 = gr.tag_t() tag3.offset = 6 tag3.key = pmt.string_to_symbol(tagname) tag3.value = pmt.from_long(4) diff --git a/gr-digital/python/digital/qa_packet_headerparser_b.py b/gr-digital/python/digital/qa_packet_headerparser_b.py index 8f63ac04d4..2dca3637e3 100755 --- a/gr-digital/python/digital/qa_packet_headerparser_b.py +++ b/gr-digital/python/digital/qa_packet_headerparser_b.py @@ -47,8 +47,11 @@ class qa_packet_headerparser_b (gr_unittest.TestCase): 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ) packet_len_tagname = "packet_len" - - src = blocks.vector_source_b(encoded_headers) + random_tag = gr.tag_t() + random_tag.offset = 5 + random_tag.key = pmt.string_to_symbol("foo") + random_tag.value = pmt.from_long(42) + src = blocks.vector_source_b(encoded_headers, tags=(random_tag,)) parser = digital.packet_headerparser_b(32, packet_len_tagname) sink = blocks.message_debug() self.tb.connect(src, parser) @@ -61,7 +64,7 @@ class qa_packet_headerparser_b (gr_unittest.TestCase): msg1 = pmt.to_python(sink.get_message(0)) msg2 = pmt.to_python(sink.get_message(1)) msg3 = pmt.to_python(sink.get_message(2)) - self.assertEqual(msg1, {'packet_len': 4, 'packet_num': 0}) + self.assertEqual(msg1, {'packet_len': 4, 'packet_num': 0, 'foo': 42}) self.assertEqual(msg2, {'packet_len': 2, 'packet_num': 1}) self.assertEqual(msg3, False) @@ -123,8 +126,11 @@ class qa_packet_headerparser_b (gr_unittest.TestCase): self.assertEqual(sink.num_messages(), 2) msg1 = pmt.to_python(sink.get_message(0)) msg2 = pmt.to_python(sink.get_message(1)) - self.assertEqual(msg1, {'packet_len': 193, 'frame_len': 25, 'packet_num': 0}) - self.assertEqual(msg2, {'packet_len': 8, 'frame_len': 1, 'packet_num': 1}) + # Multiply with 4 because unpacked bytes have only two bits + self.assertEqual(msg1, {'packet_len': 193*4, 'frame_len': 25, 'packet_num': 0}) + self.assertEqual(msg2, {'packet_len': 8*4, 'frame_len': 1, 'packet_num': 1}) if __name__ == '__main__': gr_unittest.run(qa_packet_headerparser_b, "qa_packet_headerparser_b.xml") + + diff --git a/gr-digital/python/digital/utils/tagged_streams.py b/gr-digital/python/digital/utils/tagged_streams.py index f2a58ffe1e..c7edbf61eb 100644 --- a/gr-digital/python/digital/utils/tagged_streams.py +++ b/gr-digital/python/digital/utils/tagged_streams.py @@ -27,7 +27,7 @@ def make_lengthtags(lengths, offsets, tagname='length', vlen=1): tags = [] assert(len(offsets) == len(lengths)) for offset, length in zip(offsets, lengths): - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = offset/vlen tag.key = pmt.string_to_symbol(tagname) tag.value = pmt.from_long(length/vlen) @@ -124,7 +124,7 @@ def packets_to_vectors(packets, lengthtagname, vlen=1): offset = 0 for packet in packets: data.extend(packet) - tag = gr.gr_tag_t() + tag = gr.tag_t() tag.offset = offset/vlen tag.key = pmt.string_to_symbol(lengthtagname) tag.value = pmt.from_long(len(packet)/vlen) |