diff options
-rw-r--r-- | gr-digital/include/gnuradio/digital/packet_header_ofdm.h | 24 | ||||
-rw-r--r-- | gr-digital/lib/packet_header_ofdm.cc | 40 | ||||
-rw-r--r-- | gr-digital/python/digital/ofdm_txrx.py | 116 | ||||
-rwxr-xr-x | gr-digital/python/digital/qa_packet_headerparser_b.py | 37 |
4 files changed, 160 insertions, 57 deletions
diff --git a/gr-digital/include/gnuradio/digital/packet_header_ofdm.h b/gr-digital/include/gnuradio/digital/packet_header_ofdm.h index 4603450915..2b73dc6a5f 100644 --- a/gr-digital/include/gnuradio/digital/packet_header_ofdm.h +++ b/gr-digital/include/gnuradio/digital/packet_header_ofdm.h @@ -45,17 +45,31 @@ namespace gr { const std::string &frame_len_tag_key, const std::string &num_tag_key, int bits_per_header_sym, - int bits_per_payload_sym); + int bits_per_payload_sym, + bool scramble_header); ~packet_header_ofdm(); /*! + * \brief Header formatter. + * + * Does the same as packet_header_default::header_formatter(), but + * optionally scrambles the bits (this is more important for OFDM to avoid + * PAPR spikes). + */ + bool header_formatter( + long packet_len, + unsigned char *out, + const std::vector<tag_t> &tags + ); + + /*! * \brief Inverse function to header_formatter(). * * Does the same as packet_header_default::header_parser(), but * adds another tag that stores the number of OFDM symbols in the * packet. * Note that there is usually no linear connection between the number - * of OFDM symbols and the packet length, because, a packet might + * of OFDM symbols and the packet length because a packet might * finish mid-OFDM-symbol. */ bool header_parser( @@ -85,15 +99,17 @@ namespace gr { const std::string &frame_len_tag_key="frame_len", const std::string &num_tag_key="packet_num", int bits_per_header_sym=1, - int bits_per_payload_sym=1 + int bits_per_payload_sym=1, + bool scramble_header=false ); protected: - pmt::pmt_t d_frame_len_tag_key; + pmt::pmt_t d_frame_len_tag_key; //!< Tag key of the additional frame length tag const std::vector<std::vector<int> > d_occupied_carriers; //!< Which carriers/symbols carry data int d_syms_per_set; //!< Helper variable: Total number of elements in d_occupied_carriers int d_bits_per_payload_sym; + std::vector<unsigned char> d_scramble_mask; //!< Bits are xor'd with this before tx'ing }; } // namespace digital diff --git a/gr-digital/lib/packet_header_ofdm.cc b/gr-digital/lib/packet_header_ofdm.cc index f163657bab..4893b866e2 100644 --- a/gr-digital/lib/packet_header_ofdm.cc +++ b/gr-digital/lib/packet_header_ofdm.cc @@ -24,6 +24,7 @@ #endif #include <gnuradio/digital/packet_header_ofdm.h> +#include <gnuradio/digital/lfsr.h> namespace gr { namespace digital { @@ -46,11 +47,13 @@ namespace gr { const std::string &frame_len_tag_key, const std::string &num_tag_key, int bits_per_header_sym, - int bits_per_payload_sym) + int bits_per_payload_sym, + bool scramble_header) { return packet_header_ofdm::sptr( new packet_header_ofdm( - occupied_carriers, n_syms, len_tag_key, frame_len_tag_key, num_tag_key, bits_per_header_sym, bits_per_payload_sym + occupied_carriers, n_syms, len_tag_key, frame_len_tag_key, num_tag_key, + bits_per_header_sym, bits_per_payload_sym, scramble_header ) ); } @@ -62,8 +65,9 @@ namespace gr { const std::string &frame_len_tag_key, const std::string &num_tag_key, int bits_per_header_sym, - int bits_per_payload_sym) - : packet_header_default( + int bits_per_payload_sym, + bool scramble_header + ) : packet_header_default( _get_header_len_from_occupied_carriers(occupied_carriers, n_syms), len_tag_key, num_tag_key, @@ -71,23 +75,47 @@ namespace gr { d_frame_len_tag_key(pmt::string_to_symbol(frame_len_tag_key)), d_occupied_carriers(occupied_carriers), d_syms_per_set(0), - d_bits_per_payload_sym(bits_per_payload_sym) + d_bits_per_payload_sym(bits_per_payload_sym), + d_scramble_mask(d_header_len, 0) { for (unsigned i = 0; i < d_occupied_carriers.size(); i++) { d_syms_per_set += d_occupied_carriers[i].size(); } + + // Init scrambler mask + if (scramble_header) { + // These are just random values which already have OK PAPR: + gr::digital::lfsr shift_reg(0x8a, 0x6f, 7); + for (int i = 0; i < d_header_len; i++) { + for (int k = 0; k < bits_per_header_sym; k++) { + d_scramble_mask[i] ^= shift_reg.next_bit() << k; + } + } + } } packet_header_ofdm::~packet_header_ofdm() { } + bool packet_header_ofdm::header_formatter(long packet_len, unsigned char *out, const std::vector<tag_t> &tags) + { + bool ret_val = packet_header_default::header_formatter(packet_len, out, tags); + for (int i = 0; i < d_header_len; i++) { + out[i] ^= d_scramble_mask[i]; + } + return ret_val; + } bool packet_header_ofdm::header_parser( const unsigned char *in, std::vector<tag_t> &tags) { - if (!packet_header_default::header_parser(in, tags)) { + std::vector<unsigned char> in_descrambled(d_header_len, 0); + for (int i = 0; i < d_header_len; i++) { + in_descrambled[i] = in[i] ^ d_scramble_mask[i]; + } + if (!packet_header_default::header_parser(&in_descrambled[0], tags)) { return false; } int packet_len = 0; // # of bytes in this frame diff --git a/gr-digital/python/digital/ofdm_txrx.py b/gr-digital/python/digital/ofdm_txrx.py index 76bf337b8d..b1ebc9e9c2 100644 --- a/gr-digital/python/digital/ofdm_txrx.py +++ b/gr-digital/python/digital/ofdm_txrx.py @@ -63,6 +63,7 @@ _seq_seed = 42 def _get_active_carriers(fft_len, occupied_carriers, pilot_carriers): + """ Returns a list of all carriers that at some point carry data or pilots. """ active_carriers = list() for carrier in list(occupied_carriers[0]) + list(pilot_carriers[0]): if carrier < 0: @@ -134,6 +135,8 @@ class ofdm_tx(gr.hier_block2): 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. + debug_log: Write output into log files (Warning: creates lots of data!) + scramble_bits: Activates the scramblers (set this to True unless debugging) """ def __init__(self, fft_len=_def_fft_len, cp_len=_def_cp_len, packet_length_tag_key=_def_packet_length_tag_key, @@ -160,7 +163,6 @@ class ofdm_tx(gr.hier_block2): self.pilot_symbols = pilot_symbols self.bps_header = bps_header self.bps_payload = bps_payload - n_sync_words = 1 self.sync_word1 = sync_word1 if sync_word1 is None: self.sync_word1 = _make_sync_word1(fft_len, occupied_carriers, pilot_carriers) @@ -175,8 +177,11 @@ class ofdm_tx(gr.hier_block2): if len(self.sync_word2) != fft_len: raise ValueError("Length of sync sequence(s) must be FFT length.") self.sync_word2 = list(self.sync_word2) - n_sync_words = 2 self.sync_words.append(self.sync_word2) + if scramble_bits: + self.scramble_seed = 0x7f + else: + self.scramble_seed = 0x00 # We deactivate the scrambler by init'ing it with zeros ### Header modulation ################################################ crc = digital.crc32_bb(False, self.packet_length_tag_key) header_constellation = _get_constellation(bps_header) @@ -184,42 +189,39 @@ class ofdm_tx(gr.hier_block2): formatter_object = digital.packet_header_ofdm( occupied_carriers=occupied_carriers, n_syms=1, bits_per_header_sym=self.bps_header, - bits_per_payload_sym=self.bps_payload + bits_per_payload_sym=self.bps_payload, + scramble_header=scramble_bits ) 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)) + 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()) - if scramble_bits: - self.connect( - crc, - digital.additive_scrambler_bb( - 0x8a, 0x7f, 7, - bits_per_byte=bps_payload, - reset_tag_key=self.packet_length_tag_key - ), - blocks.repack_bits_bb( - 8, # Unpack 8 bits per byte - bps_payload, - self.packet_length_tag_key - ), - payload_mod - ) - else: - self.connect( - crc, - blocks.repack_bits_bb( - 8, # Unpack 8 bits per byte - bps_payload, - self.packet_length_tag_key - ), - payload_mod - ) + payload_scrambler = digital.additive_scrambler_bb( + 0x8a, + self.scramble_seed, + 7, + 0, # Don't reset after fixed length (let the reset tag do that) + bits_per_byte=bps_payload, + reset_tag_key=self.packet_length_tag_key + ) self.connect( + crc, + payload_scrambler, + blocks.repack_bits_bb( + 8, # Unpack 8 bits per byte + bps_payload, + self.packet_length_tag_key + ), payload_mod, (header_payload_mux, 1) ) @@ -246,8 +248,8 @@ class ofdm_tx(gr.hier_block2): ) 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')) + self.connect(allocator, blocks.file_sink(gr.sizeof_gr_complex * fft_len, 'tx-post-allocator.dat')) + self.connect(cyclic_prefixer, blocks.file_sink(gr.sizeof_gr_complex, 'tx-signal.dat')) class ofdm_rx(gr.hier_block2): @@ -312,6 +314,10 @@ class ofdm_rx(gr.hier_block2): raise ValueError("Length of sync sequence(s) must be FFT length.") self.sync_word2 = sync_word2 n_sync_words = 2 + if scramble_bits: + self.scramble_seed = 0x7f + else: + self.scramble_seed = 0x00 # We deactivate the scrambler by init'ing it with zeros ### Sync ############################################################ sync_detect = digital.ofdm_sync_sc_cfb(fft_len, cp_len) delay = blocks.delay(gr.sizeof_gr_complex, fft_len+cp_len) @@ -357,10 +363,19 @@ class ofdm_rx(gr.hier_block2): frame_length_tag_key, packet_num_tag_key, bps_header, - bps_payload + bps_payload, + scramble_header=scramble_bits ) 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.connect( + (hpd, 0), + header_fft, + chanest, + header_eq, + header_serializer, + header_demod, + header_parser + ) self.msg_connect(header_parser, "header_data", hpd, "header_data") if debug_log: self.connect((chanest, 1), blocks.file_sink(gr.sizeof_gr_complex * fft_len, 'channel-estimate.dat')) @@ -368,7 +383,7 @@ class ofdm_rx(gr.hier_block2): self.connect((chanest, 0), blocks.tag_debug(gr.sizeof_gr_complex * fft_len, 'post-hdr-chanest')) self.connect(header_eq, blocks.file_sink(gr.sizeof_gr_complex * fft_len, 'post-hdr-eq.dat')) self.connect(header_serializer, blocks.file_sink(gr.sizeof_gr_complex, 'post-hdr-serializer.dat')) - self.connect(header_demod, blocks.file_sink(1, 'post-hdr-demod.dat')) + self.connect(header_descrambler, blocks.file_sink(1, 'post-hdr-demod.dat')) ### Payload demod #################################################### payload_fft = fft.fft_vcc(self.fft_len, True, (), True) payload_constellation = _get_constellation(bps_payload) @@ -391,20 +406,29 @@ class ofdm_rx(gr.hier_block2): 1 # Skip 1 symbol (that was already in the header) ) payload_demod = digital.constellation_decoder_cb(payload_constellation.base()) + self.payload_descrambler = digital.additive_scrambler_bb( + 0x8a, + self.scramble_seed, + 7, + 0, # Don't reset after fixed length + bits_per_byte=bps_payload, + reset_tag_key=self.packet_length_tag_key + ) 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) - if scramble_bits: - descrambler = digital.additive_scrambler_bb( - 0x8a, 0x7f, 7, - bits_per_byte=bps_payload, - reset_tag_key=self.packet_length_tag_key - ) - self.connect(repack, descrambler, crc, self) - else: - self.connect(repack, crc, self) + self.crc = digital.crc32_bb(True, self.packet_length_tag_key) + self.connect( + (hpd, 1), + payload_fft, + payload_eq, + payload_serializer, + payload_demod, + repack, + self.payload_descrambler, + self.crc, + self + ) if debug_log: - self.connect((hpd, 1), blocks.tag_debug(gr.sizeof_gr_complex*fft_len, 'post-hpd')); + self.connect((hpd, 1), blocks.tag_debug(gr.sizeof_gr_complex*fft_len, 'post-hpd')) self.connect(payload_fft, blocks.file_sink(gr.sizeof_gr_complex*fft_len, 'post-payload-fft.dat')) self.connect(payload_eq, blocks.file_sink(gr.sizeof_gr_complex*fft_len, 'post-payload-eq.dat')) self.connect(payload_serializer, blocks.file_sink(gr.sizeof_gr_complex, 'post-payload-serializer.dat')) diff --git a/gr-digital/python/digital/qa_packet_headerparser_b.py b/gr-digital/python/digital/qa_packet_headerparser_b.py index abd23c8945..7754a7304c 100755 --- a/gr-digital/python/digital/qa_packet_headerparser_b.py +++ b/gr-digital/python/digital/qa_packet_headerparser_b.py @@ -130,7 +130,42 @@ class qa_packet_headerparser_b (gr_unittest.TestCase): 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}) + def test_004_ofdm_scramble(self): + """ + Test scrambling for OFDM header gen + """ + header_len = 32 + packet_length = 23 + packet_len_tagname = "packet_len" + frame_len_tagname = "frame_len" + data, tags = tagged_streams.packets_to_vectors([range(packet_length),range(packet_length),], packet_len_tagname) + src = blocks.vector_source_b(data, False, 1, tags) + header_formatter = digital.packet_header_ofdm( + (range(32),), # 32 carriers are occupied (which doesn't matter here) + 1, # 1 OFDM symbol per header (= 32 bits) + packet_len_tagname, + frame_len_tagname, + "packet_num", + 1, # 1 bit per header symbols (BPSK) + 2, # 2 bits per payload symbol (QPSK) + scramble_header=True + ) + header_gen = digital.packet_headergenerator_bb(header_formatter.base()) + header_parser = digital.packet_headerparser_b(header_formatter.base()) + sink = blocks.message_debug() + self.tb.connect(src, header_gen, header_parser) + self.tb.msg_connect(header_parser, "header_data", sink, "store") + self.tb.start() + time.sleep(1) + self.tb.stop() + self.tb.wait() + msg = pmt.to_python(sink.get_message(0)) + self.assertEqual(msg, {'packet_len': packet_length, 'packet_num': 0, 'frame_len': 4}) + msg = pmt.to_python(sink.get_message(1)) + self.assertEqual(msg, {'packet_len': packet_length, 'packet_num': 1, 'frame_len': 4}) + if __name__ == '__main__': - gr_unittest.run(qa_packet_headerparser_b, "qa_packet_headerparser_b.xml") + #gr_unittest.run(qa_packet_headerparser_b, "qa_packet_headerparser_b.xml") + gr_unittest.run(qa_packet_headerparser_b) |