diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..55faf5ad6503ff0e054698ef0765085f8da24f4a --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +build/ +__pycache__/ +*.swp diff --git a/grc/elen90089_header_format_cdc.block.yml b/grc/elen90089_header_format_cdc.block.yml index 0bee652f6d423161eb42cba630cbd3bde1c584c2..df139f0f7033b9cfbad8072d24582eb06e3f4cb7 100644 --- a/grc/elen90089_header_format_cdc.block.yml +++ b/grc/elen90089_header_format_cdc.block.yml @@ -5,13 +5,14 @@ flags: [ show_id ] templates: imports: import elen90089 - make: elen90089.header_format_cdc(${access_code}, ${threshold}, ${bps}) + var_make: |- + self.${id} = ${id} = elen90089.header_format_cdc(${access_code}, ${threshold}, ${bps}) parameters: - id: access_code label: Access Code dtype: string - default: '11001010' + default: digital.packet_utils.default_access_code - id: threshold label: Threshold dtype: int @@ -20,5 +21,7 @@ parameters: label: Bits per symbol dtype: int default: 1 - + +value: ${ elen90089.header_format_cdc(access_code, threshold, bps) } + file_format: 1 diff --git a/grc/elen90089_symbol_mapper_c.block.yml b/grc/elen90089_symbol_mapper_c.block.yml index e10c2d5b35612308548b540bc209fec5602b621e..1704c1d64863dce0b94ae640ad50427a2608626f 100644 --- a/grc/elen90089_symbol_mapper_c.block.yml +++ b/grc/elen90089_symbol_mapper_c.block.yml @@ -4,7 +4,7 @@ category: '[elen90089]' templates: imports: import elen90089 - make: elen90089.symbol_mapper_c(elen90089.symbol_mapper_c.constel.${constel_header}, {tsb_tag_key}) + make: elen90089.symbol_mapper_c(elen90089.symbol_mapper_c.constel.${constel_header}, ${tsb_tag_key}) parameters: - id: constel_header diff --git a/include/elen90089/header_format_cdc.h b/include/elen90089/header_format_cdc.h index 42d4e3144c4ee83713f153005ece883790ea965c..21df1b0fc5838943557b93a049a6e43c86dfbf9d 100644 --- a/include/elen90089/header_format_cdc.h +++ b/include/elen90089/header_format_cdc.h @@ -11,26 +11,28 @@ #include <elen90089/api.h> #include <gnuradio/digital/header_format_default.h> #include <pmt/pmt.h> +#include <boost/crc.hpp> namespace gr { namespace elen90089 { /*! - * \brief Header formatter for CDC packets that adds payload bits/symbol - * format and a packet number counter + * \brief Header formatter for CDC packets that adds counter, packet length, + * payload bits/symbol, and 8-bit CRC. * * \details * - * Same as digital::header_format_counter but updates \p bps field of header + * Child class of header_format_default. The \p bps field of header is updated * based on value found in packet metadata dictionary. Packet structure is: * * | access_code | hdr | payload | * * Where the access_code is <= 64 bits and hdr is: * - * | 0 -- 15 | 16 -- 31 | - * | pkt len | pkt len | - * | bits/sym | counter | + * | 0 -- 7 | 8 -- 15 | + * | counter | + * | pkt len | + * | bits/sym | crc8 | * * Access code and header are formatted for network byte order (big endian) * @@ -51,7 +53,7 @@ public: * Creates a header from the access code and packet length to build an * output packet in the form: * - * | access code | pkt len | pkt len | bps | counter | + * | access code | counter | pkt len | bps | crc8 | * * \param nbytes The length (in bytes) of the \p input payload * \param input An array of unsigned chars of the packet payload @@ -85,7 +87,8 @@ public: int bps); protected: - uint16_t d_counter; //!< keeps track of the number of packets transmitted + uint16_t d_counter; + boost::crc_optimal<8, 0x07, 0xFF, 0x00, false, false> d_crc_impl; //! Verify that the header is valid bool header_ok() override; @@ -95,9 +98,7 @@ protected: * * Extracts the header of the form: * - * \verbatim - * | access code | pkt len | pkt len | bps | counter | payload | - * \endverbatim + * | access code | counter | pkt len | bps | crc8 | payload | */ int header_payload() override; }; diff --git a/lib/CMakeLists.txt b/lib/CMakeLists.txt index e6470911aa94bebf40d3c57e3d4d1cb2aca035f7..505cbddc305651cced6842d7a1cbb6d1eb7b500a 100644 --- a/lib/CMakeLists.txt +++ b/lib/CMakeLists.txt @@ -68,7 +68,10 @@ list(APPEND test_elen90089_sources qa_header_format_cdc.cc ) # Anything we need to link to for the unit tests go here -list(APPEND GR_TEST_TARGET_DEPS gnuradio-elen90089) +list(APPEND GR_TEST_TARGET_DEPS + gnuradio-elen90089 + gnuradio-blocks +) if(NOT test_elen90089_sources) MESSAGE(STATUS "No C++ unit tests... skipping") diff --git a/lib/header_format_cdc.cc b/lib/header_format_cdc.cc index 568f63f14cc1809cc75f018c2a8dc57a22fbb4bb..efcac9486d04fce0be3813de7827f441df1d463e 100644 --- a/lib/header_format_cdc.cc +++ b/lib/header_format_cdc.cc @@ -13,7 +13,9 @@ namespace gr { namespace elen90089 { header_format_cdc::sptr -header_format_cdc::make(const std::string& access_code, int threshold, int bps) +header_format_cdc::make(const std::string& access_code, + int threshold, + int bps) { return header_format_cdc::sptr( new header_format_cdc(access_code, threshold, bps)); @@ -22,9 +24,10 @@ header_format_cdc::make(const std::string& access_code, int threshold, int bps) header_format_cdc::header_format_cdc(const std::string& access_code, int threshold, int bps) - : header_format_default(access_code, threshold, bps) + : header_format_default(access_code, threshold, bps), + d_counter(0) { - d_counter = 0; + assert(bps > 0 && bps < 5); } header_format_cdc::~header_format_cdc() {} @@ -35,59 +38,77 @@ bool header_format_cdc::format(int nbytes_in, pmt::pmt_t& info) { - // update bps from metadata if present - int bps = d_bps; - if (pmt::is_dict(info) && pmt::dict_has_key(info, pmt::mp("bps"))) { - pmt::pmt_t val = pmt::dict_ref(info, pmt::mp("bps"), pmt::PMT_NIL); - bps = pmt::to_long(val); - } - assert(bps > 0 && bps < 5); - // Creating the output pmt copies data; free our own here when done. volk::vector<uint8_t> bytes_out(header_nbytes()); + // update bps from metadata dict if present + uint8_t bps = (uint8_t)pmt::to_long(pmt::dict_ref(info, + pmt::mp("bps"), + pmt::from_long(d_bps))); + assert(bps > 0 && bps < 5); + + // calculate 8-bit CRC + d_crc_impl.reset(); + d_crc_impl.process_bytes((void const*)&d_counter, 2); + d_crc_impl.process_bytes((void const*)&nbytes_in, 2); + d_crc_impl.process_bytes((void const*)&bps, 1); + uint8_t crc = d_crc_impl(); + + // create header digital::header_buffer header(bytes_out.data()); header.add_field64(d_access_code, d_access_code_len); - header.add_field16((uint16_t)(nbytes_in)); - header.add_field16((uint16_t)(nbytes_in)); - header.add_field16((uint16_t)(bps)); - header.add_field16((uint16_t)(d_counter)); + header.add_field16(d_counter); + header.add_field16(nbytes_in); + header.add_field8 (bps); + header.add_field8 (crc); + + d_counter++; // Package output data into a PMT vector output = pmt::init_u8vector(header_nbytes(), bytes_out.data()); - d_counter++; - return true; } size_t header_format_cdc::header_nbits() const { - return d_access_code_len + 8 * 4 * sizeof(uint16_t); + return d_access_code_len + 8 * 3 * sizeof(uint16_t); } bool header_format_cdc::header_ok() { - // confirm that two copies of header info are identical - uint16_t len0 = d_hdr_reg.extract_field16(0); - uint16_t len1 = d_hdr_reg.extract_field16(16); - return (len0 ^ len1) == 0; + uint16_t counter = d_hdr_reg.extract_field16(0); + uint16_t pktlen = d_hdr_reg.extract_field16(16); + uint8_t bps = d_hdr_reg.extract_field8(32); + uint8_t crc_rcvd = d_hdr_reg.extract_field8(40); + + // check crc8 + d_crc_impl.reset(); + d_crc_impl.process_bytes((void const*)&counter, 2); + d_crc_impl.process_bytes((void const*)&pktlen,2); + d_crc_impl.process_bytes((void const*)&bps, 1); + uint8_t crc_clcd = d_crc_impl(); + + return (crc_rcvd == crc_clcd); } int header_format_cdc::header_payload() { - uint16_t len = d_hdr_reg.extract_field16(0); - uint16_t bps = d_hdr_reg.extract_field16(32); - uint16_t counter = d_hdr_reg.extract_field16(48); + uint16_t counter = d_hdr_reg.extract_field16(0); + uint16_t pktlen = d_hdr_reg.extract_field16(16); + uint8_t bps = d_hdr_reg.extract_field8(32); d_bps = bps; d_info = pmt::make_dict(); d_info = pmt::dict_add( - d_info, pmt::intern("payload symbols"), pmt::from_long(8 * len / d_bps)); - d_info = pmt::dict_add(d_info, pmt::intern("bps"), pmt::from_long(bps)); - d_info = pmt::dict_add(d_info, pmt::intern("counter"), pmt::from_long(counter)); - return static_cast<int>(len); + d_info, pmt::intern("counter"), pmt::from_long(counter)); + d_info = pmt::dict_add( + d_info, pmt::intern("payload symbols"), pmt::from_long(8*pktlen/d_bps)); + d_info = pmt::dict_add( + d_info, pmt::intern("bps"), pmt::from_long(bps)); + + return static_cast<int>(pktlen); } } /* namespace elen90089 */ diff --git a/lib/qa_header_format_cdc.cc b/lib/qa_header_format_cdc.cc index 4896e160e9247aea970650db4b27cabf18585e16..423589624f63f9a3133baf26b66a30a4cd2facd1 100644 --- a/lib/qa_header_format_cdc.cc +++ b/lib/qa_header_format_cdc.cc @@ -2,57 +2,106 @@ #include <elen90089/header_format_cdc.h> #include <gnuradio/attributes.h> +#include <gnuradio/blocks/unpack_k_bits.h> +#include <volk/volk_alloc.hh> #include <boost/test/unit_test.hpp> namespace gr { namespace elen90089 { -BOOST_AUTO_TEST_CASE(test_header_format_cdc_bps) +BOOST_AUTO_TEST_CASE(test_cdc_format) { static const int N = 4; std::string ac = "10110010"; // 0xB2 std::vector<unsigned char> data{ 0x01, 0x02, 0x03, 0x04 }; int threshold = 3; - int bps = 1; header_format_cdc::sptr hdr_format; - hdr_format = header_format_cdc::make(ac, threshold, bps); + hdr_format = header_format_cdc::make(ac, threshold, 1); + int bps = 2; pmt::pmt_t output; pmt::pmt_t info = pmt::make_dict(); - info = pmt::dict_add(info, pmt::mp("bps"), pmt::from_long(2)); + info = pmt::dict_add(info, pmt::mp("bps"), pmt::from_long(bps)); bool ret = hdr_format->format(N, data.data(), output, info); size_t length = pmt::length(output); BOOST_REQUIRE(ret); BOOST_REQUIRE_EQUAL(length, hdr_format->header_nbytes()); - BOOST_REQUIRE_EQUAL(8 * length, hdr_format->header_nbits()); + BOOST_REQUIRE_EQUAL(8*length, hdr_format->header_nbits()); - // Test access code + // Test access code - 0xb2 unsigned char h0 = pmt::u8vector_ref(output, 0); - BOOST_REQUIRE_EQUAL(0xB2, (int)h0); + BOOST_REQUIRE_EQUAL(0xb2, (int)h0); - // Test packet length - 0x0404 + // Test counter - 0x0000 unsigned char h1 = pmt::u8vector_ref(output, 1); unsigned char h2 = pmt::u8vector_ref(output, 2); + BOOST_REQUIRE_EQUAL(0x00, (int)h1); + BOOST_REQUIRE_EQUAL(0x00, (int)h2); + + // Test packet length - 0x0004 unsigned char h3 = pmt::u8vector_ref(output, 3); unsigned char h4 = pmt::u8vector_ref(output, 4); - BOOST_REQUIRE_EQUAL(0x00, (int)h1); - BOOST_REQUIRE_EQUAL(0x04, (int)h2); BOOST_REQUIRE_EQUAL(0x00, (int)h3); BOOST_REQUIRE_EQUAL(0x04, (int)h4); - // Test bits/symbol - 0x0002 + // Test bits/symbol - 0x02 unsigned char h5 = pmt::u8vector_ref(output, 5); + BOOST_REQUIRE_EQUAL(0x02, (int)h5); + + // Test CRC - 0x9c unsigned char h6 = pmt::u8vector_ref(output, 6); - BOOST_REQUIRE_EQUAL(0x00, (int)h5); - BOOST_REQUIRE_EQUAL(0x02, (int)h6); + BOOST_REQUIRE_EQUAL(0x9c, (int)h6); +} - // Test counter - 0x0000 - unsigned char h7 = pmt::u8vector_ref(output, 7); - unsigned char h8 = pmt::u8vector_ref(output, 8); - BOOST_REQUIRE_EQUAL(0x00, (int)h7); - BOOST_REQUIRE_EQUAL(0x00, (int)h8); +BOOST_AUTO_TEST_CASE(test_cdc_parse) +{ + static const int nbytes = 11; + static const int nbits = 8 * nbytes; + volk::vector<unsigned char> bytes(nbytes); + volk::vector<unsigned char> bits(nbits); + + int index = 0; + bytes[index + 0] = 0xb2; // access code + bytes[index + 1] = 0x00; // counter + bytes[index + 2] = 0x00; + bytes[index + 3] = 0x00; // pkt len + bytes[index + 4] = 0x04; + bytes[index + 5] = 0x02; // bps + bytes[index + 6] = 0x9c; // crc8 + bytes[index + 7] = 0x01; // payload + bytes[index + 8] = 0x02; + bytes[index + 9] = 0x03; + bytes[index + 10] = 0x04; + + blocks::kernel::unpack_k_bits unpacker = blocks::kernel::unpack_k_bits(8); + unpacker.unpack(bits.data(), bytes.data(), nbytes); + + uint8_t expected_bps = 2; + std::string ac = "10110010"; + header_format_cdc::sptr hdr_format = header_format_cdc::make(ac, 0, 1); + + int count = 0; + std::vector<pmt::pmt_t> info; + bool ret = hdr_format->parse(nbits, bits.data(), info, count); + + BOOST_REQUIRE(ret); + BOOST_REQUIRE_EQUAL((size_t)1, info.size()); + + pmt::pmt_t dict = info[0]; + int counter = pmt::to_long( + pmt::dict_ref(dict, pmt::intern("counter"), pmt::from_long(-1))); + int payload_syms = pmt::to_long( + pmt::dict_ref(dict, pmt::intern("payload symbols"), pmt::from_long(-1))); + int bps = pmt::to_long( + pmt::dict_ref(dict, pmt::intern("bps"), pmt::from_long(-1))); + + int hdr_bits = (int)hdr_format->header_nbits(); + int expected_bits = nbits - hdr_bits; + BOOST_REQUIRE_EQUAL(0, counter); + BOOST_REQUIRE_EQUAL(expected_bits, payload_syms * bps); + BOOST_REQUIRE_EQUAL(expected_bps, (uint8_t)bps); } } /* namespace elen90089 */ diff --git a/lib/symbol_mapper_c_impl.cc b/lib/symbol_mapper_c_impl.cc index 02ee584903488a407d5cbfab190c326177a7dfec..2a562cd4f0db489bfc517814bfd8803215d133d2 100644 --- a/lib/symbol_mapper_c_impl.cc +++ b/lib/symbol_mapper_c_impl.cc @@ -90,7 +90,7 @@ int symbol_mapper_c_impl::calculate_output_stream_length(const gr_vector_int &ni d_metadata = pmt::car(msg); d_bps_payload = pmt::to_long(pmt::dict_ref( - d_metadata, pmt::mp("bps"), pmt::PMT_NIL)); + d_metadata, pmt::mp("bps"), pmt::from_long(1))); d_header = pmt::cdr(msg); d_len_header = pmt::blob_length(d_header); @@ -177,20 +177,25 @@ int symbol_mapper_c_impl::work(int noutput_items, bytes = static_cast<const uint8_t*>(pmt::uniform_vector_elements(d_payload, io)); map_to_symbols(bytes, d_len_payload, out, d_bps_payload); + // add start of burst and end of burst tags + int offset = nitems_written(0); + add_item_tag(0, offset, + pmt::mp("tx_sob"), + pmt::PMT_T, + alias_pmt()); + add_item_tag(0, offset + nout - 1, + pmt::mp("tx_eob"), + pmt::PMT_T, + alias_pmt()); + // 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()); + add_item_tag(0, offset, + pmt::car(item), + pmt::cdr(item), + alias_pmt()); } d_len_header = 0; diff --git a/python/bindings/header_format_cdc_python.cc b/python/bindings/header_format_cdc_python.cc index 5ac77bc8d69570f12fc385cec9ac1e688d562f87..28af348675f208e9876fc9db1a326e847ea17864 100644 --- a/python/bindings/header_format_cdc_python.cc +++ b/python/bindings/header_format_cdc_python.cc @@ -14,7 +14,7 @@ /* BINDTOOL_GEN_AUTOMATIC(0) */ /* BINDTOOL_USE_PYGCCXML(0) */ /* BINDTOOL_HEADER_FILE(header_format_cdc.h) */ -/* BINDTOOL_HEADER_FILE_HASH(a83bb767baf65cce828e12e815183c6a) */ +/* BINDTOOL_HEADER_FILE_HASH(54efca7bed3ffdffd52613e3cdfea044) */ /***********************************************************************************/ #include <pybind11/complex.h>