diff --git a/grc/CMakeLists.txt b/grc/CMakeLists.txt index cd500415b647c169f6783d716d007a5bd76362b0..adfaa90b8ae8174de1914afa6f3bcbc02a4b8eb7 100644 --- a/grc/CMakeLists.txt +++ b/grc/CMakeLists.txt @@ -9,5 +9,6 @@ install(FILES elen90089_corr_est_cc.block.yml elen90089_moe_symbol_sync_cc.block.yml + elen90089_symbol_mapper_c.block.yml DESTINATION share/gnuradio/grc/blocks ) diff --git a/grc/elen90089_symbol_mapper_c.block.yml b/grc/elen90089_symbol_mapper_c.block.yml new file mode 100644 index 0000000000000000000000000000000000000000..e10c2d5b35612308548b540bc209fec5602b621e --- /dev/null +++ b/grc/elen90089_symbol_mapper_c.block.yml @@ -0,0 +1,33 @@ +id: elen90089_symbol_mapper_c +label: CDC Symbol Mapper +category: '[elen90089]' + +templates: + imports: import elen90089 + make: elen90089.symbol_mapper_c(elen90089.symbol_mapper_c.constel.${constel_header}, {tsb_tag_key}) + +parameters: +- id: constel_header + label: Header Constellation + dtype: enum + options: [CONSTEL_BPSK, CONSTEL_QPSK, CONSTEL_8PSK, CONSTEL_16QAM] + option_labels: ['BPSK', 'QPSK', '8PSK', '16QAM'] +- id: tsb_tag_key + label: TSB Tag Key + default: 'packet_len' + dtype: string + +inputs: +- label: hdr + domain: message + optional: True +- label: pld + domain: message + optional: True + +outputs: +- label: out + domain: stream + dtype: complex + +file_format: 1 diff --git a/include/elen90089/CMakeLists.txt b/include/elen90089/CMakeLists.txt index 7ad10f5f6c1f20a95cda5262f0e84db3e3ec0866..e47a60c06608643b7f30bf5d5de8147b3f53e85c 100644 --- a/include/elen90089/CMakeLists.txt +++ b/include/elen90089/CMakeLists.txt @@ -13,5 +13,6 @@ install(FILES api.h corr_est_cc.h moe_symbol_sync_cc.h + symbol_mapper_c.h DESTINATION include/elen90089 ) diff --git a/include/elen90089/symbol_mapper_c.h b/include/elen90089/symbol_mapper_c.h new file mode 100644 index 0000000000000000000000000000000000000000..9cd7f16cd3fc06a45efa898b4ad77595c2fd7028 --- /dev/null +++ b/include/elen90089/symbol_mapper_c.h @@ -0,0 +1,95 @@ +/* -*- c++ -*- */ +/* + * Copyright 2022 University of Melbourne. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef INCLUDED_ELEN90089_SYMBOL_MAPPER_C_H +#define INCLUDED_ELEN90089_SYMBOL_MAPPER_C_H + +#include <elen90089/api.h> +#include <gnuradio/tagged_stream_block.h> + +namespace gr { +namespace elen90089 { + +/*! + * \brief Maps header and payload bytes of CDC packet to constellation symbols. + * \ingroup elen90089 + * + * \details + * This block takes in the header and payload PDUs of a CDC packet, mapping + * the respective bytes of each PDU to constellation symbol points to form + * a modulated packet. Mapping of header and payload bytes is done using + * different constellations. The default header constellation is BPSK and + * but can be set via the \p constel_header parameter. The default payload + * constellation is BPSK but can be chosen on a packet-by-packet basis by + * setting the \p bps (bits-per-symbol) parameter in the metadata dictionary + * of the header PDU. The following constellation types are supported for + * both header and payload mapping: + * + * \li BPSK: bps = 1 + * \li QPSK: bps = 2 + * \li 8PSK: bsp = 3 + * \li 16QAM: bps = 4 + * + * This block uses asynchronous message passing interfaces to receive PDUs. + * The input message ports are: + * + * \li hdr: header PDU of CDC packet with metadata dict specifying payload bps. + * \li pld: payload PDU of CDC packet + * + * The block output is a complex stream of symbol constellation points with + * the following stream tags: + * + * \li metadata: first packet symbol is tagged with all entries in + * header PDU metadata dictionary + * \li \p tsb_tag_key: first packet symbol is tagged with burst duration / + * packet length in number of symbols + * \li tx_sob: indicates first symbol in packet/burst + * \li tx_eob: indicates last symbol in packet/burst + */ +class ELEN90089_API symbol_mapper_c : virtual public gr::tagged_stream_block +{ +public: + typedef std::shared_ptr<symbol_mapper_c> sptr; + + /*! + * \brief Enum to represent constellation type. + */ + enum constel_t { + CONSTEL_BPSK, /*! BPSK constellation (bps=1) */ + CONSTEL_QPSK, /*! QPSK constellation (bps=2) */ + CONSTEL_8PSK, /*! 8PSK constellation (bps=3) */ + CONSTEL_16QAM, /*! 16QAM constellation (bps=4) */ + }; + + /*! + * \brief Make a CDC Symbol Mapper block. + * + * \param constel_header Constellation used for header bytes (BPSK, QPSK, + * 8PSK, or 16QAM) + * \param tsb_tag_key Output tag key indicating packet/burst duration + */ + static sptr make(constel_t constel_header = CONSTEL_BPSK, + const std::string& tsb_tag_key = "packet_len"); + + /*! + * \brief Returns the constellation used to map header bits. + */ + virtual constel_t constel_header() const = 0; + + /*! + * \brief Sets the constellation used to map header bits. + * + * \param constel_header (constel_t) Constellation used for header bits + * (BPSK, QPSK, 8PSK, or 16QAM) + */ + virtual void set_constel_header(constel_t constel_header) = 0; +}; + +} // namespace elen90089 +} // namespace gr + +#endif /* INCLUDED_ELEN90089_SYMBOL_MAPPER_C_H */ diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index f05d33b12649a0da5d038c2d595bb3478072f802..8b8158b74b647b76cec672be3b5720e3e577d60b 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -14,6 +14,7 @@ include(GrPlatform) #define LIB_SUFFIX list(APPEND elen90089_sources corr_est_cc_impl.cc moe_symbol_sync_cc_impl.cc + symbol_mapper_c_impl.cc ) set(elen90089_sources "${elen90089_sources}" PARENT_SCOPE) @@ -27,7 +28,8 @@ add_library(gnuradio-elen90089 SHARED ${elen90089_sources}) target_link_libraries( gnuradio-elen90089 gnuradio::gnuradio-runtime - gnuradio-filter) + gnuradio-filter + gnuradio-digital) target_include_directories(gnuradio-elen90089 PUBLIC $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}/../include> diff --git a/lib/symbol_mapper_c_impl.cc b/lib/symbol_mapper_c_impl.cc new file mode 100644 index 0000000000000000000000000000000000000000..02ee584903488a407d5cbfab190c326177a7dfec --- /dev/null +++ b/lib/symbol_mapper_c_impl.cc @@ -0,0 +1,203 @@ +/* -*- c++ -*- */ +/* + * Copyright 2022 University of Melbourne. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#include <gnuradio/io_signature.h> +#include "symbol_mapper_c_impl.h" + +namespace gr { +namespace elen90089 { + +symbol_mapper_c::sptr symbol_mapper_c::make(constel_t constel_header, + const std::string& tsb_tag_key) +{ + return gnuradio::make_block_sptr<symbol_mapper_c_impl>( + constel_header, tsb_tag_key); +} + +symbol_mapper_c_impl::symbol_mapper_c_impl(constel_t constel_header, + const std::string& tsb_tag_key) + : gr::tagged_stream_block("symbol_mapper_c", + gr::io_signature::make(0, 0, 0), + gr::io_signature::make(1, 1, sizeof(gr_complex)), + tsb_tag_key), + d_bps_header(0), + d_bps_payload(0), + d_len_header(0), + d_len_payload(0) +{ + // register message ports to receive header and payload pdus + message_port_register_in(pmt::mp("hdr")); + message_port_register_in(pmt::mp("pld")); + + // set header bps + set_constel_header(constel_header); + + // create constellation objects + d_bpsk = digital::constellation_bpsk::make(); + d_8psk = digital::constellation_8psk::make(); + d_16qam = digital::constellation_16qam::make(); + + // digital::constellation_qpsk is unnormalized so create manually + d_qpsk = digital::constellation_calcdist::make( + {gr_complex(-1, -1), gr_complex( 1, -1), + gr_complex(-1, 1), gr_complex( 1, 1)}, // constel + {0x0, 0x2, 0x3, 0x1}, // pre_diff_code + 4, // rotational_symmetry + 1, // dimensionality + digital::constellation::POWER_NORMALIZATION); +} + +symbol_mapper_c_impl::~symbol_mapper_c_impl() { } + +symbol_mapper_c::constel_t symbol_mapper_c_impl::constel_header() const +{ + constel_t constel; + + switch(d_bps_header) { + case 1: constel = CONSTEL_BPSK; break; + case 2: constel = CONSTEL_QPSK; break; + case 3: constel = CONSTEL_8PSK; break; + case 4: constel = CONSTEL_16QAM; break; + } + + return constel; +} + +void symbol_mapper_c_impl::set_constel_header(constel_t constel) +{ + switch(constel) { + case CONSTEL_BPSK: d_bps_header = 1; break; + case CONSTEL_QPSK: d_bps_header = 2; break; + case CONSTEL_8PSK: d_bps_header = 3; break; + case CONSTEL_16QAM: d_bps_header = 4; break; + } +} + +int symbol_mapper_c_impl::calculate_output_stream_length(const gr_vector_int &ninput_items) +{ + int noutput_items = 0; + + // get next header pdu + if (d_len_header == 0) { + pmt::pmt_t msg(delete_head_nowait(pmt::mp("hdr"))); + + if (msg.get() != nullptr) { + assert(!pmt::is_pair(msg)); + + d_metadata = pmt::car(msg); + d_bps_payload = pmt::to_long(pmt::dict_ref( + d_metadata, pmt::mp("bps"), pmt::PMT_NIL)); + + d_header = pmt::cdr(msg); + d_len_header = pmt::blob_length(d_header); + } + } + + // get next payload pdu + if (d_len_payload == 0) { + pmt::pmt_t msg(delete_head_nowait(pmt::mp("pld"))); + + if (msg.get() != nullptr) { + d_payload = pmt::cdr(msg); + d_len_payload = pmt::blob_length(d_payload); + } + } + + if (d_len_header > 0 && d_len_payload > 0) { + noutput_items += ((d_len_header << 3) + (d_bps_header - 1)) / d_bps_header; + noutput_items += ((d_len_payload << 3) + (d_bps_payload - 1)) / d_bps_payload; + } + + return noutput_items; +} + +void symbol_mapper_c_impl::map_to_symbols(const uint8_t* bytes, + int nbytes, + gr_complex* symbols, + int bps) +{ + digital::constellation_sptr constel; + + switch (bps) { + case 1: constel = d_bpsk; break; + case 2: constel = d_qpsk; break; + case 3: constel = d_8psk; break; + case 4: constel = d_16qam; break; + } + + int nsymbols = (nbytes << 3) / bps; + int index = 0; + for (int i = 0; i < nsymbols; i++) { + // MSB unpacking of bps bits from input bytes + int x = 0; + for (unsigned int j = 0; j < bps; j++, index++) { + int bit = 0x1 & (bytes[index>>3] >> (7 - (index & 0x7))); + x = (x << 1) | bit; + } + + constel->map_to_points(x, symbols++); + } + + // zero pad and map left over bits + if (index & 0x7 != 0) { + int x = bytes[index>>3] << (bps - (8 - (index & 0x7))); + x &= (0x1 << bps) - 1; + + constel->map_to_points(x, symbols); + } +} + +int symbol_mapper_c_impl::work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items) +{ + auto out = static_cast<gr_complex*>(output_items[0]); + + if (d_len_header == 0 || d_len_payload == 0) { + return 0; + } + + int nout = ((d_len_header << 3) + (d_bps_header - 1)) / d_bps_header; + nout += ((d_len_payload << 3) + (d_bps_payload - 1)) / d_bps_payload; + + assert (noutput_items >= nout); + + // modulate header bytes + size_t io(0); + const uint8_t* bytes = static_cast<const uint8_t*>(pmt::uniform_vector_elements(d_header, io)); + map_to_symbols(bytes, d_len_header, out, d_bps_header); + out += ((d_len_header << 3) + (d_bps_header - 1)) / d_bps_header; + + // modulate payload bytes + bytes = static_cast<const uint8_t*>(pmt::uniform_vector_elements(d_payload, io)); + map_to_symbols(bytes, d_len_payload, out, d_bps_payload); + + // convert header metadata to stream tags + pmt::pmt_t items(pmt::dict_items(d_metadata)); + for (size_t i = 0; i < pmt::length(items); i++) { + pmt::pmt_t item(pmt::nth(i, items)); + pmt::pmt_t key(pmt::car(item)); + pmt::pmt_t val(pmt::cdr(item)); + + // determine absolute sample offset of tag + int offset = nitems_written(0); + if (pmt::equal(key, pmt::string_to_symbol("tx_eob"))) { + offset += nout - 1; + } + + add_item_tag(0, offset, key, val, alias_pmt()); + } + + d_len_header = 0; + d_len_payload = 0; + + return nout; +} + +} /* namespace elen90089 */ +} /* namespace gr */ diff --git a/lib/symbol_mapper_c_impl.h b/lib/symbol_mapper_c_impl.h new file mode 100644 index 0000000000000000000000000000000000000000..8334b5865ae44464a4de9ecf92ab7a3a7af3534b --- /dev/null +++ b/lib/symbol_mapper_c_impl.h @@ -0,0 +1,61 @@ +/* -*- c++ -*- */ +/* + * Copyright 2022 University of Melbourne. + * + * SPDX-License-Identifier: GPL-3.0-or-later + */ + +#ifndef INCLUDED_ELEN90089_SYMBOL_MAPPER_C_IMPL_H +#define INCLUDED_ELEN90089_SYMBOL_MAPPER_C_IMPL_H + +#include <elen90089/symbol_mapper_c.h> +#include <gnuradio/digital/constellation.h> + +namespace gr { +namespace elen90089 { + +class symbol_mapper_c_impl : public symbol_mapper_c +{ +private: + int d_bps_header; + int d_bps_payload; + int d_len_header; + int d_len_payload; + + pmt::pmt_t d_header; + pmt::pmt_t d_payload; + pmt::pmt_t d_metadata; + + // constellation objects + digital::constellation_sptr d_bpsk; + digital::constellation_sptr d_qpsk; + digital::constellation_sptr d_8psk; + digital::constellation_sptr d_16qam; + + void map_to_symbols(const uint8_t* bytes, + int nbytes, + gr_complex* symbols, + int bps); + +protected: + int calculate_output_stream_length(const gr_vector_int &ninput_items) override; + +public: + symbol_mapper_c_impl(constel_t constel_header = CONSTEL_BPSK, + const std::string& tsb_tag_key = "packet_len"); + + ~symbol_mapper_c_impl(); + + constel_t constel_header() const override; + void set_constel_header(constel_t constel_header) override; + + int work(int noutput_items, + gr_vector_int &ninput_items, + gr_vector_const_void_star &input_items, + gr_vector_void_star &output_items); +}; + +} // namespace elen90089 +} // namespace gr + +#endif /* INCLUDED_ELEN90089_SYMBOL_MAPPER_C_IMPL_H */ diff --git a/python/CMakeLists.txt b/python/CMakeLists.txt index 6c242766ff2cb44608eeda67c542ef988be61004..a984ec18889711b40eaf9669b58b83d1a0abc92a 100644 --- a/python/CMakeLists.txt +++ b/python/CMakeLists.txt @@ -41,3 +41,4 @@ add_custom_target( ) GR_ADD_TEST(qa_corr_est_cc ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_corr_est_cc.py) GR_ADD_TEST(qa_moe_symbol_sync_cc ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_moe_symbol_sync_cc.py) +GR_ADD_TEST(qa_symbol_mapper_c ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/qa_symbol_mapper_c.py) diff --git a/python/bindings/CMakeLists.txt b/python/bindings/CMakeLists.txt index 9ed43664351ad1cc647674b233ebd94d58006b7e..6c6cd4df39a1d296aa4aaaf1bdd78be62e903cdf 100644 --- a/python/bindings/CMakeLists.txt +++ b/python/bindings/CMakeLists.txt @@ -31,6 +31,7 @@ include(GrPybind) list(APPEND elen90089_python_files corr_est_cc_python.cc moe_symbol_sync_cc_python.cc + symbol_mapper_c_python.cc python_bindings.cc) GR_PYBIND_MAKE_OOT(elen90089 diff --git a/python/bindings/docstrings/symbol_mapper_c_pydoc_template.h b/python/bindings/docstrings/symbol_mapper_c_pydoc_template.h new file mode 100644 index 0000000000000000000000000000000000000000..97b25e35220fc4bb2eb12fc45ebba10c32663473 --- /dev/null +++ b/python/bindings/docstrings/symbol_mapper_c_pydoc_template.h @@ -0,0 +1,36 @@ +/* + * Copyright 2022 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ +#include "pydoc_macros.h" +#define D(...) DOC(gr,elen90089, __VA_ARGS__ ) +/* + This file contains placeholders for docstrings for the Python bindings. + Do not edit! These were automatically extracted during the binding process + and will be overwritten during the build process + */ + + + + static const char *__doc_gr_elen90089_symbol_mapper_c = R"doc()doc"; + + + static const char *__doc_gr_elen90089_symbol_mapper_c_symbol_mapper_c_0 = R"doc()doc"; + + + static const char *__doc_gr_elen90089_symbol_mapper_c_symbol_mapper_c_1 = R"doc()doc"; + + + static const char *__doc_gr_elen90089_symbol_mapper_c_make = R"doc()doc"; + + + static const char *__doc_gr_elen90089_symbol_mapper_c_constel_header = R"doc()doc"; + + + static const char *__doc_gr_elen90089_symbol_mapper_c_set_constel_header = R"doc()doc"; + + diff --git a/python/bindings/python_bindings.cc b/python/bindings/python_bindings.cc index fff3f49b3ae58c0bf1446a5b9bad7880c0d35c6b..a6f73a6a9618aff5ff266d5f636df852b18314a9 100644 --- a/python/bindings/python_bindings.cc +++ b/python/bindings/python_bindings.cc @@ -23,6 +23,7 @@ namespace py = pybind11; // BINDING_FUNCTION_PROTOTYPES( void bind_corr_est_cc(py::module& m); void bind_moe_symbol_sync_cc(py::module& m); + void bind_symbol_mapper_c(py::module& m); // ) END BINDING_FUNCTION_PROTOTYPES @@ -53,5 +54,6 @@ PYBIND11_MODULE(elen90089_python, m) // BINDING_FUNCTION_CALLS( bind_corr_est_cc(m); bind_moe_symbol_sync_cc(m); + bind_symbol_mapper_c(m); // ) END BINDING_FUNCTION_CALLS } \ No newline at end of file diff --git a/python/bindings/symbol_mapper_c_python.cc b/python/bindings/symbol_mapper_c_python.cc new file mode 100644 index 0000000000000000000000000000000000000000..bf416b4a6b0a3d1298a6aa618a60eea80610ee43 --- /dev/null +++ b/python/bindings/symbol_mapper_c_python.cc @@ -0,0 +1,64 @@ +/* + * Copyright 2022 Free Software Foundation, Inc. + * + * This file is part of GNU Radio + * + * SPDX-License-Identifier: GPL-3.0-or-later + * + */ + +/***********************************************************************************/ +/* This file is automatically generated using bindtool and can be manually edited */ +/* The following lines can be configured to regenerate this file during cmake */ +/* If manual edits are made, the following tags should be modified accordingly. */ +/* BINDTOOL_GEN_AUTOMATIC(0) */ +/* BINDTOOL_USE_PYGCCXML(0) */ +/* BINDTOOL_HEADER_FILE(symbol_mapper_c.h) */ +/* BINDTOOL_HEADER_FILE_HASH(b347387bb1dad3898ceb8adbe91a8e32) */ +/***********************************************************************************/ + +#include <pybind11/complex.h> +#include <pybind11/pybind11.h> +#include <pybind11/stl.h> + +namespace py = pybind11; + +#include <elen90089/symbol_mapper_c.h> +// pydoc.h is automatically generated in the build directory +#include <symbol_mapper_c_pydoc.h> + +void bind_symbol_mapper_c(py::module& m) +{ + + using symbol_mapper_c = ::gr::elen90089::symbol_mapper_c; + + py::class_<symbol_mapper_c, + gr::tagged_stream_block, + gr::block, + gr::basic_block, + std::shared_ptr<symbol_mapper_c>> symbol_mapper_c_class(m, "symbol_mapper_c", D(symbol_mapper_c)); + + py::enum_<symbol_mapper_c::constel_t>(symbol_mapper_c_class, "constel") + .value("CONSTEL_BPSK", symbol_mapper_c::CONSTEL_BPSK) + .value("CONSTEL_QPSK", symbol_mapper_c::CONSTEL_QPSK) + .value("CONSTEL_8PSK", symbol_mapper_c::CONSTEL_8PSK) + .value("CONSTEL_16QAM", symbol_mapper_c::CONSTEL_16QAM) + .export_values(); + + py::implicitly_convertible<int, symbol_mapper_c::constel_t>(); + + symbol_mapper_c_class + .def(py::init(&symbol_mapper_c::make), + py::arg("constel_header") = symbol_mapper_c::constel_t::CONSTEL_BPSK, + py::arg("tsb_tag_key") = "packet_len", + D(symbol_mapper_c,make)) + + .def("constel_header", + &symbol_mapper_c::constel_header, + D(symbol_mapper_c, constel_header)) + + .def("set_constel_header", + &symbol_mapper_c::set_constel_header, + py::arg("constel_header"), + D(symbol_mapper_c, set_constel_header)); +} diff --git a/python/qa_symbol_mapper_c.py b/python/qa_symbol_mapper_c.py new file mode 100755 index 0000000000000000000000000000000000000000..52a3f08e1b7600d6ddd52b22a1997240898b1dc4 --- /dev/null +++ b/python/qa_symbol_mapper_c.py @@ -0,0 +1,110 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# Copyright 2022 University of Melbourne. +# +# SPDX-License-Identifier: GPL-3.0-or-later +# + +from gnuradio import gr, gr_unittest +from gnuradio import blocks, digital +import pmt +from time import sleep +import numpy as np +try: + from elen90089 import symbol_mapper_c +except ImportError: + import os + import sys + dirname, filename = os.path.split(os.path.abspath(__file__)) + sys.path.append(os.path.join(dirname, "bindings")) + from elen90089 import symbol_mapper_c + + +def modulate_u8vector(cnst, vec): + bits = np.unpackbits(vec) + bps = cnst.bits_per_symbol() + + symb = [] + for ii in range(0, len(bits), bps): + x = np.flip(bits[ii:ii+bps]) + s = np.packbits(x, bitorder='little')[0] + symb.append(cnst.points()[s]) + + return symb + + +def create_pdu(bps_pld, vec): + meta = pmt.make_dict(); + meta = pmt.dict_add(meta, pmt.intern('bps'), pmt.from_long(bps_pld)) + pld = pmt.init_u8vector(len(vec), vec) + + return pmt.cons(meta, pld) + + +class qa_symbol_mapper_c(gr_unittest.TestCase): + + def setUp(self): + self.tb = gr.top_block() + + def tearDown(self): + self.tb = None + + def test_001_qpsk(self): + data_hdr = np.random.randint(256, size=(2,), dtype=np.uint8) + data_pld = np.random.randint(256, size=(4,), dtype=np.uint8) + cnst_hdr = digital.constellation_bpsk() + cnst_pld = digital.constellation_calcdist( # normalized QPSK + [-1-1j, 1-1j, -1+1j, 1+1j], + [0x0, 0x2, 0x3, 0x1], + 4, + 1, + digital.constellation.normalization.POWER_NORMALIZATION) + expected_result = modulate_u8vector(cnst_hdr, data_hdr) + expected_result += modulate_u8vector(cnst_pld, data_pld) + # create and run flowgraph + mapper = symbol_mapper_c(symbol_mapper_c.constel.CONSTEL_BPSK) + mapper.to_basic_block()._post( + pmt.intern('hdr'), + create_pdu(cnst_pld.bits_per_symbol(), data_hdr)) + mapper.to_basic_block()._post( + pmt.intern('pld'), + create_pdu(cnst_pld.bits_per_symbol(), data_pld)) + dst = blocks.vector_sink_c() + self.tb.connect(mapper, dst) + self.tb.start() + sleep(1) # wait for flowgraph to complete + self.tb.stop() + # check data + actual_result = dst.data() + self.assertEqual(len(actual_result), len(expected_result)) + self.assertFloatTuplesAlmostEqual(actual_result, expected_result) + + def test_002_16qam(self): + data_hdr = np.random.randint(256, size=(2,), dtype=np.uint8) + data_pld = np.random.randint(256, size=(4,), dtype=np.uint8) + cnst_hdr = digital.constellation_bpsk() + cnst_pld = digital.constellation_16qam() + expected_result = modulate_u8vector(cnst_hdr, data_hdr) + expected_result += modulate_u8vector(cnst_pld, data_pld) + # create and run flowgraph + mapper = symbol_mapper_c(symbol_mapper_c.constel.CONSTEL_BPSK) + mapper.to_basic_block()._post( + pmt.intern('hdr'), + create_pdu(cnst_pld.bits_per_symbol(), data_hdr)) + mapper.to_basic_block()._post( + pmt.intern('pld'), + create_pdu(cnst_pld.bits_per_symbol(), data_pld)) + dst = blocks.vector_sink_c() + self.tb.connect(mapper, dst) + self.tb.start() + sleep(1) # wait for flowgraph to complete + self.tb.stop() + # check data + actual_result = dst.data() + self.assertEqual(len(actual_result), len(expected_result)) + self.assertFloatTuplesAlmostEqual(actual_result, expected_result) + + +if __name__ == '__main__': + gr_unittest.run(qa_symbol_mapper_c)