From 8235f53e44d5de188b0884142b200afa0ef2188e Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 2 Jul 2023 11:38:00 +0200 Subject: [PATCH 01/35] implement argument parsing for IQ stream --- README.md | 1 + include/decoder.hpp | 7 ++++++- src/decoder.cpp | 16 +++++++++++++--- src/main.cpp | 8 +++++--- 4 files changed, 25 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 6a5ea13..75adf81 100644 --- a/README.md +++ b/README.md @@ -18,6 +18,7 @@ Usage: with -i option) -d arg print debug information (default: 0) -P, --packed pack rx data (1 byte = 8 bits) + --iq Receive IQ instead of bitstream --uplink arg enable uplink parsing with predefined scrambilng code ``` diff --git a/include/decoder.hpp b/include/decoder.hpp index 770b5d0..1123cb7 100644 --- a/include/decoder.hpp +++ b/include/decoder.hpp @@ -39,7 +39,8 @@ class Decoder { public: Decoder(unsigned int receive_port, unsigned int send_port, bool packed, std::optional input_file, - std::optional output_file, std::optional uplink_scrambling_code); + std::optional output_file, bool iq_or_bit_stream, + std::optional uplink_scrambling_code); ~Decoder(); void main_loop(); @@ -61,6 +62,10 @@ class Decoder { // uplink ? std::optional uplink_scrambling_code_; + // IQ stream -> true + // bit stream -> false + bool iq_or_bit_stream_; + const std::size_t kRX_BUFFER_SIZE = 4096; const std::size_t kFRAME_LEN = 510; diff --git a/src/decoder.cpp b/src/decoder.cpp index 6273197..4031a3b 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -23,16 +23,26 @@ #include Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::optional input_file, - std::optional output_file, std::optional uplink_scrambling_code) + std::optional output_file, bool iq_or_bit_stream, + std::optional uplink_scrambling_code) : reporter_(std::make_shared(send_port)) , packed_(packed) - , uplink_scrambling_code_(uplink_scrambling_code) { + , uplink_scrambling_code_(uplink_scrambling_code) + , iq_or_bit_stream_(iq_or_bit_stream) { lower_mac_ = std::make_unique(reporter_); - // set scrambling_code for uplink if (uplink_scrambling_code_.has_value()) { + // set scrambling_code for uplink lower_mac_->set_scrambling_code(uplink_scrambling_code_.value()); + + if (iq_or_bit_stream_) { + throw std::runtime_error("IQ Stream is not supported for uplink decoding"); + } + } else { + if (iq_or_bit_stream_) { + throw std::runtime_error("IQ Stream is not supported for downlink decoding"); + } } // read input file from file or from socket diff --git a/src/main.cpp b/src/main.cpp index 508b7ee..ce2ec4a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -16,7 +16,7 @@ void sigint_handler(int s) { auto main(int argc, char** argv) -> int { unsigned int receive_port, send_port, debug_level; - bool packed; + bool packed, iq_or_bit_stream; std::optional input_file, output_file; std::optional uplink_scrambling_code; @@ -35,6 +35,7 @@ auto main(int argc, char** argv) -> int { ("o,outfile", " record data to binary file (can be replayed with -i option)", cxxopts::value>(output_file)) ("d", " print debug information", cxxopts::value()->default_value("0")) ("P,packed", "pack rx data (1 byte = 8 bits)", cxxopts::value()->default_value("false")) + ("iq", "Receive IQ instead of bitstream", cxxopts::value()->default_value("false")) ("uplink", " enable uplink parsing with predefined scrambilng code", cxxopts::value>(uplink_scrambling_code)) ; // clang-format on @@ -52,13 +53,14 @@ auto main(int argc, char** argv) -> int { debug_level = result["d"].as(); packed = result["packed"].as(); + iq_or_bit_stream = result["iq"].as(); } catch (std::exception& e) { std::cout << "error parsing options: " << e.what() << std::endl; return EXIT_FAILURE; } - auto decoder = - std::make_unique(receive_port, send_port, packed, input_file, output_file, uplink_scrambling_code); + auto decoder = std::make_unique(receive_port, send_port, packed, input_file, output_file, iq_or_bit_stream, + uplink_scrambling_code); if (input_file.has_value()) { std::cout << "Reading from input file " << *input_file << std::endl; From a513161fc247ba7d1a24585165ed1036b4561860 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 2 Jul 2023 14:18:39 +0200 Subject: [PATCH 02/35] move current decoder to bit_stream_decoder --- CMakeLists.txt | 1 + include/bit_stream_decoder.hpp | 115 ++++++++++++++++++++++++ include/decoder.hpp | 74 +--------------- src/bit_stream_decoder.cpp | 156 +++++++++++++++++++++++++++++++++ src/decoder.cpp | 152 +++----------------------------- 5 files changed, 288 insertions(+), 210 deletions(-) create mode 100644 include/bit_stream_decoder.hpp create mode 100644 src/bit_stream_decoder.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 325ba30..89896b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(tetra src/main.cpp src/reporter.cpp src/decoder.cpp + src/bit_stream_decoder.cpp src/l2/lower_mac.cpp src/l2/lower_mac_coding.cpp src/l2/upper_mac.cpp diff --git a/include/bit_stream_decoder.hpp b/include/bit_stream_decoder.hpp new file mode 100644 index 0000000..d630816 --- /dev/null +++ b/include/bit_stream_decoder.hpp @@ -0,0 +1,115 @@ +/* + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + * Tassilo Tanneberger + */ + +#pragma once + +#include +#include +#include +#include + +#include +#include + +/** + * Tetra downlink decoder for PI/4-DQPSK modulation + * + * Following downlink burst types for Phase Modulation are supported: (See TSI + * EN 300 392-2 V3.8.1 (2016-08) Table 9.2) + * + * normal continuous downlink burst (NDB) + * + * synchronization continuous downlink burst (SB) + * + * Follawing downlink burst types for Phase Modulation are not supported: + * discontinuous downlink burst (NDB) + * synchronization discontinuous downlink burst (SB) + * + * Uplink burst types are not supported: + * control uplink burst (CB) + * normal uplink burst (NUB) + */ +class BitStreamDecoder { + public: + BitStreamDecoder(std::shared_ptr lower_mac, bool is_uplink) + : lower_mac_(lower_mac) + , is_uplink_(is_uplink){}; + ~BitStreamDecoder() = default; + + /** + * @brief Process a received symbol. + * + * This function is called by "physical layer" when a bit is ready + * to be processed. + * + * Note that "frame" is actually called "burst" in Tetra doc + * + * @return true if frame (burst) found, false otherwise + * + */ + void process_bit(uint8_t symbol) noexcept; + + private: + std::shared_ptr lower_mac_{}; + + bool is_synchronized_ = false; + bool is_uplink_{}; + std::size_t sync_bit_counter_ = 0; + + const std::size_t kFRAME_LEN = 510; + + std::vector frame_{}; + + // 9.4.4.3.2 Normal training sequence + const std::vector kNORMAL_TRAINING_SEQ_1 = {1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0}; // n1..n22 + const std::vector kNORMAL_TRAINING_SEQ_2 = {0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, + 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0}; // p1..p22 + const std::vector kNORMAL_TRAINING_SEQ_3_BEGIN = {0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1}; // q11..q22 + const std::vector kNORMAL_TRAINING_SEQ_3_END = {1, 0, 1, 1, 0, 1, 1, 1, 0, 0}; // q1..q10 + + // 9.4.4.3.3 Extended training sequence + const std::vector kEXTENDED_TRAINING_SEQ = {1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, + 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1}; // x1..x30 + + // 9.4.4.3.4 Synchronisation training sequence + const std::vector kSYNC_TRAINING_SEQ = {1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, + 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1}; // y1..y38 + + /** + * @brief Reset the synchronizer + * + * Burst was matched, we can reset the synchronizer to allow 50 missing frames + * (expressed in burst units = 50 * 510 bits) + * + */ + void reset_synchronizer() noexcept; + + /** + * @brief Process frame to decide which type of burst it is then service lower + * MAC + * + */ + void process_downlink_frame() noexcept; + + /** + * @brief Return pattern/data comparison errors count at position in data + * vector + * + * @param data Vector to look in from pattern + * @param pattern Pattern to search + * @param position Position in vector to start search + * + * @return Score based on similarity with pattern (differences count between + * vector and pattern) + * + */ + static auto pattern_at_position_score(const std::vector& data, const std::vector& pattern, + std::size_t position) noexcept -> std::size_t; +}; diff --git a/include/decoder.hpp b/include/decoder.hpp index 1123cb7..b1036c2 100644 --- a/include/decoder.hpp +++ b/include/decoder.hpp @@ -7,14 +7,14 @@ * Tassilo Tanneberger */ -#ifndef TETRA_DECODER_DECODER_HPP -#define TETRA_DECODER_DECODER_HPP +#pragma once #include #include #include #include +#include #include #include @@ -46,12 +46,11 @@ class Decoder { void main_loop(); private: - std::unique_ptr lower_mac_{}; + std::shared_ptr lower_mac_{}; std::shared_ptr reporter_{}; + std::unique_ptr bit_stream_decoder_{}; bool packed_ = false; - bool is_synchronized_ = false; - std::size_t sync_bit_counter_ = 0; // input and output file descriptor int input_fd_ = 0; @@ -67,69 +66,4 @@ class Decoder { bool iq_or_bit_stream_; const std::size_t kRX_BUFFER_SIZE = 4096; - const std::size_t kFRAME_LEN = 510; - - std::vector frame_{}; - - // 9.4.4.3.2 Normal training sequence - const std::vector kNORMAL_TRAINING_SEQ_1 = {1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, - 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0}; // n1..n22 - const std::vector kNORMAL_TRAINING_SEQ_2 = {0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, - 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 0}; // p1..p22 - const std::vector kNORMAL_TRAINING_SEQ_3_BEGIN = {0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1}; // q11..q22 - const std::vector kNORMAL_TRAINING_SEQ_3_END = {1, 0, 1, 1, 0, 1, 1, 1, 0, 0}; // q1..q10 - - // 9.4.4.3.3 Extended training sequence - const std::vector kEXTENDED_TRAINING_SEQ = {1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, - 0, 1, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1}; // x1..x30 - - // 9.4.4.3.4 Synchronisation training sequence - const std::vector kSYNC_TRAINING_SEQ = {1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, - 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1}; // y1..y38 - - /** - * @brief Process a received symbol. - * - * This function is called by "physical layer" when a bit is ready - * to be processed. - * - * Note that "frame" is actually called "burst" in Tetra doc - * - * @return true if frame (burst) found, false otherwise - * - */ - void process_bit(uint8_t symbol) noexcept; - - /** - * @brief Reset the synchronizer - * - * Burst was matched, we can reset the synchronizer to allow 50 missing frames - * (expressed in burst units = 50 * 510 bits) - * - */ - void reset_synchronizer() noexcept; - - /** - * @brief Process frame to decide which type of burst it is then service lower - * MAC - * - */ - void process_downlink_frame() noexcept; - - /** - * @brief Return pattern/data comparison errors count at position in data - * vector - * - * @param data Vector to look in from pattern - * @param pattern Pattern to search - * @param position Position in vector to start search - * - * @return Score based on similarity with pattern (differences count between - * vector and pattern) - * - */ - static auto pattern_at_position_score(const std::vector& data, const std::vector& pattern, - std::size_t position) noexcept -> std::size_t; }; - -#endif // TETRA_DECODER_DECODER_HPP diff --git a/src/bit_stream_decoder.cpp b/src/bit_stream_decoder.cpp new file mode 100644 index 0000000..736e1c2 --- /dev/null +++ b/src/bit_stream_decoder.cpp @@ -0,0 +1,156 @@ +/* + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + * Tassilo Tanneberger + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +void BitStreamDecoder::process_bit(uint8_t symbol) noexcept { + assert(symbol <= 1); + + // insert symbol at buffer end + frame_.push_back(symbol); + + // not enough data to process + if (frame_.size() < kFRAME_LEN) { + return; + } + + if (!is_uplink_) { + bool frame_found = false; + // XXX: this will only find Normal Continous Downlink Burst and + // Synchronization Continous Downlink Burst + uint32_t score_begin = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_3_BEGIN, 0); + uint32_t score_end = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_3_END, 500); + + // frame (burst) is matched and can be processed + if ((score_begin == 0) && (score_end < 2)) { + frame_found = true; + // reset missing sync synchronizer + reset_synchronizer(); + } + + bool cleared_flag = false; + + // the frame can be processed either by presence of + // training sequence, either by synchronized and + // still allowed missing frames + if (frame_found || (is_synchronized_ && ((sync_bit_counter_ % 510) == 0))) { + process_downlink_frame(); + + // frame has been processed, so clear it + frame_.clear(); + + // set flag to prevent erasing first bit in frame + cleared_flag = true; + } + + sync_bit_counter_--; + + // synchronization is lost + if (sync_bit_counter_ <= 0) { + printf("* synchronization lost\n"); + is_synchronized_ = false; + sync_bit_counter_ = 0; + } + + // remove first symbol from buffer to make space for next one + if (!cleared_flag) { + frame_.erase(frame_.begin()); + } + } else { + // check at the end + auto score_ssn = pattern_at_position_score(frame_, kEXTENDED_TRAINING_SEQ, 88); + + auto score_nub = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_1, 220); + auto score_nub_split = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_2, 220); + + auto minimum_score = score_ssn; + BurstType burstType = BurstType::ControlUplinkBurst; + + if (score_nub < minimum_score) { + minimum_score = score_nub; + burstType = BurstType::NormalUplinkBurst; + } + + if (score_nub_split < minimum_score) { + minimum_score = score_nub_split; + burstType = BurstType::NormalUplinkBurstSplit; + } + + if (score_ssn <= 4) { + // fmt::print("Processing burst type: {}\n", ControlUplinkBurst); + if (lower_mac_->process(frame_, burstType)) + std::vector(frame_.begin() + 200, frame_.end()).swap(frame_); + else + frame_.erase(frame_.begin()); + } else if (minimum_score <= 2) { + // valid burst found, send it to lower MAC + // fmt::print("Processing burst type: {}\n", burstType); + lower_mac_->process(frame_, burstType); + + frame_.erase(frame_.begin()); + // std::vector(frame_.begin()+462, frame_.end()).swap(frame_); + } else { + frame_.erase(frame_.begin()); + } + } +} + +void BitStreamDecoder::reset_synchronizer() noexcept { + is_synchronized_ = true; + sync_bit_counter_ = kFRAME_LEN * 50; +} + +void BitStreamDecoder::process_downlink_frame() noexcept { + auto score_sb = pattern_at_position_score(frame_, kSYNC_TRAINING_SEQ, 214); + auto score_ndb = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_1, 244); + auto score_ndb_split = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_2, 244); + + auto minimum_score = score_sb; + BurstType burstType = BurstType::SynchronizationBurst; + + if (score_ndb < minimum_score) { + minimum_score = score_ndb; + burstType = BurstType::NormalDownlinkBurst; + } + + if (score_ndb_split < minimum_score) { + minimum_score = score_ndb_split; + burstType = BurstType::NormalDownlinkBurstSplit; + } + + if (minimum_score <= 5) { + // valid burst found, send it to lower MAC + fmt::print("Processing burst type: {}\n", burstType); + lower_mac_->process(frame_, burstType); + } +} + +auto BitStreamDecoder::pattern_at_position_score(const std::vector& data, const std::vector& pattern, + std::size_t position) noexcept -> std::size_t { + std::size_t errors = 0; + + for (auto i = 0ul; i < pattern.size(); i++) { + errors += (pattern[i] ^ data[position + i]); + } + + return errors; +} diff --git a/src/decoder.cpp b/src/decoder.cpp index 4031a3b..ad4e0d6 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -30,7 +30,8 @@ Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::op , uplink_scrambling_code_(uplink_scrambling_code) , iq_or_bit_stream_(iq_or_bit_stream) { - lower_mac_ = std::make_unique(reporter_); + lower_mac_ = std::make_shared(reporter_); + bit_stream_decoder_ = std::make_unique(lower_mac_, uplink_scrambling_code_.has_value()); if (uplink_scrambling_code_.has_value()) { // set scrambling_code for uplink @@ -106,146 +107,17 @@ void Decoder::main_loop() { } } - for (auto i = 0; i < bytes_read; i++) { - if (packed_) { - for (auto j = 0; j < 8; j++) { - this->process_bit((rx_buffer[i] >> j) & 0x1); - } - } else { - this->process_bit(rx_buffer[i]); - } - } -} - -void Decoder::process_bit(uint8_t symbol) noexcept { - assert(symbol <= 1); - - // insert symbol at buffer end - frame_.push_back(symbol); - - // not enough data to process - if (frame_.size() < kFRAME_LEN) { - return; - } - - if (!uplink_scrambling_code_.has_value()) { - bool frame_found = false; - // XXX: this will only find Normal Continous Downlink Burst and - // Synchronization Continous Downlink Burst - uint32_t score_begin = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_3_BEGIN, 0); - uint32_t score_end = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_3_END, 500); - - // frame (burst) is matched and can be processed - if ((score_begin == 0) && (score_end < 2)) { - frame_found = true; - // reset missing sync synchronizer - reset_synchronizer(); - } - - bool cleared_flag = false; - - // the frame can be processed either by presence of - // training sequence, either by synchronized and - // still allowed missing frames - if (frame_found || (is_synchronized_ && ((sync_bit_counter_ % 510) == 0))) { - process_downlink_frame(); - - // frame has been processed, so clear it - frame_.clear(); - - // set flag to prevent erasing first bit in frame - cleared_flag = true; - } - - sync_bit_counter_--; - - // synchronization is lost - if (sync_bit_counter_ <= 0) { - printf("* synchronization lost\n"); - is_synchronized_ = false; - sync_bit_counter_ = 0; - } - - // remove first symbol from buffer to make space for next one - if (!cleared_flag) { - frame_.erase(frame_.begin()); - } + if (iq_or_bit_stream_) { + throw std::runtime_error("IQ Stream is not supported"); } else { - // check at the end - auto score_ssn = pattern_at_position_score(frame_, kEXTENDED_TRAINING_SEQ, 88); - - auto score_nub = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_1, 220); - auto score_nub_split = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_2, 220); - - auto minimum_score = score_ssn; - BurstType burstType = BurstType::ControlUplinkBurst; - - if (score_nub < minimum_score) { - minimum_score = score_nub; - burstType = BurstType::NormalUplinkBurst; - } - - if (score_nub_split < minimum_score) { - minimum_score = score_nub_split; - burstType = BurstType::NormalUplinkBurstSplit; - } - - if (score_ssn <= 4) { - // fmt::print("Processing burst type: {}\n", ControlUplinkBurst); - if (lower_mac_->process(frame_, burstType)) - std::vector(frame_.begin() + 200, frame_.end()).swap(frame_); - else - frame_.erase(frame_.begin()); - } else if (minimum_score <= 2) { - // valid burst found, send it to lower MAC - // fmt::print("Processing burst type: {}\n", burstType); - lower_mac_->process(frame_, burstType); - - frame_.erase(frame_.begin()); - // std::vector(frame_.begin()+462, frame_.end()).swap(frame_); - } else { - frame_.erase(frame_.begin()); + for (auto i = 0; i < bytes_read; i++) { + if (packed_) { + for (auto j = 0; j < 8; j++) { + bit_stream_decoder_->process_bit((rx_buffer[i] >> j) & 0x1); + } + } else { + bit_stream_decoder_->process_bit(rx_buffer[i]); + } } } } - -void Decoder::reset_synchronizer() noexcept { - is_synchronized_ = true; - sync_bit_counter_ = kFRAME_LEN * 50; -} - -void Decoder::process_downlink_frame() noexcept { - auto score_sb = pattern_at_position_score(frame_, kSYNC_TRAINING_SEQ, 214); - auto score_ndb = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_1, 244); - auto score_ndb_split = pattern_at_position_score(frame_, kNORMAL_TRAINING_SEQ_2, 244); - - auto minimum_score = score_sb; - BurstType burstType = BurstType::SynchronizationBurst; - - if (score_ndb < minimum_score) { - minimum_score = score_ndb; - burstType = BurstType::NormalDownlinkBurst; - } - - if (score_ndb_split < minimum_score) { - minimum_score = score_ndb_split; - burstType = BurstType::NormalDownlinkBurstSplit; - } - - if (minimum_score <= 5) { - // valid burst found, send it to lower MAC - fmt::print("Processing burst type: {}\n", burstType); - lower_mac_->process(frame_, burstType); - } -} - -auto Decoder::pattern_at_position_score(const std::vector& data, const std::vector& pattern, - std::size_t position) noexcept -> std::size_t { - std::size_t errors = 0; - - for (auto i = 0ul; i < pattern.size(); i++) { - errors += (pattern[i] ^ data[position + i]); - } - - return errors; -} From 9ef1196df7e4d212f23619fef160e4e8e7b6d73a Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 2 Jul 2023 17:02:41 +0200 Subject: [PATCH 03/35] implement reading IQ and hard decision symbol mapper --- CMakeLists.txt | 1 + include/bit_stream_decoder.hpp | 1 - include/decoder.hpp | 4 +++- include/iq_stream_decoder.hpp | 34 ++++++++++++++++++++++++++++++ src/bit_stream_decoder.cpp | 12 +---------- src/decoder.cpp | 22 +++++++++++--------- src/iq_stream_decoder.cpp | 38 ++++++++++++++++++++++++++++++++++ 7 files changed, 89 insertions(+), 23 deletions(-) create mode 100644 include/iq_stream_decoder.hpp create mode 100644 src/iq_stream_decoder.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 89896b4..e6771ef 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(tetra src/reporter.cpp src/decoder.cpp src/bit_stream_decoder.cpp + src/iq_stream_decoder.cpp src/l2/lower_mac.cpp src/l2/lower_mac_coding.cpp src/l2/upper_mac.cpp diff --git a/include/bit_stream_decoder.hpp b/include/bit_stream_decoder.hpp index d630816..f490d81 100644 --- a/include/bit_stream_decoder.hpp +++ b/include/bit_stream_decoder.hpp @@ -15,7 +15,6 @@ #include #include -#include /** * Tetra downlink decoder for PI/4-DQPSK modulation diff --git a/include/decoder.hpp b/include/decoder.hpp index b1036c2..d191537 100644 --- a/include/decoder.hpp +++ b/include/decoder.hpp @@ -15,6 +15,7 @@ #include #include +#include #include #include @@ -48,7 +49,8 @@ class Decoder { private: std::shared_ptr lower_mac_{}; std::shared_ptr reporter_{}; - std::unique_ptr bit_stream_decoder_{}; + std::shared_ptr bit_stream_decoder_{}; + std::unique_ptr iq_stream_decoder_{}; bool packed_ = false; diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp new file mode 100644 index 0000000..06e5a52 --- /dev/null +++ b/include/iq_stream_decoder.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + * Tassilo Tanneberger + */ + +#pragma once + +#include + +#include + +/** + * Tetra downlink decoder for PI/4-DQPSK modulation + * + * Implements Channel Estimation + */ +class IQStreamDecoder { + public: + IQStreamDecoder(std::shared_ptr bit_stream_decoder, bool is_uplink) + : bit_stream_decoder_(bit_stream_decoder) + , is_uplink_(is_uplink){}; + ~IQStreamDecoder() = default; + + void process_complex(std::complex symbol) noexcept; + + private: + std::shared_ptr bit_stream_decoder_{}; + + bool is_uplink_{}; +}; diff --git a/src/bit_stream_decoder.cpp b/src/bit_stream_decoder.cpp index 736e1c2..b7ffae1 100644 --- a/src/bit_stream_decoder.cpp +++ b/src/bit_stream_decoder.cpp @@ -7,18 +7,8 @@ * Tassilo Tanneberger */ -#include -#include -#include -#include -#include -#include -#include -#include -#include - +#include #include -#include #include #include diff --git a/src/decoder.cpp b/src/decoder.cpp index ad4e0d6..34feff7 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -9,6 +9,7 @@ #include #include +#include #include #include #include @@ -31,19 +32,12 @@ Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::op , iq_or_bit_stream_(iq_or_bit_stream) { lower_mac_ = std::make_shared(reporter_); - bit_stream_decoder_ = std::make_unique(lower_mac_, uplink_scrambling_code_.has_value()); + bit_stream_decoder_ = std::make_shared(lower_mac_, uplink_scrambling_code_.has_value()); + iq_stream_decoder_ = std::make_unique(bit_stream_decoder_, uplink_scrambling_code_.has_value()); if (uplink_scrambling_code_.has_value()) { // set scrambling_code for uplink lower_mac_->set_scrambling_code(uplink_scrambling_code_.value()); - - if (iq_or_bit_stream_) { - throw std::runtime_error("IQ Stream is not supported for uplink decoding"); - } - } else { - if (iq_or_bit_stream_) { - throw std::runtime_error("IQ Stream is not supported for downlink decoding"); - } } // read input file from file or from socket @@ -108,7 +102,15 @@ void Decoder::main_loop() { } if (iq_or_bit_stream_) { - throw std::runtime_error("IQ Stream is not supported"); + std::complex* rx_buffer_complex = reinterpret_cast*>(rx_buffer); + + assert(("Size of rx_buffer is not a multiple of std::complex", + bytes_read % sizeof(*rx_buffer_complex) == 0)); + auto size = bytes_read / sizeof(*rx_buffer_complex); + + for (auto i = 0; i < size; i++) { + iq_stream_decoder_->process_complex(rx_buffer_complex[i]); + } } else { for (auto i = 0; i < bytes_read; i++) { if (packed_) { diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp new file mode 100644 index 0000000..3f7b2da --- /dev/null +++ b/src/iq_stream_decoder.cpp @@ -0,0 +1,38 @@ +/* + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + * Tassilo Tanneberger + */ + +#include + +void IQStreamDecoder::process_complex(std::complex symbol) noexcept { + // Simple hard decission symbol mapper for now... + auto real = symbol.real(); + auto imag = symbol.imag(); + + if (real > 0.0) { + if (imag > 0.0) { + // I + bit_stream_decoder_->process_bit(0); + bit_stream_decoder_->process_bit(0); + } else { + // IV + bit_stream_decoder_->process_bit(1); + bit_stream_decoder_->process_bit(0); + } + } else { + if (imag > 0.0) { + // II + bit_stream_decoder_->process_bit(0); + bit_stream_decoder_->process_bit(1); + } else { + // III + bit_stream_decoder_->process_bit(1); + bit_stream_decoder_->process_bit(1); + } + } +} From 5e91c937eff5c7d482dc5544e6ac7b041484229a Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 3 Jul 2023 15:22:55 +0200 Subject: [PATCH 04/35] uplink sequence burst detection --- include/fixed_queue.hpp | 31 ++++++++++ include/iq_stream_decoder.hpp | 22 +++++++ src/iq_stream_decoder.cpp | 112 ++++++++++++++++++++++++++++------ 3 files changed, 145 insertions(+), 20 deletions(-) create mode 100644 include/fixed_queue.hpp diff --git a/include/fixed_queue.hpp b/include/fixed_queue.hpp new file mode 100644 index 0000000..3f30ce2 --- /dev/null +++ b/include/fixed_queue.hpp @@ -0,0 +1,31 @@ +#include +#include +#include + +template > +class FixedQueue : public std::queue { + public: + FixedQueue() { + for (auto i = 0; i < MaxLen; i++) { + T default_value{}; + std::queue::push(default_value); + } + } + + void push(const T& value) { + if (this->size() == MaxLen) { + this->c.pop_front(); + } + std::queue::push(value); + } + + void pop(const T& value) { std::logic_error("Function not implemented"); } + + typename Container::iterator begin() { return this->c.begin(); } + + typename Container::reverse_iterator rbegin() { return this->c.rbegin(); } + + typename Container::iterator end() { return this->c.end(); } + + typename Container::reverse_iterator rend() { return this->c.rend(); } +}; diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index 06e5a52..fc8ce57 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -12,6 +12,7 @@ #include #include +#include /** * Tetra downlink decoder for PI/4-DQPSK modulation @@ -28,6 +29,27 @@ class IQStreamDecoder { void process_complex(std::complex symbol) noexcept; private: + std::complex hard_decision(std::complex symbol); + + std::vector> convolve_valid(std::vector> const& a, + std::vector> const& b); + + FixedQueue, 300> symbol_buffer_; + FixedQueue, 300> symbol_buffer_hard_decision_; + + // 9.4.4.3.2 Normal training sequence + const std::vector> training_seq_n_ = {{-1, -1}, {-1, 1}, {1, 1}, {1, 1}, {-1, -1}, {1, -1}, + {1, -1}, {-1, 1}, {-1, -1}, {-1, 1}, {1, 1}}; + const std::vector> training_seq_p_ = {{-1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}, {1.0, -1.0}, + {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}, + {-1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}}; + // 9.4.4.3.3 Extended training sequence + const std::vector> training_seq_x_ = { + {1.0, -1.0}, {-1.0, 1.0}, {-1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}, + {1.0, -1.0}, {-1.0, 1.0}, {-1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}}; + + const float SEQUENCE_DETECTION_THRESHOLD = 1.5; + std::shared_ptr bit_stream_decoder_{}; bool is_uplink_{}; diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index 3f7b2da..6d8d2f5 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -9,30 +9,102 @@ #include -void IQStreamDecoder::process_complex(std::complex symbol) noexcept { - // Simple hard decission symbol mapper for now... - auto real = symbol.real(); - auto imag = symbol.imag(); - - if (real > 0.0) { - if (imag > 0.0) { - // I - bit_stream_decoder_->process_bit(0); - bit_stream_decoder_->process_bit(0); +std::complex IQStreamDecoder::hard_decision(std::complex symbol) { + if (symbol.real() > 0) { + if (symbol.imag() > 0) { + return std::complex(1, 1); + } else { + return std::complex(1, -1); + } + } else { + if (symbol.imag() > 0) { + return std::complex(-1, 1); } else { - // IV - bit_stream_decoder_->process_bit(1); - bit_stream_decoder_->process_bit(0); + return std::complex(-1, -1); + } + } +} + +std::vector> IQStreamDecoder::convolve_valid(std::vector> const& a, + std::vector> const& b) { + std::vector> res; + + // make shure a is longer equal than b + if (b.size() > a.size()) { + return convolve_valid(b, a); + } + + for (int i = 0; i < a.size() - b.size() + 1; i++) { + std::complex v{}; + auto ita = a.begin(); + std::advance(ita, i); + auto itb = b.rbegin(); + for (; itb != b.rend(); ita++, itb++) { + v += *ita * *itb; + } + res.push_back(v); + } + + return res; +} + +void IQStreamDecoder::process_complex(std::complex symbol) noexcept { + if (is_uplink_) { + // Control Uplink Burst or Normal Uplink Burst + symbol_buffer_.push(symbol); + symbol_buffer_hard_decision_.push(hard_decision(symbol)); + + // convolve hard_decision with flipped conjugate of n, p and x and check if abs > SEQUENCE_DETECTION_THRESHOLD + // to find potential correlation peaks + auto find_n = + convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_n_); + auto find_p = + convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_p_); + auto find_x = + convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_x_); + + // find CUB + // 2 tail + 42 coded symbols + middle of 15 training sequence (8) = 52 + if (std::abs(find_x[51]) > SEQUENCE_DETECTION_THRESHOLD) { + // potentially found CUB + std::cout << "Potential CUB found" << std::endl; + } + + // find NUB + // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 + if (std::abs(find_p[115]) > SEQUENCE_DETECTION_THRESHOLD) { + std::cout << "Potential NUB_Split found" << std::endl; + } + + if (std::abs(find_n[115]) > SEQUENCE_DETECTION_THRESHOLD && + std::abs(find_x[115]) <= SEQUENCE_DETECTION_THRESHOLD) { + std::cout << "Potential NUB found" << std::endl; } } else { - if (imag > 0.0) { - // II - bit_stream_decoder_->process_bit(0); - bit_stream_decoder_->process_bit(1); + // Simple hard decission symbol mapper for now... + auto real = symbol.real(); + auto imag = symbol.imag(); + + if (real > 0.0) { + if (imag > 0.0) { + // I + bit_stream_decoder_->process_bit(0); + bit_stream_decoder_->process_bit(0); + } else { + // IV + bit_stream_decoder_->process_bit(1); + bit_stream_decoder_->process_bit(0); + } } else { - // III - bit_stream_decoder_->process_bit(1); - bit_stream_decoder_->process_bit(1); + if (imag > 0.0) { + // II + bit_stream_decoder_->process_bit(0); + bit_stream_decoder_->process_bit(1); + } else { + // III + bit_stream_decoder_->process_bit(1); + bit_stream_decoder_->process_bit(1); + } } } } From fe70e99133f2b6d238dfb11671de30bb8c9a1d39 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 4 Jul 2023 00:15:45 +0200 Subject: [PATCH 05/35] implement first version of synchronization of bursts. very unperformant, but a start --- include/fixed_queue.hpp | 8 +- include/iq_stream_decoder.hpp | 14 +++- src/decoder.cpp | 3 +- src/iq_stream_decoder.cpp | 146 ++++++++++++++++++++++++---------- 4 files changed, 122 insertions(+), 49 deletions(-) diff --git a/include/fixed_queue.hpp b/include/fixed_queue.hpp index 3f30ce2..9557f15 100644 --- a/include/fixed_queue.hpp +++ b/include/fixed_queue.hpp @@ -21,11 +21,11 @@ class FixedQueue : public std::queue { void pop(const T& value) { std::logic_error("Function not implemented"); } - typename Container::iterator begin() { return this->c.begin(); } + typename Container::const_iterator cbegin() { return this->c.cbegin(); } - typename Container::reverse_iterator rbegin() { return this->c.rbegin(); } + typename Container::const_reverse_iterator crbegin() { return this->c.crbegin(); } - typename Container::iterator end() { return this->c.end(); } + typename Container::const_iterator cend() { return this->c.cend(); } - typename Container::reverse_iterator rend() { return this->c.rend(); } + typename Container::const_reverse_iterator crend() { return this->c.crend(); } }; diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index fc8ce57..b018cca 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -13,6 +13,7 @@ #include #include +#include /** * Tetra downlink decoder for PI/4-DQPSK modulation @@ -21,35 +22,42 @@ */ class IQStreamDecoder { public: - IQStreamDecoder(std::shared_ptr bit_stream_decoder, bool is_uplink) - : bit_stream_decoder_(bit_stream_decoder) - , is_uplink_(is_uplink){}; + IQStreamDecoder(std::shared_ptr lower_mac, std::shared_ptr bit_stream_decoder, + bool is_uplink); ~IQStreamDecoder() = default; void process_complex(std::complex symbol) noexcept; private: std::complex hard_decision(std::complex symbol); + std::vector symbols_to_bitstream(std::vector> const& stream); std::vector> convolve_valid(std::vector> const& a, std::vector> const& b); + std::vector> channel_estimation(std::vector> const& stream, + std::vector> const& pilots); + FixedQueue, 300> symbol_buffer_; FixedQueue, 300> symbol_buffer_hard_decision_; // 9.4.4.3.2 Normal training sequence const std::vector> training_seq_n_ = {{-1, -1}, {-1, 1}, {1, 1}, {1, 1}, {-1, -1}, {1, -1}, {1, -1}, {-1, 1}, {-1, -1}, {-1, 1}, {1, 1}}; + std::vector> training_seq_n_reversed_conj_{}; const std::vector> training_seq_p_ = {{-1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}, {1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}, {-1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}}; + std::vector> training_seq_p_reversed_conj_{}; // 9.4.4.3.3 Extended training sequence const std::vector> training_seq_x_ = { {1.0, -1.0}, {-1.0, 1.0}, {-1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}, {1.0, -1.0}, {1.0, -1.0}, {-1.0, 1.0}, {-1.0, -1.0}, {-1.0, 1.0}, {1.0, 1.0}, {1.0, 1.0}, {-1.0, -1.0}}; + std::vector> training_seq_x_reversed_conj_{}; const float SEQUENCE_DETECTION_THRESHOLD = 1.5; + std::shared_ptr lower_mac_{}; std::shared_ptr bit_stream_decoder_{}; bool is_uplink_{}; diff --git a/src/decoder.cpp b/src/decoder.cpp index 34feff7..fd64699 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -33,7 +33,8 @@ Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::op lower_mac_ = std::make_shared(reporter_); bit_stream_decoder_ = std::make_shared(lower_mac_, uplink_scrambling_code_.has_value()); - iq_stream_decoder_ = std::make_unique(bit_stream_decoder_, uplink_scrambling_code_.has_value()); + iq_stream_decoder_ = + std::make_unique(lower_mac_, bit_stream_decoder_, uplink_scrambling_code_.has_value()); if (uplink_scrambling_code_.has_value()) { // set scrambling_code for uplink diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index 6d8d2f5..b2ad306 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -7,8 +7,23 @@ * Tassilo Tanneberger */ +#include + #include +IQStreamDecoder::IQStreamDecoder(std::shared_ptr lower_mac, + std::shared_ptr bit_stream_decoder, bool is_uplink) + : lower_mac_(lower_mac) + , bit_stream_decoder_(bit_stream_decoder) + , is_uplink_(is_uplink) { + std::transform(training_seq_n_.crbegin(), training_seq_n_.crend(), + std::back_inserter(training_seq_n_reversed_conj_), [](auto v) { return std::conj(v); }); + std::transform(training_seq_p_.crbegin(), training_seq_p_.crend(), + std::back_inserter(training_seq_p_reversed_conj_), [](auto v) { return std::conj(v); }); + std::transform(training_seq_x_.crbegin(), training_seq_x_.crend(), + std::back_inserter(training_seq_x_reversed_conj_), [](auto v) { return std::conj(v); }); +} + std::complex IQStreamDecoder::hard_decision(std::complex symbol) { if (symbol.real() > 0) { if (symbol.imag() > 0) { @@ -25,6 +40,40 @@ std::complex IQStreamDecoder::hard_decision(std::complex symbol) { } } +std::vector IQStreamDecoder::symbols_to_bitstream(std::vector> const& stream) { + std::vector bits; + + for (auto it = stream.begin(); it != stream.end(); it++) { + + auto real = it->real(); + auto imag = it->imag(); + + if (real > 0.0) { + if (imag > 0.0) { + // I + bits.push_back(0); + bits.push_back(0); + } else { + // IV + bits.push_back(1); + bits.push_back(0); + } + } else { + if (imag > 0.0) { + // II + bits.push_back(0); + bits.push_back(1); + } else { + // III + bits.push_back(1); + bits.push_back(1); + } + } + } + + return bits; +} + std::vector> IQStreamDecoder::convolve_valid(std::vector> const& a, std::vector> const& b) { std::vector> res; @@ -48,6 +97,12 @@ std::vector> IQStreamDecoder::convolve_valid(std::vector> IQStreamDecoder::channel_estimation(std::vector> const& stream, + std::vector> const& pilots) { + // TODO: implement channel estimation + return stream; +} + void IQStreamDecoder::process_complex(std::complex symbol) noexcept { if (is_uplink_) { // Control Uplink Burst or Normal Uplink Burst @@ -56,55 +111,64 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { // convolve hard_decision with flipped conjugate of n, p and x and check if abs > SEQUENCE_DETECTION_THRESHOLD // to find potential correlation peaks - auto find_n = - convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_n_); - auto find_p = - convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_p_); - auto find_x = - convolve_valid({symbol_buffer_hard_decision_.begin(), symbol_buffer_hard_decision_.end()}, training_seq_x_); - + // + // find NUB + // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 + auto start_n = symbol_buffer_hard_decision_.cbegin(); + std::advance(start_n, 109); + auto end_n = start_n; + std::advance(end_n, 11); + auto find_n = convolve_valid({start_n, end_n}, training_seq_n_reversed_conj_)[0]; + // find NUB_Split + // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 + auto start_p = symbol_buffer_hard_decision_.cbegin(); + std::advance(start_p, 109); + auto end_p = start_p; + std::advance(end_p, 11); + auto find_p = convolve_valid({start_p, end_p}, training_seq_p_reversed_conj_)[0]; // find CUB // 2 tail + 42 coded symbols + middle of 15 training sequence (8) = 52 - if (std::abs(find_x[51]) > SEQUENCE_DETECTION_THRESHOLD) { - // potentially found CUB - std::cout << "Potential CUB found" << std::endl; + auto start_x = symbol_buffer_hard_decision_.cbegin(); + std::advance(start_x, 44); + auto end_x = start_x; + std::advance(end_x, 15); + auto find_x = convolve_valid({start_x, end_x}, training_seq_x_reversed_conj_)[0]; + + auto start = symbol_buffer_.cbegin(); + + if (std::abs(find_x) >= SEQUENCE_DETECTION_THRESHOLD) { + // std::cout << "Potential CUB found" << std::endl; + + auto end = start; + std::advance(end, 103); + + auto corrected = channel_estimation({start, end}, training_seq_x_reversed_conj_); + lower_mac_->process(symbols_to_bitstream(corrected), BurstType::ControlUplinkBurst); } - // find NUB - // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 - if (std::abs(find_p[115]) > SEQUENCE_DETECTION_THRESHOLD) { - std::cout << "Potential NUB_Split found" << std::endl; + if (std::abs(find_p) >= SEQUENCE_DETECTION_THRESHOLD) { + // std::cout << "Potential NUB_Split found" << std::endl; + + auto end = start; + std::advance(end, 231); + + auto corrected = channel_estimation({start, end}, training_seq_p_reversed_conj_); + lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurstSplit); } - if (std::abs(find_n[115]) > SEQUENCE_DETECTION_THRESHOLD && - std::abs(find_x[115]) <= SEQUENCE_DETECTION_THRESHOLD) { - std::cout << "Potential NUB found" << std::endl; + if (std::abs(find_n) >= SEQUENCE_DETECTION_THRESHOLD) { + // std::cout << "Potential NUB found" << std::endl; + + auto end = start; + std::advance(end, 231); + + auto corrected = channel_estimation({start, end}, training_seq_n_reversed_conj_); + lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurst); } } else { - // Simple hard decission symbol mapper for now... - auto real = symbol.real(); - auto imag = symbol.imag(); - - if (real > 0.0) { - if (imag > 0.0) { - // I - bit_stream_decoder_->process_bit(0); - bit_stream_decoder_->process_bit(0); - } else { - // IV - bit_stream_decoder_->process_bit(1); - bit_stream_decoder_->process_bit(0); - } - } else { - if (imag > 0.0) { - // II - bit_stream_decoder_->process_bit(0); - bit_stream_decoder_->process_bit(1); - } else { - // III - bit_stream_decoder_->process_bit(1); - bit_stream_decoder_->process_bit(1); - } + auto bits = symbols_to_bitstream({symbol}); + for (auto it = bits.begin(); it != bits.end(); ++it) { + bit_stream_decoder_->process_bit(*it); } } } From a99c4631baee54133f55a4df310b5f8c6411bb35 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 4 Jul 2023 20:10:24 +0200 Subject: [PATCH 06/35] add parsing stub for supplementary llc pdu --- include/l2/logical_link_control.hpp | 1 + src/l2/logical_link_control.cpp | 25 +++++++++++++++++++++++++ 2 files changed, 26 insertions(+) diff --git a/include/l2/logical_link_control.hpp b/include/l2/logical_link_control.hpp index 60a0f4a..f4f040e 100644 --- a/include/l2/logical_link_control.hpp +++ b/include/l2/logical_link_control.hpp @@ -35,6 +35,7 @@ class LogicalLinkControl { void process_bl_adata_without_fcs(const AddressType address, BitVector& vec); // Basic link (acknowledged service in connectionless mode) without Frame Check Sequence void process_bl_data_without_fcs(const AddressType address, BitVector& vec); + void process_supplementary_llc_pdu(const AddressType address, BitVector& vec); std::shared_ptr reporter_{}; std::shared_ptr mle_{}; diff --git a/src/l2/logical_link_control.cpp b/src/l2/logical_link_control.cpp index 611b6fb..223f0a6 100644 --- a/src/l2/logical_link_control.cpp +++ b/src/l2/logical_link_control.cpp @@ -40,6 +40,8 @@ void LogicalLinkControl::process(const AddressType address, BitVector& vec) { // BL-UDATA without FCS mle_->service_user_pdu(address, vec); break; + case 0b1101: + process_supplementary_llc_pdu(address, vec); default: break; } @@ -62,4 +64,27 @@ void LogicalLinkControl::process_bl_data_without_fcs(const AddressType address, mle_->service_user_pdu(address, vec); } +void LogicalLinkControl::process_supplementary_llc_pdu(const AddressType address, BitVector& vec) { + std::string supplementary_llc_pdu[] = {"AL-X-DATA/AL-X-DATA-AR/AL-X-FINAL/AL-X-FINAL-AR", "AL-X-UDATA/AL-X-UFINAL", + "AL-X-UDATA/AL-X-UFINAL", "AL-X-ACK/AL-X-RNR", "Reserved"}; + + auto pdu_type = vec.take(2); + + std::cout << " " << supplementary_llc_pdu[pdu_type] << std::endl; + std::cout << " Data: " << vec << std::endl; + + switch (pdu_type) { + case 0b00: + break; + case 0b01: + break; + case 0b10: + break; + case 0b11: + break; + default: + break; + } +} + auto operator<<(std::ostream& stream, const LogicalLinkControl& llc) -> std::ostream& { return stream; } From 109e26c843c24ce58e1a1d9fee2948b7f5fda65b Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 4 Jul 2023 20:13:26 +0200 Subject: [PATCH 07/35] fix upper mac parsing logic. add lower mac parsing for Normal Uplink Burst Split --- src/l2/lower_mac.cpp | 20 ++++++++++++-------- src/l2/upper_mac.cpp | 12 +++++++++--- 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 9a4f98c..2579fe0 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -200,7 +200,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) upper_mac_->process_SCH_HU(burst_type, cb); return true; } else { - fmt::print("CUB Burst crc failed\n"); + // fmt::print("CUB Burst crc failed\n"); return false; } } else if (burst_type == BurstType::NormalUplinkBurst) { @@ -217,7 +217,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) fmt::print("NUB Burst crc good\n"); upper_mac_->process_SCH_F(burst_type, bkn1); } else { - // fmt::print("NUB Burst crc failed\n"); + // fmt::print("NUB Burst crc failed\n"); } } else if (burst_type == BurstType::NormalUplinkBurstSplit) { // TODO: finish NormalUplinkBurstSplit implementation @@ -232,14 +232,18 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) if (check_crc_16_ccitt(bkn1, 140)) { bkn1 = vectorExtract(bkn1, 0, 124); fmt::print("NUB_S 1 Burst crc good\n"); + upper_mac_->process_STCH(burst_type, bkn1); } - bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = depuncture23(bkn2, 216); - bkn2 = viter_bi_decode_1614(bkn2); - if (check_crc_16_ccitt(bkn2, 140)) { - bkn2 = vectorExtract(bkn2, 0, 124); - fmt::print("NUB_S 2 Burst crc good\n"); + if (upper_mac_->second_slot_stolen()) { + bkn2 = deinterleave(bkn2, 216, 101); + bkn2 = depuncture23(bkn2, 216); + bkn2 = viter_bi_decode_1614(bkn2); + if (check_crc_16_ccitt(bkn2, 140)) { + bkn2 = vectorExtract(bkn2, 0, 124); + fmt::print("NUB_S 2 Burst crc good\n"); + upper_mac_->process_STCH(burst_type, bkn2); + } } } else { throw std::runtime_error("LowerMac does not implement the burst type supplied"); diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 3566213..403087f 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -206,7 +206,9 @@ void UpperMac::process_signalling_channel(const BurstType burst_type, BitVector& // Broadcast // TMB-SAP // ✅ done - assert(burst_type.is_downlink_burst()); + if (burst_type.is_uplink_burst()) { + throw std::runtime_error("Uplink Burst Type may not send broadcast packets"); + } process_broadcast(vec); } else if (pduType == 0b11) { // Supplementary MAC PDU (not on STCH, SCH/HD or SCH-P8/HD) @@ -388,6 +390,7 @@ void UpperMac::process_mac_usignal(BitVector& vec) { // TODO: TM-SDU auto tm_sdu = BitVector(vec.take_vector(vec.bits_left())); std::cout << "MAC U-SIGNAL" << std::endl; + std::cout << " Second subslot is stolen: " << (second_slot_stolen_ ? "true" : "false") << std::endl; std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; } @@ -577,9 +580,10 @@ void UpperMac::process_mac_resource(BitVector& vec) { auto encryption_mode = vec.take(2); auto random_access_flag = vec.take(1); auto length_indictaion = vec.take(6); - if (length_indictaion == 0b111110) { + if (length_indictaion == 0b111110 || length_indictaion == 0b111111) { second_slot_stolen_ = true; } + std::cout << " Second subslot is stolen: " << (second_slot_stolen_ ? "true" : "false") << std::endl; std::cout << " length_indictaion: 0b" << std::bitset<6>(length_indictaion) << std::endl; auto address_type = vec.take(3); auto address = AddressType(); @@ -707,7 +711,7 @@ void UpperMac::process_mac_resource(BitVector& vec) { std::cout << " fill_bit_indication: 0b" << std::bitset<1>(fill_bit_indication) << std::endl; std::cout << " Address: " << address << std::endl; - if (!(length_indictaion == 0b111111 || length_indictaion == 0b111110)) { + if (length_indictaion != 0b111111) { // no fragmentation logical_link_control_->process(address, tm_sdu); } else { @@ -747,7 +751,9 @@ void UpperMac::process_mac_data(BitVector& vec) { std::cout << " Second half slot stolen on STCH" << std::endl; second_slot_stolen_ = true; } else if (length_indication == 0b111111) { + std::cout << " Second half slot stolen on STCH" << std::endl; std::cout << " Start of fragmentation on STCH" << std::endl; + second_slot_stolen_ = true; fragmentation = true; } } else { From ce6949a1a5f56ce3b316dfb3262539ee9fe16646 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 4 Jul 2023 23:22:21 +0200 Subject: [PATCH 08/35] use std::function to abstract and delay processing of upper mac in uplink --- include/l2/lower_mac.hpp | 10 +++++-- src/bit_stream_decoder.cpp | 17 ++++++++++-- src/iq_stream_decoder.cpp | 15 ++++++++-- src/l2/lower_mac.cpp | 56 +++++++++++++++++++++++--------------- 4 files changed, 68 insertions(+), 30 deletions(-) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index 28cd4ca..3e66092 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -22,15 +22,21 @@ class LowerMac { public: LowerMac(std::shared_ptr reporter); + LowerMac(const LowerMac& lowerMac); ~LowerMac() = default; - auto process(const std::vector& frame, BurstType burst_type) -> bool; + // does the signal processing and then returns a list of function that need to be executed for data to be passed to + // upper mac sequentially. + // TODO: this is currently only done for uplink bursts + // Downlink burst get processed and passed to upper mac directly + [[nodiscard]] auto process(const std::vector& frame, BurstType burst_type) + -> std::vector>; void set_scrambling_code(unsigned int scrambling_code) { upper_mac_->set_scrambling_code(scrambling_code); }; private: std::shared_ptr reporter_{}; std::unique_ptr viter_bi_codec_1614_{}; - std::unique_ptr upper_mac_{}; + std::shared_ptr upper_mac_{}; [[nodiscard]] static auto descramble(const std::vector& data, int len, uint32_t scramblingCode) noexcept -> std::vector; diff --git a/src/bit_stream_decoder.cpp b/src/bit_stream_decoder.cpp index b7ffae1..db8fd32 100644 --- a/src/bit_stream_decoder.cpp +++ b/src/bit_stream_decoder.cpp @@ -87,14 +87,22 @@ void BitStreamDecoder::process_bit(uint8_t symbol) noexcept { if (score_ssn <= 4) { // fmt::print("Processing burst type: {}\n", ControlUplinkBurst); - if (lower_mac_->process(frame_, burstType)) + auto funcs = lower_mac_->process(frame_, burstType); + for (auto func : funcs) { + func(); + } + + if (funcs.size() > 0) std::vector(frame_.begin() + 200, frame_.end()).swap(frame_); else frame_.erase(frame_.begin()); } else if (minimum_score <= 2) { // valid burst found, send it to lower MAC // fmt::print("Processing burst type: {}\n", burstType); - lower_mac_->process(frame_, burstType); + auto funcs = lower_mac_->process(frame_, burstType); + for (auto func : funcs) { + func(); + } frame_.erase(frame_.begin()); // std::vector(frame_.begin()+462, frame_.end()).swap(frame_); @@ -130,7 +138,10 @@ void BitStreamDecoder::process_downlink_frame() noexcept { if (minimum_score <= 5) { // valid burst found, send it to lower MAC fmt::print("Processing burst type: {}\n", burstType); - lower_mac_->process(frame_, burstType); + auto funcs = lower_mac_->process(frame_, burstType); + for (auto func : funcs) { + func(); + } } } diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index b2ad306..1d3a042 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -143,7 +143,10 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { std::advance(end, 103); auto corrected = channel_estimation({start, end}, training_seq_x_reversed_conj_); - lower_mac_->process(symbols_to_bitstream(corrected), BurstType::ControlUplinkBurst); + auto funcs = lower_mac_->process(symbols_to_bitstream(corrected), BurstType::ControlUplinkBurst); + for (auto func : funcs) { + func(); + } } if (std::abs(find_p) >= SEQUENCE_DETECTION_THRESHOLD) { @@ -153,7 +156,10 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { std::advance(end, 231); auto corrected = channel_estimation({start, end}, training_seq_p_reversed_conj_); - lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurstSplit); + auto funcs = lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurstSplit); + for (auto func : funcs) { + func(); + } } if (std::abs(find_n) >= SEQUENCE_DETECTION_THRESHOLD) { @@ -163,7 +169,10 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { std::advance(end, 231); auto corrected = channel_estimation({start, end}, training_seq_n_reversed_conj_); - lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurst); + auto funcs = lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurst); + for (auto func : funcs) { + func(); + } } } else { auto bits = symbols_to_bitstream({symbol}); diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 2579fe0..69950d9 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -29,7 +29,21 @@ LowerMac::LowerMac(std::shared_ptr reporter) polynomials.push_back(0b11011); viter_bi_codec_1614_ = std::make_unique(constraint, polynomials); - upper_mac_ = std::make_unique(reporter_); + upper_mac_ = std::make_shared(reporter_); +} + +LowerMac::LowerMac(const LowerMac& lowerMac) { + reporter_ = lowerMac.reporter_; + upper_mac_ = lowerMac.upper_mac_; + + std::vector polynomials; + int constraint = 6; + + polynomials.push_back(0b10011); + polynomials.push_back(0b11101); + polynomials.push_back(0b10111); + polynomials.push_back(0b11011); + viter_bi_codec_1614_ = std::make_unique(constraint, polynomials); } static auto vectorExtract(const std::vector& vec, size_t pos, size_t length) -> std::vector { @@ -47,13 +61,15 @@ static auto vectorAppend(const std::vector& vec, std::vector& return res; } -auto LowerMac::process(const std::vector& frame, BurstType burst_type) -> bool { +auto LowerMac::process(const std::vector& frame, BurstType burst_type) -> std::vector> { std::vector sb; std::vector bkn1; std::vector bkn2; std::vector bb; std::vector cb; + std::vector> functions{}; + // The BLCH may be mapped onto block 2 of the downlink slots, when a SCH/HD, // SCH-P8/HD or a BSCH is mapped onto block 1. The number of BLCH occurrences // on one carrier shall not exceed one per 4 multiframe periods. @@ -192,16 +208,14 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) cb = vectorExtract(frame, 4, 84); cb = vectorAppend(frame, cb, 118, 84); cb = descramble(cb, 168, upper_mac_->scrambling_code()); + + // XXX: assume to be control channel cb = deinterleave(cb, 168, 13); cb = depuncture23(cb, 168); cb = viter_bi_decode_1614(cb); if (check_crc_16_ccitt(cb, 108)) { cb = vectorExtract(cb, 0, 92); - upper_mac_->process_SCH_HU(burst_type, cb); - return true; - } else { - // fmt::print("CUB Burst crc failed\n"); - return false; + functions.push_back(std::bind(&UpperMac::process_SCH_HU, upper_mac_, burst_type, cb)); } } else if (burst_type == BurstType::NormalUplinkBurst) { bkn1 = vectorExtract(frame, 4, 216); @@ -214,10 +228,8 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) bkn1 = viter_bi_decode_1614(bkn1); if (check_crc_16_ccitt(bkn1, 284)) { bkn1 = vectorExtract(bkn1, 0, 268); - fmt::print("NUB Burst crc good\n"); - upper_mac_->process_SCH_F(burst_type, bkn1); - } else { - // fmt::print("NUB Burst crc failed\n"); + // fmt::print("NUB Burst crc good\n"); + functions.push_back(std::bind(&UpperMac::process_SCH_F, upper_mac_, burst_type, bkn1)); } } else if (burst_type == BurstType::NormalUplinkBurstSplit) { // TODO: finish NormalUplinkBurstSplit implementation @@ -231,23 +243,23 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) bkn1 = viter_bi_decode_1614(bkn1); if (check_crc_16_ccitt(bkn1, 140)) { bkn1 = vectorExtract(bkn1, 0, 124); - fmt::print("NUB_S 1 Burst crc good\n"); - upper_mac_->process_STCH(burst_type, bkn1); + // fmt::print("NUB_S 1 Burst crc good\n"); + functions.push_back(std::bind(&UpperMac::process_STCH, upper_mac_, burst_type, bkn1)); } - if (upper_mac_->second_slot_stolen()) { - bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = depuncture23(bkn2, 216); - bkn2 = viter_bi_decode_1614(bkn2); - if (check_crc_16_ccitt(bkn2, 140)) { - bkn2 = vectorExtract(bkn2, 0, 124); - fmt::print("NUB_S 2 Burst crc good\n"); - upper_mac_->process_STCH(burst_type, bkn2); + bkn2 = deinterleave(bkn2, 216, 101); + bkn2 = depuncture23(bkn2, 216); + bkn2 = viter_bi_decode_1614(bkn2); + if (check_crc_16_ccitt(bkn2, 140)) { + bkn2 = vectorExtract(bkn2, 0, 124); + if (upper_mac_->second_slot_stolen()) { + // fmt::print("NUB_S 2 Burst crc good\n"); + functions.push_back(std::bind(&UpperMac::process_STCH, upper_mac_, burst_type, bkn2)); } } } else { throw std::runtime_error("LowerMac does not implement the burst type supplied"); } - return true; + return functions; } From c64bdfdb9c74b671949ec4f8a59c7185564fa628 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 5 Jul 2023 12:59:19 +0200 Subject: [PATCH 09/35] implement StreamingOrderedOutputThreadPoolExecutor --- CMakeLists.txt | 1 + include/iq_stream_decoder.hpp | 9 ++ ...ng_ordered_output_thread_pool_executor.hpp | 56 ++++++++++ src/iq_stream_decoder.cpp | 30 +++--- ...ng_ordered_output_thread_pool_executor.cpp | 100 ++++++++++++++++++ 5 files changed, 184 insertions(+), 12 deletions(-) create mode 100644 include/streaming_ordered_output_thread_pool_executor.hpp create mode 100644 src/streaming_ordered_output_thread_pool_executor.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e6771ef..8b4e9df 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,6 +8,7 @@ add_executable(tetra src/main.cpp src/reporter.cpp src/decoder.cpp + src/streaming_ordered_output_thread_pool_executor.cpp src/bit_stream_decoder.cpp src/iq_stream_decoder.cpp src/l2/lower_mac.cpp diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index b018cca..352c55d 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -10,10 +10,13 @@ #pragma once #include +#include +#include #include #include #include +#include /** * Tetra downlink decoder for PI/4-DQPSK modulation @@ -29,6 +32,8 @@ class IQStreamDecoder { void process_complex(std::complex symbol) noexcept; private: + void upperMacWorker(); + std::complex hard_decision(std::complex symbol); std::vector symbols_to_bitstream(std::vector> const& stream); @@ -61,4 +66,8 @@ class IQStreamDecoder { std::shared_ptr bit_stream_decoder_{}; bool is_uplink_{}; + + std::shared_ptr>>> threadPool_; + + std::thread upperMacWorkerThread_; }; diff --git a/include/streaming_ordered_output_thread_pool_executor.hpp b/include/streaming_ordered_output_thread_pool_executor.hpp new file mode 100644 index 0000000..80db301 --- /dev/null +++ b/include/streaming_ordered_output_thread_pool_executor.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + * Tassilo Tanneberger + */ + +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +// thread pool executing work but outputting it the order of the input +template class StreamingOrderedOutputThreadPoolExecutor { + + public: + StreamingOrderedOutputThreadPoolExecutor(int numWorkers); + ~StreamingOrderedOutputThreadPoolExecutor() = default; + + // append work to the queue + void queueWork(std::function work); + + // wait and get a finished item + ReturnType get(); + + private: + void worker(); + + // locks for input to worker threads + std::condition_variable cv_input_item; + std::mutex cv_input_item_m; + + // locks for output (get) + std::condition_variable cv_output_item; + std::mutex cv_output_item_m; + + // queue of work with and incrementing index + std::deque>> inputQueue{}; + // output queue. this is a map so we can do a lookup on the current index for ordered output + std::map outputMap{}; + + // contains the value of the next input item + uint64_t inputCounter = 0; + // contains the index of the next output item + uint64_t outputCounter = 0; + + std::vector workers; +}; diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index 1d3a042..b14262a 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -22,6 +22,18 @@ IQStreamDecoder::IQStreamDecoder(std::shared_ptr lower_mac, std::back_inserter(training_seq_p_reversed_conj_), [](auto v) { return std::conj(v); }); std::transform(training_seq_x_.crbegin(), training_seq_x_.crend(), std::back_inserter(training_seq_x_reversed_conj_), [](auto v) { return std::conj(v); }); + + threadPool_ = std::make_shared>>>(16); + + upperMacWorkerThread_ = std::thread(&IQStreamDecoder::upperMacWorker, this); +} + +void IQStreamDecoder::upperMacWorker() { + while (true) { + for (auto func : threadPool_->get()) { + func(); + } + } } std::complex IQStreamDecoder::hard_decision(std::complex symbol) { @@ -143,10 +155,8 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { std::advance(end, 103); auto corrected = channel_estimation({start, end}, training_seq_x_reversed_conj_); - auto funcs = lower_mac_->process(symbols_to_bitstream(corrected), BurstType::ControlUplinkBurst); - for (auto func : funcs) { - func(); - } + auto bitstream = symbols_to_bitstream(corrected); + threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bitstream, BurstType::ControlUplinkBurst)); } if (std::abs(find_p) >= SEQUENCE_DETECTION_THRESHOLD) { @@ -156,10 +166,8 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { std::advance(end, 231); auto corrected = channel_estimation({start, end}, training_seq_p_reversed_conj_); - auto funcs = lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurstSplit); - for (auto func : funcs) { - func(); - } + threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, symbols_to_bitstream(corrected), + BurstType::NormalUplinkBurstSplit)); } if (std::abs(find_n) >= SEQUENCE_DETECTION_THRESHOLD) { @@ -169,10 +177,8 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { std::advance(end, 231); auto corrected = channel_estimation({start, end}, training_seq_n_reversed_conj_); - auto funcs = lower_mac_->process(symbols_to_bitstream(corrected), BurstType::NormalUplinkBurst); - for (auto func : funcs) { - func(); - } + threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, symbols_to_bitstream(corrected), + BurstType::NormalUplinkBurst)); } } else { auto bits = symbols_to_bitstream({symbol}); diff --git a/src/streaming_ordered_output_thread_pool_executor.cpp b/src/streaming_ordered_output_thread_pool_executor.cpp new file mode 100644 index 0000000..e094966 --- /dev/null +++ b/src/streaming_ordered_output_thread_pool_executor.cpp @@ -0,0 +1,100 @@ +/* + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + * Tassilo Tanneberger + */ + +#include + +#include + +template +StreamingOrderedOutputThreadPoolExecutor::StreamingOrderedOutputThreadPoolExecutor(int numWorkers) { + for (auto i = 0; i < numWorkers; i++) { + std::thread t(&StreamingOrderedOutputThreadPoolExecutor::worker, this); + workers.push_back(std::move(t)); + } +} + +template void StreamingOrderedOutputThreadPoolExecutor::worker() { + while (true) { + std::optional>> work{}; + + { + std::lock_guard lk(cv_input_item_m); + if (!inputQueue.empty()) { + work = inputQueue.front(); + inputQueue.pop_front(); + } + } + + if (!work.has_value()) { + std::unique_lock lk(cv_input_item_m); + cv_input_item.wait(lk, [&] { + if (!inputQueue.empty()) { + work = inputQueue.front(); + inputQueue.pop_front(); + return true; + } + + return false; + }); + } + + if (work.has_value()) { + auto index = work->first; + + // do the work + auto result = work->second(); + + { + std::lock_guard lock(cv_output_item_m); + outputMap[index] = result; + } + cv_output_item.notify_all(); + } + } +} + +template +void StreamingOrderedOutputThreadPoolExecutor::queueWork(std::function work) { + { + std::lock_guard lock(cv_input_item_m); + inputQueue.push_back(std::make_pair(inputCounter++, work)); + } + cv_input_item.notify_one(); +} + +template ReturnType StreamingOrderedOutputThreadPoolExecutor::get() { + std::optional result{}; + + while (!result.has_value()) { + { + std::lock_guard lk(cv_output_item_m); + if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { + result = search->second; + outputCounter++; + break; + } + } + + std::unique_lock lk(cv_output_item_m); + cv_output_item.wait(lk, [&] { + // find the output item and if found set outputCounter_ to the next item + if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { + result = search->second; + outputCounter++; + return true; + } + + return false; + }); + } + + return *result; +} + +template class StreamingOrderedOutputThreadPoolExecutor>>; From 00a8bd6533e489a10f4527743a57e70eed0aef39 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 5 Jul 2023 14:42:41 +0200 Subject: [PATCH 10/35] remove duplicate code --- ...ng_ordered_output_thread_pool_executor.cpp | 36 +++++++++---------- 1 file changed, 17 insertions(+), 19 deletions(-) diff --git a/src/streaming_ordered_output_thread_pool_executor.cpp b/src/streaming_ordered_output_thread_pool_executor.cpp index e094966..160fdba 100644 --- a/src/streaming_ordered_output_thread_pool_executor.cpp +++ b/src/streaming_ordered_output_thread_pool_executor.cpp @@ -71,28 +71,26 @@ void StreamingOrderedOutputThreadPoolExecutor::queueWork(std::functi template ReturnType StreamingOrderedOutputThreadPoolExecutor::get() { std::optional result{}; - while (!result.has_value()) { - { - std::lock_guard lk(cv_output_item_m); - if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { - result = search->second; - outputCounter++; - break; - } + { + std::lock_guard lk(cv_output_item_m); + if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { + result = search->second; + outputCounter++; + return *result; } + } - std::unique_lock lk(cv_output_item_m); - cv_output_item.wait(lk, [&] { - // find the output item and if found set outputCounter_ to the next item - if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { - result = search->second; - outputCounter++; - return true; - } + std::unique_lock lk(cv_output_item_m); + cv_output_item.wait(lk, [&] { + // find the output item and if found set outputCounter_ to the next item + if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { + result = search->second; + outputCounter++; + return true; + } - return false; - }); - } + return false; + }); return *result; } From 63cc74e35e2971417fe58baf15601c4327667dc3 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 9 Jul 2023 02:37:43 +0200 Subject: [PATCH 11/35] add example for all different tetra RCPC punctering schemes --- CMakeLists.txt | 5 +++ src/examples/tetra_puncturing.cpp | 51 +++++++++++++++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 src/examples/tetra_puncturing.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 8b4e9df..d586d3b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -24,7 +24,11 @@ add_executable(tetra src/utils/bit_vector.cpp src/utils/viter_bi_codec.cpp) +add_executable(tetra-puncturing + src/examples/tetra_puncturing.cpp) + target_compile_options(tetra PUBLIC -std=c++17 -Wall -Wno-unused-variable) +target_compile_options(tetra-puncturing PUBLIC -std=c++17 -Wall -Wno-unused-variable) include_directories(src) @@ -38,3 +42,4 @@ include_directories(${CMAKE_SOURCE_DIR}/include) target_link_libraries(tetra ZLIB::ZLIB fmt::fmt nlohmann_json::nlohmann_json) install(TARGETS tetra DESTINATION bin) +install(TARGETS tetra-puncturing DESTINATION bin) diff --git a/src/examples/tetra_puncturing.cpp b/src/examples/tetra_puncturing.cpp new file mode 100644 index 0000000..33b6f77 --- /dev/null +++ b/src/examples/tetra_puncturing.cpp @@ -0,0 +1,51 @@ +#include +#include +#include + +auto puncture(std::size_t num_bits, std::vector P, std::function i_from_j) -> std::vector { + auto t = P.size() - 1; + + auto period = 8; + + std::vector res(num_bits * 4, 0); + + for (auto j = 1; j <= res.size(); j++) { + auto i = i_from_j(j); + auto k = period * ((i - 1) / t) + P[i - t * ((i - 1) / t)]; + if (k - 1 < res.size()) + res[k - 1] = j; + } + + return res; +} + +auto print(std::vector const& res) -> void { + for (auto i = 0; i < res.size();) { + auto j = i; + std::cout << " "; + for (; j < i + 16; j++) { + std::cout << res[j] << " "; + } + std::cout << std::endl; + i = j; + } +} + +auto main(int argc, char** argv) -> int { + auto rate_2_3 = puncture(4, {0, 1, 2, 5}, [](int j) { return j; }); + auto rate_1_3 = puncture(4, {0, 1, 2, 3, 5, 6, 7}, [](int j) { return j; }); + auto rate_292_432 = puncture(11 * 4, {0, 1, 2, 5}, [](int j) { return j + (j - 1) / 65; }); + auto rate_148_432 = puncture(3 * 4, {0, 1, 2, 3, 5, 6, 7}, [](int j) { return j + (j - 1) / 35; }); + + std::cout << "Rate 2/3:" << std::endl; + print(rate_2_3); + + std::cout << "Rate 1/3:" << std::endl; + print(rate_1_3); + + std::cout << "Rate 292/432:" << std::endl; + print(rate_292_432); + + std::cout << "Rate 148/432:" << std::endl; + print(rate_148_432); +} From 3853c6bc6ade863a31f93a3adb4b3beeff7b1963 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 9 Jul 2023 11:25:51 +0200 Subject: [PATCH 12/35] Use different viterbi decoder --- .gitmodules | 3 + CMakeLists.txt | 4 +- include/l2/lower_mac.hpp | 4 +- include/utils/viter_bi_codec.hpp | 117 +++++------------- lib/ViterbiDecoderCpp | 1 + src/examples/tetra_viterbi.cpp | 174 ++++++++++++++++++++++++++ src/l2/lower_mac.cpp | 66 ++-------- src/l2/lower_mac_coding.cpp | 28 ++--- src/utils/viter_bi_codec.cpp | 204 +++---------------------------- 9 files changed, 254 insertions(+), 347 deletions(-) create mode 100644 .gitmodules create mode 160000 lib/ViterbiDecoderCpp create mode 100644 src/examples/tetra_viterbi.cpp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..dfd68d4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "lib/ViterbiDecoderCpp"] + path = lib/ViterbiDecoderCpp + url = https://github.com/FiendChain/ViterbiDecoderCpp.git diff --git a/CMakeLists.txt b/CMakeLists.txt index d586d3b..ee1453d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -30,6 +30,8 @@ add_executable(tetra-puncturing target_compile_options(tetra PUBLIC -std=c++17 -Wall -Wno-unused-variable) target_compile_options(tetra-puncturing PUBLIC -std=c++17 -Wall -Wno-unused-variable) +include(lib/ViterbiDecoderCpp/viterbi-config.cmake) + include_directories(src) find_package(cxxopts CONFIG REQUIRED) @@ -39,7 +41,7 @@ find_package(nlohmann_json REQUIRED) include_directories(${CMAKE_SOURCE_DIR}/include) -target_link_libraries(tetra ZLIB::ZLIB fmt::fmt nlohmann_json::nlohmann_json) +target_link_libraries(tetra ZLIB::ZLIB fmt::fmt nlohmann_json::nlohmann_json viterbi) install(TARGETS tetra DESTINATION bin) install(TARGETS tetra-puncturing DESTINATION bin) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index 3e66092..536eed1 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -43,12 +43,12 @@ class LowerMac { [[nodiscard]] static auto deinterleave(const std::vector& data, uint32_t K, uint32_t a) noexcept -> std::vector; [[nodiscard]] static auto depuncture23(const std::vector& data, uint32_t len) noexcept - -> std::vector; + -> std::vector; [[nodiscard]] static auto reed_muller_3014_decode(const std::vector& data) noexcept -> std::vector; [[nodiscard]] static auto check_crc_16_ccitt(const std::vector& data, int len) noexcept -> int; - [[nodiscard]] auto viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector; + [[nodiscard]] auto viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector; }; #endif diff --git a/include/utils/viter_bi_codec.hpp b/include/utils/viter_bi_codec.hpp index 5e12043..c6503ec 100644 --- a/include/utils/viter_bi_codec.hpp +++ b/include/utils/viter_bi_codec.hpp @@ -1,103 +1,42 @@ /* - * Original Implementation of ViterbiCodec. - * - * Author: Min Xu - * Date: 01/30/2015 - * - * Copyright 2015 Min Xu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. * + * Authors: + * Marenz Schmidl + * Tassilo Tanneberger */ -#ifndef VITERBI2_H -#define VITERBI2_H -#include -#include -#include -#include -// This class implements both a Viterbi Decoder and a Convolutional Encoder. -class ViterbiCodec { - public: - // Note about Polynomial Descriptor of a Convolutional Encoder / Decoder. - // A generator polymonial is built as follows: Build a binary number - // representation by placing a 1 in each spot where a connection line from - // the shift register feeds into the adder, and a zero elsewhere. There are 2 - // ways to arrange the bits: - // 1. msb-current - // The MSB of the polynomial corresponds to the current input, while the - // LSB corresponds to the oldest input that still remains in the shift - // register. - // This representation is used by MATLAB. See - // http://radio.feld.cvut.cz/matlab/toolbox/comm/tutor124.html - // 2. lsb-current - // The LSB of the polynomial corresponds to the current input, while the - // MSB corresponds to the oldest input that still remains in the shift - // register. - // This representation is used by the Spiral Viterbi Decoder Software - // Generator. See http://www.spiral.net/software/viterbi.html - // We use 2. - ViterbiCodec(int constraint, const std::vector& polynomials); +#pragma once - [[nodiscard]] auto Encode(const std::string& bits) const -> std::string; - [[nodiscard]] std::string Decode(const std::string& bits) const; +#include - [[nodiscard]] int constraint() const { return constraint_c; } +// include for viterbi algorithm size_t +#include +#include "viterbi/viterbi_decoder_scalar.h" - [[nodiscard]] const std::vector& polynomials() const { return polynomials_c; } +constexpr size_t K = 5; +constexpr size_t R = 4; - friend auto operator<<(std::ostream& stream, const ViterbiCodec& vec) -> std::ostream&; +class ViterbiCodec { + public: + ViterbiCodec(){}; + [[nodiscard]] std::vector Decode(const std::vector& bits) const; private: - // Suppose - // - // Trellis trellis; - // - // Then trellis[i][s] is the state in the (i - 1)th iteration which leads to - // the current state s in the ith iteration. - // It is used for traceback. - typedef std::vector> Trellis; + const std::vector G = {19, 29, 23, 27}; - int num_parity_bits() const; + const int8_t soft_decision_high = +1; + const int8_t soft_decision_low = -1; + const uint8_t max_error = uint8_t(soft_decision_high - soft_decision_low) * uint8_t(R); + const uint8_t error_margin = max_error * uint8_t(3u); - void InitializeOutputs(); + ViterbiDecoder_Config config = { + .soft_decision_max_error = max_error, + .initial_start_error = std::numeric_limits::min(), + .initial_non_start_error = static_cast(std::numeric_limits::min() + error_margin), + .renormalisation_threshold = static_cast(std::numeric_limits::max() - error_margin)}; - int NextState(int current_state, int input) const; - - std::string Output(int current_state, int input) const; - - int BranchMetric(const std::string& bits, int source_state, int target_state) const; - - // Given num_parity_bits() received bits, compute and returns path - // metric and its corresponding previous state. - std::pair PathMetric(const std::string& bits, const std::vector& prev_path_metrics, int state) const; - - // Given num_parity_bits() received bits, update path metrics of all states - // in the current iteration, and append new traceback vector to trellis. - void UpdatePathMetrics(const std::string& bits, std::vector* path_metrics, Trellis* trellis) const; - - const int constraint_c; - const std::vector polynomials_c; - - // The output table. - // The index is current input bit combined with previous inputs in the shift - // register. The value is the output parity bits in string format for - // convenience, e.g. "10". For example, suppose the shift register contains - // 0b10 (= 2), and the current input is 0b1 (= 1), then the index is 0b110 (= - // 6). - std::vector outputs_c; + ViterbiBranchTable branch_table = + ViterbiBranchTable(G.data(), soft_decision_high, soft_decision_low); }; - -auto operator<<(std::ostream& os, const ViterbiCodec& codec) -> std::ostream&; - -#endif /* VITERBI2_H */ diff --git a/lib/ViterbiDecoderCpp b/lib/ViterbiDecoderCpp new file mode 160000 index 0000000..10d7d1a --- /dev/null +++ b/lib/ViterbiDecoderCpp @@ -0,0 +1 @@ +Subproject commit 10d7d1a8e9a071d6bcda9074fb686f747b0d968f diff --git a/src/examples/tetra_viterbi.cpp b/src/examples/tetra_viterbi.cpp new file mode 100644 index 0000000..099d23f --- /dev/null +++ b/src/examples/tetra_viterbi.cpp @@ -0,0 +1,174 @@ +#include +#include +#include +#include +#include + +#include "viter_bi_codec.hpp" +#include "viterbi/viterbi_decoder_scalar.h" + +auto viterbi = std::vector( + {1, 0, 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 1, 2, + 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, + 0, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 1, + 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, + 0, 1, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, + 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, + 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 0, 1, + 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, + 0, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, + 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, + 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 1, + 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, + 1, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 1, 2, + 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, + 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 0, + 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, + 1, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, + 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, + 1, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 0, 0, + 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, + 0, 0, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, + 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, + 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 0, 1, + 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, + 0, 1, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, + 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 0, 0, 2, 2, + 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 0, + 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 0, 2, 2, 2, + 0, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 1, 2, + 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, + 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2}); + +auto data = std::vector( + {1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, + 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, + 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, + 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 1, 0, + 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 0, + 0, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, + 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, + 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 0}); + +template struct Decoder_Config { + soft_t soft_decision_high; + soft_t soft_decision_low; + ViterbiDecoder_Config decoder_config; +}; + +Decoder_Config get_hard8_decoding_config(const size_t code_rate) { + const int8_t soft_decision_high = +1; + const int8_t soft_decision_low = -1; + const uint8_t max_error = uint8_t(soft_decision_high - soft_decision_low) * uint8_t(code_rate); + const uint8_t error_margin = max_error * uint8_t(3u); + + ViterbiDecoder_Config config; + config.soft_decision_max_error = max_error; + config.initial_start_error = std::numeric_limits::min(); + config.initial_non_start_error = config.initial_start_error + error_margin; + config.renormalisation_threshold = std::numeric_limits::max() - error_margin; + + return {soft_decision_high, soft_decision_low, config}; +} + +std::vector new_viterbi_decode(const std::vector& bits, std::size_t end_state) { + constexpr size_t K = 5; + constexpr size_t R = 4; + + const std::vector G = {19, 29, 23, 27}; + + auto config = get_hard8_decoding_config(R); + auto branch_table = ViterbiBranchTable(G.data(), config.soft_decision_high, config.soft_decision_low); + auto vitdec = std::make_unique>(branch_table, config.decoder_config); + + const size_t total_bits = bits.size() / R; + const size_t total_tail_bits = K - 1u; + const size_t total_data_bits = total_bits - total_tail_bits; + const size_t total_input_bytes = total_data_bits / 8; + + // std::vector output_bytes(total_data_bits / 8); + std::vector output_bytes; + output_bytes.resize(total_bits / 8); + + vitdec->set_traceback_length(total_data_bits); + + vitdec->reset(); + vitdec->update(bits.data(), bits.size()); + vitdec->chainback(output_bytes.data(), total_data_bits, end_state); + const uint64_t error = vitdec->get_error(); + std::cout << "error=" << error << std::endl; + + return output_bytes; +} + +auto main(int argc, char** argv) -> int { + std::vector polynomials; + int constraint = 6; + + polynomials.push_back(0b10011); + polynomials.push_back(0b11101); + polynomials.push_back(0b10111); + polynomials.push_back(0b11011); + + auto viter_bi_codec_1614_ = std::make_unique(constraint, polynomials); + + std::vector decoded_data; + + { + std::string string_input; + for (unsigned char idx : viterbi) { + string_input += (char)(idx + '0'); + } + + std::string sOut = viter_bi_codec_1614_->Decode(string_input); + + for (char idx : sOut) { + decoded_data.push_back((uint8_t)(idx - '0')); + } + } + + if (decoded_data == data) { + std::cout << "Decoded data is equal" << std::endl; + } else { + std::cout << "Error: Decoded data not is equal" << std::endl; + return EXIT_FAILURE; + } + + auto func = [&](std::size_t end_state) { + std::vector new_viterbi; + + std::transform(viterbi.cbegin(), viterbi.cend(), std::back_inserter(new_viterbi), [](uint8_t v) { + if (v == 0) { + return -1; + } else if (v == 1) { + return 1; + } else { + return 0; + } + }); + + auto new_decoded_data = new_viterbi_decode(new_viterbi, end_state); + + std::vector new_decoded_data_mapped; + + for (auto elem : new_decoded_data) + for (auto i = 0; i < 8; i++) + new_decoded_data_mapped.push_back(elem & (1 << (7 - i)) ? 1 : 0); + + for (auto elem : new_decoded_data_mapped) + std::cout << std::to_string(elem) << " "; + std::cout << std::endl; + + std::cout << new_decoded_data_mapped.size() << std::endl; + }; + + for (auto i = 0; i < 16; i++) { + std::cout << "End State: " << std::to_string(i) << std::endl; + func(i); + } + + for (auto elem : data) + std::cout << std::to_string(elem) << " "; + std::cout << std::endl; + std::cout << data.size() << std::endl; +} diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 69950d9..056e342 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -5,29 +5,7 @@ LowerMac::LowerMac(std::shared_ptr reporter) : reporter_(reporter) { - /* - * Initialize Viterbi coder/decoder for MAC - * - * 8.2.3.1.1 Generator polynomials for the RCPC 16-state mother code of rate - * 1/4 - * - * G1 = 1 + D + D^4 (8.3) - * G2 = 1 + D^2 + D^3 + D^4 (8.4) - * G3 = 1 + D + D^2 + D^4 (8.5) - * G4 = 1 + D + D^3 + D^4 (8.6) - * - * NOTE: representing bit order must be reversed for the codec, eg. 1 + D + 0 - * + 0 + D^4 -> 10011 - * - */ - std::vector polynomials; - int constraint = 6; - - polynomials.push_back(0b10011); - polynomials.push_back(0b11101); - polynomials.push_back(0b10111); - polynomials.push_back(0b11011); - viter_bi_codec_1614_ = std::make_unique(constraint, polynomials); + viter_bi_codec_1614_ = std::make_unique(); upper_mac_ = std::make_shared(reporter_); } @@ -36,14 +14,7 @@ LowerMac::LowerMac(const LowerMac& lowerMac) { reporter_ = lowerMac.reporter_; upper_mac_ = lowerMac.upper_mac_; - std::vector polynomials; - int constraint = 6; - - polynomials.push_back(0b10011); - polynomials.push_back(0b11101); - polynomials.push_back(0b10111); - polynomials.push_back(0b11011); - viter_bi_codec_1614_ = std::make_unique(constraint, polynomials); + viter_bi_codec_1614_ = std::make_unique(); } static auto vectorExtract(const std::vector& vec, size_t pos, size_t length) -> std::vector { @@ -81,8 +52,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) sb = vectorExtract(frame, 94, 120); sb = descramble(sb, 120, 0x0003); sb = deinterleave(sb, 120, 11); - sb = depuncture23(sb, 120); - sb = viter_bi_decode_1614(sb); + sb = viter_bi_decode_1614(depuncture23(sb, 120)); if (check_crc_16_ccitt(sb, 76)) { sb = vectorExtract(sb, 0, 60); upper_mac_->process_BSCH(burst_type, sb); @@ -103,8 +73,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) bkn2 = vectorExtract(frame, 282, 216); bkn2 = descramble(bkn2, 216, upper_mac_->scrambling_code()); bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = depuncture23(bkn2, 216); - bkn2 = viter_bi_decode_1614(bkn2); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2, 216)); // if the crc does not work, then it might be a BLCH if (check_crc_16_ccitt(bkn2, 140)) { bkn2 = vectorExtract(bkn2, 0, 124); @@ -136,8 +105,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // control channel // ✅done bkn1 = deinterleave(bkn1, 432, 103); - bkn1 = depuncture23(bkn1, 432); - bkn1 = viter_bi_decode_1614(bkn1); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 432)); if (check_crc_16_ccitt(bkn1, 284)) { bkn1 = vectorExtract(bkn1, 0, 268); upper_mac_->process_SCH_F(burst_type, bkn1); @@ -163,8 +131,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) if (upper_mac_->downlink_usage() == DownlinkUsage::Traffic && upper_mac_->time_slot() <= 17) { bkn1 = deinterleave(bkn1, 216, 101); - bkn1 = depuncture23(bkn1, 216); - bkn1 = viter_bi_decode_1614(bkn1); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 216)); if (check_crc_16_ccitt(bkn1, 140)) { bkn1 = vectorExtract(bkn1, 0, 124); upper_mac_->process_STCH(burst_type, bkn1); @@ -172,8 +139,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) if (upper_mac_->second_slot_stolen()) { bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = depuncture23(bkn2, 216); - bkn2 = viter_bi_decode_1614(bkn2); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2, 216)); if (check_crc_16_ccitt(bkn2, 140)) { bkn2 = vectorExtract(bkn2, 0, 124); upper_mac_->process_STCH(burst_type, bkn2); @@ -189,16 +155,14 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // ✅done bkn1 = deinterleave(bkn1, 216, 101); - bkn1 = depuncture23(bkn1, 216); - bkn1 = viter_bi_decode_1614(bkn1); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 216)); if (check_crc_16_ccitt(bkn1, 140)) { bkn1 = vectorExtract(bkn1, 0, 124); upper_mac_->process_SCH_HD(burst_type, bkn1); } // control channel bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = depuncture23(bkn2, 216); - bkn2 = viter_bi_decode_1614(bkn2); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2, 216)); if (check_crc_16_ccitt(bkn2, 140)) { bkn2 = vectorExtract(bkn2, 0, 124); upper_mac_->process_SCH_HD(burst_type, bkn2); @@ -211,8 +175,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // XXX: assume to be control channel cb = deinterleave(cb, 168, 13); - cb = depuncture23(cb, 168); - cb = viter_bi_decode_1614(cb); + cb = viter_bi_decode_1614(depuncture23(cb, 168)); if (check_crc_16_ccitt(cb, 108)) { cb = vectorExtract(cb, 0, 92); functions.push_back(std::bind(&UpperMac::process_SCH_HU, upper_mac_, burst_type, cb)); @@ -224,8 +187,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // XXX: assume to be control channel bkn1 = deinterleave(bkn1, 432, 103); - bkn1 = depuncture23(bkn1, 432); - bkn1 = viter_bi_decode_1614(bkn1); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 432)); if (check_crc_16_ccitt(bkn1, 284)) { bkn1 = vectorExtract(bkn1, 0, 268); // fmt::print("NUB Burst crc good\n"); @@ -239,8 +201,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) bkn2 = descramble(bkn2, 216, upper_mac_->scrambling_code()); bkn1 = deinterleave(bkn1, 216, 101); - bkn1 = depuncture23(bkn1, 216); - bkn1 = viter_bi_decode_1614(bkn1); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 216)); if (check_crc_16_ccitt(bkn1, 140)) { bkn1 = vectorExtract(bkn1, 0, 124); // fmt::print("NUB_S 1 Burst crc good\n"); @@ -248,8 +209,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) } bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = depuncture23(bkn2, 216); - bkn2 = viter_bi_decode_1614(bkn2); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2, 216)); if (check_crc_16_ccitt(bkn2, 140)) { bkn2 = vectorExtract(bkn2, 0, 124); if (upper_mac_->second_slot_stolen()) { diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index 6225e6a..a0e3fc5 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -66,10 +66,10 @@ auto LowerMac::deinterleave(const std::vector& data, const uint32_t K, * @brief Depuncture with 2/3 rate - 8.2.3.1.3 * */ -auto LowerMac::depuncture23(const std::vector& data, const uint32_t len) noexcept -> std::vector { +auto LowerMac::depuncture23(const std::vector& data, const uint32_t len) noexcept -> std::vector { const uint8_t P[] = {0, 1, 2, 5}; // 8.2.3.1.3 - P[1..t] - std::vector res(4 * len * 2 / 3, - 2); // 8.2.3.1.2 with flag 2 for erase bit in Viterbi routine + std::vector res(4 * len * 2 / 3, + 0); // 8.2.3.1.2 with flag 0 for erase bit in Viterbi routine uint8_t t = 3; // 8.2.3.1.3 uint8_t period = 8; // 8.2.3.1.2 @@ -78,7 +78,7 @@ auto LowerMac::depuncture23(const std::vector& data, const uint32_t len uint32_t i = j; // punct->i_func(j); uint32_t k = period * ((i - 1) / t) + P[i - t * ((i - 1) / t)]; // punct->period * ((i-1)/t) + P[i - t*((i-1)/t)]; - res[k - 1] = data[j - 1]; + res[k - 1] = data[j - 1] ? 1 : -1; } return res; @@ -90,21 +90,15 @@ auto LowerMac::depuncture23(const std::vector& data, const uint32_t len * */ -auto LowerMac::viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector { - std::string string_input; - for (unsigned char idx : data) { - string_input += (char)(idx + '0'); - } - - std::string sOut = viter_bi_codec_1614_->Decode(string_input); - - std::vector res; - - for (char idx : sOut) { - res.push_back((uint8_t)(idx - '0')); +auto LowerMac::viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector { + std::vector out{}; + for (auto elem : viter_bi_codec_1614_->Decode(data)) { + for (auto i = 0; i < 8; i++) { + out.push_back((elem & (1 << (7 - i))) ? 1 : 0); + } } - return res; + return out; } /** diff --git a/src/utils/viter_bi_codec.cpp b/src/utils/viter_bi_codec.cpp index 3072cf8..3fdd592 100644 --- a/src/utils/viter_bi_codec.cpp +++ b/src/utils/viter_bi_codec.cpp @@ -1,196 +1,30 @@ /* - * Original Implementation of ViterbiCodec. - * - * Author: Min Xu - * Date: 01/30/2015 - * - * Copyright 2015 Min Xu - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. * + * Authors: + * Marenz Schmidl + * Tassilo Tanneberger */ -#include - -#include -#include -#include -#include -#include -#include -#include - -namespace { - -auto HammingDistance(const std::string& x, const std::string& y) -> int { - assert(x.size() == y.size()); - int distance = 0; - for (std::size_t i = 0; i < x.size(); i++) { - distance += x[i] != y[i]; - } - return distance; -} - -} // namespace - -auto operator<<(std::ostream& os, const ViterbiCodec& codec) -> std::ostream& { - os << "ViterbiCodec(" << codec.constraint() << ", {"; - const std::vector& polynomials = codec.polynomials(); - assert(!polynomials.empty()); - os << polynomials.front(); - for (std::size_t i = 1; i < polynomials.size(); i++) { - os << ", " << polynomials[i]; - } - return os << "})"; -} - -static auto ReverseBits(int num_bits, int input) -> int { - assert(input < (1 << num_bits)); - int output = 0; - while (num_bits-- > 0) { - output = (output << 1) + (input & 1); - input >>= 1; - } - return output; -} - -ViterbiCodec::ViterbiCodec(int constraint, const std::vector& polynomials) - : constraint_c(constraint) - , polynomials_c(polynomials) { - assert(!polynomials_c.empty()); - for (std::size_t i = 0; i < polynomials_c.size(); i++) { - assert(polynomials_c[i] > 0); - assert(polynomials_c[i] < (1 << constraint_c)); - } - InitializeOutputs(); -} - -int ViterbiCodec::num_parity_bits() const { return polynomials_c.size(); } -int ViterbiCodec::NextState(int current_state, int input) const { - return (current_state >> 1) | (input << (constraint_c - 2)); -} - -std::string ViterbiCodec::Output(int current_state, int input) const { - return outputs_c.at(current_state | (input << (constraint_c - 1))); -} - -std::string ViterbiCodec::Encode(const std::string& bits) const { - std::string encoded; - int state = 0; - - // Encode the message bits. - for (std::size_t i = 0; i < bits.size(); i++) { - char c = bits[i]; - assert(c == '0' || c == '1'); - int input = c - '0'; - encoded += Output(state, input); - state = NextState(state, input); - } - - // Encode (constaint_ - 1) flushing bits. - for (int i = 0; i < constraint_c - 1; i++) { - encoded += Output(state, 0); - state = NextState(state, 0); - } - - return encoded; -} - -void ViterbiCodec::InitializeOutputs() { - outputs_c.resize(1 << constraint_c); - for (std::size_t i = 0; i < outputs_c.size(); i++) { - for (int j = 0; j < num_parity_bits(); j++) { - // Reverse polynomial bits to make the convolution code simpler. - int polynomial = ReverseBits(constraint_c, polynomials_c[j]); - int input = i; - int output = 0; - for (int k = 0; k < constraint_c; k++) { - output ^= (input & 1) & (polynomial & 1); - polynomial >>= 1; - input >>= 1; - } - outputs_c[i] += output ? "1" : "0"; - } - } -} - -int ViterbiCodec::BranchMetric(const std::string& bits, int source_state, int target_state) const { - assert((int)bits.size() == num_parity_bits()); - assert((target_state & ((1 << (constraint_c - 2)) - 1)) == source_state >> 1); - const std::string output = Output(source_state, target_state >> (constraint_c - 2)); - - return HammingDistance(bits, output); -} - -std::pair ViterbiCodec::PathMetric(const std::string& bits, const std::vector& prev_path_metrics, - int state) const { - int s = (state & ((1 << (constraint_c - 2)) - 1)) << 1; - int source_state1 = s | 0; - int source_state2 = s | 1; - - int pm1 = prev_path_metrics[source_state1]; - if (pm1 < std::numeric_limits::max()) { - pm1 += BranchMetric(bits, source_state1, state); - } - int pm2 = prev_path_metrics[source_state2]; - if (pm2 < std::numeric_limits::max()) { - pm2 += BranchMetric(bits, source_state2, state); - } +#include - if (pm1 <= pm2) { - return std::make_pair(pm1, source_state1); - } else { - return std::make_pair(pm2, source_state2); - } -} +std::vector ViterbiCodec::Decode(const std::vector& bits) const { + auto vitdec = ViterbiDecoder_Scalar(branch_table, config); -void ViterbiCodec::UpdatePathMetrics(const std::string& bits, std::vector* path_metrics, Trellis* trellis) const { - std::vector new_path_metrics(path_metrics->size()); - std::vector new_trellis_column(1 << (constraint_c - 1)); - for (std::size_t i = 0; i < path_metrics->size(); i++) { - std::pair p = PathMetric(bits, *path_metrics, i); - new_path_metrics[i] = p.first; - new_trellis_column[i] = p.second; - } + const size_t total_bits = bits.size() / R; + const size_t total_tail_bits = K - 1u; + const size_t total_data_bits = total_bits - total_tail_bits; - *path_metrics = new_path_metrics; - trellis->push_back(new_trellis_column); -} + std::vector output_bytes(total_bits / 8); -std::string ViterbiCodec::Decode(const std::string& bits) const { - // Compute path metrics and generate trellis. - Trellis trellis; - std::vector path_metrics(1 << (constraint_c - 1), std::numeric_limits::max()); - path_metrics.front() = 0; - for (std::size_t i = 0; i < bits.size(); i += num_parity_bits()) { - std::string current_bits(bits, i, num_parity_bits()); - // If some bits are missing, fill with trailing zeros. - // This is not ideal but it is the best we can do. - if ((int)current_bits.size() < num_parity_bits()) { - current_bits.append(std::string(num_parity_bits() - current_bits.size(), '0')); - } - UpdatePathMetrics(current_bits, &path_metrics, &trellis); - } + vitdec.set_traceback_length(total_data_bits); - // Traceback. - std::string decoded; - int state = std::min_element(path_metrics.begin(), path_metrics.end()) - path_metrics.begin(); - for (int i = trellis.size() - 1; i >= 0; i--) { - decoded += state >> (constraint_c - 2) ? "1" : "0"; - state = trellis[i][state]; - } - std::reverse(decoded.begin(), decoded.end()); + vitdec.reset(); + vitdec.update(bits.data(), bits.size()); + vitdec.chainback(output_bytes.data(), total_data_bits, 0); + const uint64_t error = vitdec.get_error(); + // std::cout << "error=" << error << std::endl; - // Remove (constraint_c - 1) flushing bits. - return decoded.substr(0, decoded.size()); // - constraint_c + 1); + return output_bytes; } From 4a7b1e9d6664a8373b2dfc05464cb00465a5852e Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 10 Jul 2023 14:51:40 +0200 Subject: [PATCH 13/35] add test for viterbi decoder --- CMakeLists.txt | 7 ++ lib/ViterbiDecoderCpp | 2 +- src/examples/viter_bi_codec.cpp | 196 ++++++++++++++++++++++++++++++++ src/examples/viter_bi_codec.hpp | 103 +++++++++++++++++ 4 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 src/examples/viter_bi_codec.cpp create mode 100644 src/examples/viter_bi_codec.hpp diff --git a/CMakeLists.txt b/CMakeLists.txt index ee1453d..db53153 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -27,8 +27,13 @@ add_executable(tetra add_executable(tetra-puncturing src/examples/tetra_puncturing.cpp) +add_executable(tetra-viterbi + src/examples/viter_bi_codec.cpp + src/examples/tetra_viterbi.cpp) + target_compile_options(tetra PUBLIC -std=c++17 -Wall -Wno-unused-variable) target_compile_options(tetra-puncturing PUBLIC -std=c++17 -Wall -Wno-unused-variable) +target_compile_options(tetra-viterbi PUBLIC -std=c++17 -Wall -Wno-unused-variable) include(lib/ViterbiDecoderCpp/viterbi-config.cmake) @@ -42,6 +47,8 @@ find_package(nlohmann_json REQUIRED) include_directories(${CMAKE_SOURCE_DIR}/include) target_link_libraries(tetra ZLIB::ZLIB fmt::fmt nlohmann_json::nlohmann_json viterbi) +target_link_libraries(tetra-viterbi viterbi) install(TARGETS tetra DESTINATION bin) install(TARGETS tetra-puncturing DESTINATION bin) +install(TARGETS tetra-viterbi DESTINATION bin) diff --git a/lib/ViterbiDecoderCpp b/lib/ViterbiDecoderCpp index 10d7d1a..2193a63 160000 --- a/lib/ViterbiDecoderCpp +++ b/lib/ViterbiDecoderCpp @@ -1 +1 @@ -Subproject commit 10d7d1a8e9a071d6bcda9074fb686f747b0d968f +Subproject commit 2193a63d129a154cdb776aa954cc62df9123d4be diff --git a/src/examples/viter_bi_codec.cpp b/src/examples/viter_bi_codec.cpp new file mode 100644 index 0000000..6cd02dc --- /dev/null +++ b/src/examples/viter_bi_codec.cpp @@ -0,0 +1,196 @@ +/* + * Original Implementation of ViterbiCodec. + * + * Author: Min Xu + * Date: 01/30/2015 + * + * Copyright 2015 Min Xu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#include "viter_bi_codec.hpp" + +#include +#include +#include +#include +#include +#include +#include + +namespace { + +auto HammingDistance(const std::string& x, const std::string& y) -> int { + assert(x.size() == y.size()); + int distance = 0; + for (std::size_t i = 0; i < x.size(); i++) { + distance += x[i] != y[i]; + } + return distance; +} + +} // namespace + +auto operator<<(std::ostream& os, const ViterbiCodec& codec) -> std::ostream& { + os << "ViterbiCodec(" << codec.constraint() << ", {"; + const std::vector& polynomials = codec.polynomials(); + assert(!polynomials.empty()); + os << polynomials.front(); + for (std::size_t i = 1; i < polynomials.size(); i++) { + os << ", " << polynomials[i]; + } + return os << "})"; +} + +static auto ReverseBits(int num_bits, int input) -> int { + assert(input < (1 << num_bits)); + int output = 0; + while (num_bits-- > 0) { + output = (output << 1) + (input & 1); + input >>= 1; + } + return output; +} + +ViterbiCodec::ViterbiCodec(int constraint, const std::vector& polynomials) + : constraint_c(constraint) + , polynomials_c(polynomials) { + assert(!polynomials_c.empty()); + for (std::size_t i = 0; i < polynomials_c.size(); i++) { + assert(polynomials_c[i] > 0); + assert(polynomials_c[i] < (1 << constraint_c)); + } + InitializeOutputs(); +} + +int ViterbiCodec::num_parity_bits() const { return polynomials_c.size(); } + +int ViterbiCodec::NextState(int current_state, int input) const { + return (current_state >> 1) | (input << (constraint_c - 2)); +} + +std::string ViterbiCodec::Output(int current_state, int input) const { + return outputs_c.at(current_state | (input << (constraint_c - 1))); +} + +std::string ViterbiCodec::Encode(const std::string& bits) const { + std::string encoded; + int state = 0; + + // Encode the message bits. + for (std::size_t i = 0; i < bits.size(); i++) { + char c = bits[i]; + assert(c == '0' || c == '1'); + int input = c - '0'; + encoded += Output(state, input); + state = NextState(state, input); + } + + // Encode (constaint_ - 1) flushing bits. + for (int i = 0; i < constraint_c - 1; i++) { + encoded += Output(state, 0); + state = NextState(state, 0); + } + + return encoded; +} + +void ViterbiCodec::InitializeOutputs() { + outputs_c.resize(1 << constraint_c); + for (std::size_t i = 0; i < outputs_c.size(); i++) { + for (int j = 0; j < num_parity_bits(); j++) { + // Reverse polynomial bits to make the convolution code simpler. + int polynomial = ReverseBits(constraint_c, polynomials_c[j]); + int input = i; + int output = 0; + for (int k = 0; k < constraint_c; k++) { + output ^= (input & 1) & (polynomial & 1); + polynomial >>= 1; + input >>= 1; + } + outputs_c[i] += output ? "1" : "0"; + } + } +} + +int ViterbiCodec::BranchMetric(const std::string& bits, int source_state, int target_state) const { + assert((int)bits.size() == num_parity_bits()); + assert((target_state & ((1 << (constraint_c - 2)) - 1)) == source_state >> 1); + const std::string output = Output(source_state, target_state >> (constraint_c - 2)); + + return HammingDistance(bits, output); +} + +std::pair ViterbiCodec::PathMetric(const std::string& bits, const std::vector& prev_path_metrics, + int state) const { + int s = (state & ((1 << (constraint_c - 2)) - 1)) << 1; + int source_state1 = s | 0; + int source_state2 = s | 1; + + int pm1 = prev_path_metrics[source_state1]; + if (pm1 < std::numeric_limits::max()) { + pm1 += BranchMetric(bits, source_state1, state); + } + int pm2 = prev_path_metrics[source_state2]; + if (pm2 < std::numeric_limits::max()) { + pm2 += BranchMetric(bits, source_state2, state); + } + + if (pm1 <= pm2) { + return std::make_pair(pm1, source_state1); + } else { + return std::make_pair(pm2, source_state2); + } +} + +void ViterbiCodec::UpdatePathMetrics(const std::string& bits, std::vector* path_metrics, Trellis* trellis) const { + std::vector new_path_metrics(path_metrics->size()); + std::vector new_trellis_column(1 << (constraint_c - 1)); + for (std::size_t i = 0; i < path_metrics->size(); i++) { + std::pair p = PathMetric(bits, *path_metrics, i); + new_path_metrics[i] = p.first; + new_trellis_column[i] = p.second; + } + + *path_metrics = new_path_metrics; + trellis->push_back(new_trellis_column); +} + +std::string ViterbiCodec::Decode(const std::string& bits) const { + // Compute path metrics and generate trellis. + Trellis trellis; + std::vector path_metrics(1 << (constraint_c - 1), std::numeric_limits::max()); + path_metrics.front() = 0; + for (std::size_t i = 0; i < bits.size(); i += num_parity_bits()) { + std::string current_bits(bits, i, num_parity_bits()); + // If some bits are missing, fill with trailing zeros. + // This is not ideal but it is the best we can do. + if ((int)current_bits.size() < num_parity_bits()) { + current_bits.append(std::string(num_parity_bits() - current_bits.size(), '0')); + } + UpdatePathMetrics(current_bits, &path_metrics, &trellis); + } + + // Traceback. + std::string decoded; + int state = std::min_element(path_metrics.begin(), path_metrics.end()) - path_metrics.begin(); + for (int i = trellis.size() - 1; i >= 0; i--) { + decoded += state >> (constraint_c - 2) ? "1" : "0"; + state = trellis[i][state]; + } + std::reverse(decoded.begin(), decoded.end()); + + // Remove (constraint_c - 1) flushing bits. + return decoded.substr(0, decoded.size()); // - constraint_c + 1); +} diff --git a/src/examples/viter_bi_codec.hpp b/src/examples/viter_bi_codec.hpp new file mode 100644 index 0000000..5e12043 --- /dev/null +++ b/src/examples/viter_bi_codec.hpp @@ -0,0 +1,103 @@ +/* + * Original Implementation of ViterbiCodec. + * + * Author: Min Xu + * Date: 01/30/2015 + * + * Copyright 2015 Min Xu + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ +#ifndef VITERBI2_H +#define VITERBI2_H +#include +#include +#include +#include + +// This class implements both a Viterbi Decoder and a Convolutional Encoder. +class ViterbiCodec { + public: + // Note about Polynomial Descriptor of a Convolutional Encoder / Decoder. + // A generator polymonial is built as follows: Build a binary number + // representation by placing a 1 in each spot where a connection line from + // the shift register feeds into the adder, and a zero elsewhere. There are 2 + // ways to arrange the bits: + // 1. msb-current + // The MSB of the polynomial corresponds to the current input, while the + // LSB corresponds to the oldest input that still remains in the shift + // register. + // This representation is used by MATLAB. See + // http://radio.feld.cvut.cz/matlab/toolbox/comm/tutor124.html + // 2. lsb-current + // The LSB of the polynomial corresponds to the current input, while the + // MSB corresponds to the oldest input that still remains in the shift + // register. + // This representation is used by the Spiral Viterbi Decoder Software + // Generator. See http://www.spiral.net/software/viterbi.html + // We use 2. + ViterbiCodec(int constraint, const std::vector& polynomials); + + [[nodiscard]] auto Encode(const std::string& bits) const -> std::string; + [[nodiscard]] std::string Decode(const std::string& bits) const; + + [[nodiscard]] int constraint() const { return constraint_c; } + + [[nodiscard]] const std::vector& polynomials() const { return polynomials_c; } + + friend auto operator<<(std::ostream& stream, const ViterbiCodec& vec) -> std::ostream&; + + private: + // Suppose + // + // Trellis trellis; + // + // Then trellis[i][s] is the state in the (i - 1)th iteration which leads to + // the current state s in the ith iteration. + // It is used for traceback. + typedef std::vector> Trellis; + + int num_parity_bits() const; + + void InitializeOutputs(); + + int NextState(int current_state, int input) const; + + std::string Output(int current_state, int input) const; + + int BranchMetric(const std::string& bits, int source_state, int target_state) const; + + // Given num_parity_bits() received bits, compute and returns path + // metric and its corresponding previous state. + std::pair PathMetric(const std::string& bits, const std::vector& prev_path_metrics, int state) const; + + // Given num_parity_bits() received bits, update path metrics of all states + // in the current iteration, and append new traceback vector to trellis. + void UpdatePathMetrics(const std::string& bits, std::vector* path_metrics, Trellis* trellis) const; + + const int constraint_c; + const std::vector polynomials_c; + + // The output table. + // The index is current input bit combined with previous inputs in the shift + // register. The value is the output parity bits in string format for + // convenience, e.g. "10". For example, suppose the shift register contains + // 0b10 (= 2), and the current input is 0b1 (= 1), then the index is 0b110 (= + // 6). + std::vector outputs_c; +}; + +auto operator<<(std::ostream& os, const ViterbiCodec& codec) -> std::ostream&; + +#endif /* VITERBI2_H */ From ad2eadefdaf1f61e9d33bfd99d198e420fef70ce Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 10 Jul 2023 17:35:02 +0200 Subject: [PATCH 14/35] remove old code --- include/l2/lower_mac.hpp | 3 +-- src/l2/lower_mac.cpp | 10 +--------- 2 files changed, 2 insertions(+), 11 deletions(-) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index 536eed1..fdfd728 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -22,7 +22,6 @@ class LowerMac { public: LowerMac(std::shared_ptr reporter); - LowerMac(const LowerMac& lowerMac); ~LowerMac() = default; // does the signal processing and then returns a list of function that need to be executed for data to be passed to @@ -35,7 +34,7 @@ class LowerMac { private: std::shared_ptr reporter_{}; - std::unique_ptr viter_bi_codec_1614_{}; + std::shared_ptr viter_bi_codec_1614_{}; std::shared_ptr upper_mac_{}; [[nodiscard]] static auto descramble(const std::vector& data, int len, uint32_t scramblingCode) noexcept diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 056e342..735448a 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -5,18 +5,10 @@ LowerMac::LowerMac(std::shared_ptr reporter) : reporter_(reporter) { - viter_bi_codec_1614_ = std::make_unique(); - + viter_bi_codec_1614_ = std::make_shared(); upper_mac_ = std::make_shared(reporter_); } -LowerMac::LowerMac(const LowerMac& lowerMac) { - reporter_ = lowerMac.reporter_; - upper_mac_ = lowerMac.upper_mac_; - - viter_bi_codec_1614_ = std::make_unique(); -} - static auto vectorExtract(const std::vector& vec, size_t pos, size_t length) -> std::vector { std::vector res; From c87e0d8aa6f584870ad21fbdb6f270a7db5ab94d Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 11 Jul 2023 09:15:51 +0200 Subject: [PATCH 15/35] fix memory accumalation --- src/streaming_ordered_output_thread_pool_executor.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/streaming_ordered_output_thread_pool_executor.cpp b/src/streaming_ordered_output_thread_pool_executor.cpp index 160fdba..55cbd1e 100644 --- a/src/streaming_ordered_output_thread_pool_executor.cpp +++ b/src/streaming_ordered_output_thread_pool_executor.cpp @@ -75,6 +75,7 @@ template ReturnType StreamingOrderedOutputThreadPoolExecut std::lock_guard lk(cv_output_item_m); if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { result = search->second; + outputMap.erase(search); outputCounter++; return *result; } @@ -85,6 +86,7 @@ template ReturnType StreamingOrderedOutputThreadPoolExecut // find the output item and if found set outputCounter_ to the next item if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { result = search->second; + outputMap.erase(search); outputCounter++; return true; } From 07609837d9546b1b058afa8f0d2d04511b66b69f Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 11 Jul 2023 09:45:41 +0200 Subject: [PATCH 16/35] update to newer version of viterbi decoder library. use sse4.1 --- CMakeLists.txt | 2 +- include/l2/lower_mac.hpp | 4 ++-- include/utils/viter_bi_codec.hpp | 23 ++++++++++++----------- lib/ViterbiDecoderCpp | 2 +- src/examples/tetra_viterbi.cpp | 13 +++++++------ src/l2/lower_mac_coding.cpp | 8 ++++---- src/utils/viter_bi_codec.cpp | 7 ++++--- 7 files changed, 31 insertions(+), 28 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index db53153..674136d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ add_executable(tetra-viterbi src/examples/viter_bi_codec.cpp src/examples/tetra_viterbi.cpp) -target_compile_options(tetra PUBLIC -std=c++17 -Wall -Wno-unused-variable) +target_compile_options(tetra PUBLIC -std=c++17 -Wall -Wno-unused-variable -msse4.1) target_compile_options(tetra-puncturing PUBLIC -std=c++17 -Wall -Wno-unused-variable) target_compile_options(tetra-viterbi PUBLIC -std=c++17 -Wall -Wno-unused-variable) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index fdfd728..aa4b6e0 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -42,12 +42,12 @@ class LowerMac { [[nodiscard]] static auto deinterleave(const std::vector& data, uint32_t K, uint32_t a) noexcept -> std::vector; [[nodiscard]] static auto depuncture23(const std::vector& data, uint32_t len) noexcept - -> std::vector; + -> std::vector; [[nodiscard]] static auto reed_muller_3014_decode(const std::vector& data) noexcept -> std::vector; [[nodiscard]] static auto check_crc_16_ccitt(const std::vector& data, int len) noexcept -> int; - [[nodiscard]] auto viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector; + [[nodiscard]] auto viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector; }; #endif diff --git a/include/utils/viter_bi_codec.hpp b/include/utils/viter_bi_codec.hpp index c6503ec..825d028 100644 --- a/include/utils/viter_bi_codec.hpp +++ b/include/utils/viter_bi_codec.hpp @@ -14,6 +14,7 @@ // include for viterbi algorithm size_t #include #include "viterbi/viterbi_decoder_scalar.h" +#include "viterbi/x86/viterbi_decoder_sse_u16.h" constexpr size_t K = 5; constexpr size_t R = 4; @@ -21,22 +22,22 @@ constexpr size_t R = 4; class ViterbiCodec { public: ViterbiCodec(){}; - [[nodiscard]] std::vector Decode(const std::vector& bits) const; + [[nodiscard]] std::vector Decode(const std::vector& bits) const; private: const std::vector G = {19, 29, 23, 27}; - const int8_t soft_decision_high = +1; - const int8_t soft_decision_low = -1; - const uint8_t max_error = uint8_t(soft_decision_high - soft_decision_low) * uint8_t(R); - const uint8_t error_margin = max_error * uint8_t(3u); + const int16_t soft_decision_high = +1; + const int16_t soft_decision_low = -1; + const uint16_t max_error = uint16_t(soft_decision_high - soft_decision_low) * uint16_t(R); + const uint16_t error_margin = max_error * uint16_t(3u); - ViterbiDecoder_Config config = { + const ViterbiDecoder_Config config = { .soft_decision_max_error = max_error, - .initial_start_error = std::numeric_limits::min(), - .initial_non_start_error = static_cast(std::numeric_limits::min() + error_margin), - .renormalisation_threshold = static_cast(std::numeric_limits::max() - error_margin)}; + .initial_start_error = std::numeric_limits::min(), + .initial_non_start_error = static_cast(std::numeric_limits::min() + error_margin), + .renormalisation_threshold = static_cast(std::numeric_limits::max() - error_margin)}; - ViterbiBranchTable branch_table = - ViterbiBranchTable(G.data(), soft_decision_high, soft_decision_low); + const ViterbiBranchTable branch_table = + ViterbiBranchTable(G.data(), soft_decision_high, soft_decision_low); }; diff --git a/lib/ViterbiDecoderCpp b/lib/ViterbiDecoderCpp index 2193a63..17514d1 160000 --- a/lib/ViterbiDecoderCpp +++ b/lib/ViterbiDecoderCpp @@ -1 +1 @@ -Subproject commit 2193a63d129a154cdb776aa954cc62df9123d4be +Subproject commit 17514d16867796b1cdc5635137824659c2568ce0 diff --git a/src/examples/tetra_viterbi.cpp b/src/examples/tetra_viterbi.cpp index 099d23f..2ec0c6d 100644 --- a/src/examples/tetra_viterbi.cpp +++ b/src/examples/tetra_viterbi.cpp @@ -79,7 +79,8 @@ std::vector new_viterbi_decode(const std::vector& bits, std::si auto config = get_hard8_decoding_config(R); auto branch_table = ViterbiBranchTable(G.data(), config.soft_decision_high, config.soft_decision_low); - auto vitdec = std::make_unique>(branch_table, config.decoder_config); + auto vitdec = ViterbiDecoder_Core(branch_table, config.decoder_config); + using Decoder = ViterbiDecoder_Scalar; const size_t total_bits = bits.size() / R; const size_t total_tail_bits = K - 1u; @@ -90,12 +91,12 @@ std::vector new_viterbi_decode(const std::vector& bits, std::si std::vector output_bytes; output_bytes.resize(total_bits / 8); - vitdec->set_traceback_length(total_data_bits); + vitdec.set_traceback_length(total_data_bits); - vitdec->reset(); - vitdec->update(bits.data(), bits.size()); - vitdec->chainback(output_bytes.data(), total_data_bits, end_state); - const uint64_t error = vitdec->get_error(); + vitdec.reset(); + Decoder::template update(vitdec, bits.data(), bits.size()); + vitdec.chainback(output_bytes.data(), total_data_bits, end_state); + const uint64_t error = vitdec.get_error(); std::cout << "error=" << error << std::endl; return output_bytes; diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index a0e3fc5..9b414c5 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -66,10 +66,10 @@ auto LowerMac::deinterleave(const std::vector& data, const uint32_t K, * @brief Depuncture with 2/3 rate - 8.2.3.1.3 * */ -auto LowerMac::depuncture23(const std::vector& data, const uint32_t len) noexcept -> std::vector { +auto LowerMac::depuncture23(const std::vector& data, const uint32_t len) noexcept -> std::vector { const uint8_t P[] = {0, 1, 2, 5}; // 8.2.3.1.3 - P[1..t] - std::vector res(4 * len * 2 / 3, - 0); // 8.2.3.1.2 with flag 0 for erase bit in Viterbi routine + std::vector res(4 * len * 2 / 3, + 0); // 8.2.3.1.2 with flag 0 for erase bit in Viterbi routine uint8_t t = 3; // 8.2.3.1.3 uint8_t period = 8; // 8.2.3.1.2 @@ -90,7 +90,7 @@ auto LowerMac::depuncture23(const std::vector& data, const uint32_t len * */ -auto LowerMac::viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector { +auto LowerMac::viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector { std::vector out{}; for (auto elem : viter_bi_codec_1614_->Decode(data)) { for (auto i = 0; i < 8; i++) { diff --git a/src/utils/viter_bi_codec.cpp b/src/utils/viter_bi_codec.cpp index 3fdd592..beaf7c1 100644 --- a/src/utils/viter_bi_codec.cpp +++ b/src/utils/viter_bi_codec.cpp @@ -9,8 +9,9 @@ #include -std::vector ViterbiCodec::Decode(const std::vector& bits) const { - auto vitdec = ViterbiDecoder_Scalar(branch_table, config); +std::vector ViterbiCodec::Decode(const std::vector& bits) const { + auto vitdec = ViterbiDecoder_Core(branch_table, config); + using Decoder = ViterbiDecoder_SSE_u16; const size_t total_bits = bits.size() / R; const size_t total_tail_bits = K - 1u; @@ -21,7 +22,7 @@ std::vector ViterbiCodec::Decode(const std::vector& bits) const vitdec.set_traceback_length(total_data_bits); vitdec.reset(); - vitdec.update(bits.data(), bits.size()); + Decoder::template update(vitdec, bits.data(), bits.size()); vitdec.chainback(output_bytes.data(), total_data_bits, 0); const uint64_t error = vitdec.get_error(); // std::cout << "error=" << error << std::endl; From a8ec51c5fabb231e30d9ad27e89bbb312c8241dd Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sat, 12 Aug 2023 00:57:11 +0200 Subject: [PATCH 17/35] streaming thread pool executor wait_lok instead of lock and less threads --- src/iq_stream_decoder.cpp | 2 +- src/streaming_ordered_output_thread_pool_executor.cpp | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index b14262a..8ecc83d 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -23,7 +23,7 @@ IQStreamDecoder::IQStreamDecoder(std::shared_ptr lower_mac, std::transform(training_seq_x_.crbegin(), training_seq_x_.crend(), std::back_inserter(training_seq_x_reversed_conj_), [](auto v) { return std::conj(v); }); - threadPool_ = std::make_shared>>>(16); + threadPool_ = std::make_shared>>>(4); upperMacWorkerThread_ = std::thread(&IQStreamDecoder::upperMacWorker, this); } diff --git a/src/streaming_ordered_output_thread_pool_executor.cpp b/src/streaming_ordered_output_thread_pool_executor.cpp index 55cbd1e..f9e8c8e 100644 --- a/src/streaming_ordered_output_thread_pool_executor.cpp +++ b/src/streaming_ordered_output_thread_pool_executor.cpp @@ -10,6 +10,9 @@ #include #include +#include + +using namespace std::chrono_literals; template StreamingOrderedOutputThreadPoolExecutor::StreamingOrderedOutputThreadPoolExecutor(int numWorkers) { @@ -33,7 +36,7 @@ template void StreamingOrderedOutputThreadPoolExecutor lk(cv_input_item_m); - cv_input_item.wait(lk, [&] { + cv_input_item.wait_for(lk, 10ms, [&] { if (!inputQueue.empty()) { work = inputQueue.front(); inputQueue.pop_front(); @@ -82,7 +85,7 @@ template ReturnType StreamingOrderedOutputThreadPoolExecut } std::unique_lock lk(cv_output_item_m); - cv_output_item.wait(lk, [&] { + cv_output_item.wait_for(lk, 10ms, [&] { // find the output item and if found set outputCounter_ to the next item if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { result = search->second; From ebf09988071dfc7d23d4b30a548cf2369d8b9b2d Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sat, 12 Aug 2023 10:37:16 +0200 Subject: [PATCH 18/35] clang-format. add pthread names --- include/l2/logical_link_control.hpp | 1 + include/utils/viter_bi_codec.hpp | 2 + src/iq_stream_decoder.cpp | 9 ++++ src/l2/logical_link_control.cpp | 10 +++++ src/l3/mobile_link_entity.cpp | 3 ++ ...ng_ordered_output_thread_pool_executor.cpp | 42 +++++++++++-------- 6 files changed, 50 insertions(+), 17 deletions(-) diff --git a/include/l2/logical_link_control.hpp b/include/l2/logical_link_control.hpp index f4f040e..ebe12a2 100644 --- a/include/l2/logical_link_control.hpp +++ b/include/l2/logical_link_control.hpp @@ -35,6 +35,7 @@ class LogicalLinkControl { void process_bl_adata_without_fcs(const AddressType address, BitVector& vec); // Basic link (acknowledged service in connectionless mode) without Frame Check Sequence void process_bl_data_without_fcs(const AddressType address, BitVector& vec); + void process_bl_ack_without_fcs(const AddressType address, BitVector& vec); void process_supplementary_llc_pdu(const AddressType address, BitVector& vec); std::shared_ptr reporter_{}; diff --git a/include/utils/viter_bi_codec.hpp b/include/utils/viter_bi_codec.hpp index 825d028..96670b1 100644 --- a/include/utils/viter_bi_codec.hpp +++ b/include/utils/viter_bi_codec.hpp @@ -11,10 +11,12 @@ #include +// clang-format off // include for viterbi algorithm size_t #include #include "viterbi/viterbi_decoder_scalar.h" #include "viterbi/x86/viterbi_decoder_sse_u16.h" +// clang-format on constexpr size_t K = 5; constexpr size_t R = 4; diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index 8ecc83d..f21aea4 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -8,6 +8,9 @@ */ #include +#if defined(__linux__) +#include +#endif #include @@ -26,6 +29,11 @@ IQStreamDecoder::IQStreamDecoder(std::shared_ptr lower_mac, threadPool_ = std::make_shared>>>(4); upperMacWorkerThread_ = std::thread(&IQStreamDecoder::upperMacWorker, this); + +#if defined(__linux__) + auto handle = upperMacWorkerThread_.native_handle(); + pthread_setname_np(handle, "UpperMacWorker"); +#endif } void IQStreamDecoder::upperMacWorker() { @@ -146,6 +154,7 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { std::advance(end_x, 15); auto find_x = convolve_valid({start_x, end_x}, training_seq_x_reversed_conj_)[0]; + // use actual signal for further processing auto start = symbol_buffer_.cbegin(); if (std::abs(find_x) >= SEQUENCE_DETECTION_THRESHOLD) { diff --git a/src/l2/logical_link_control.cpp b/src/l2/logical_link_control.cpp index 223f0a6..3145b9d 100644 --- a/src/l2/logical_link_control.cpp +++ b/src/l2/logical_link_control.cpp @@ -40,6 +40,9 @@ void LogicalLinkControl::process(const AddressType address, BitVector& vec) { // BL-UDATA without FCS mle_->service_user_pdu(address, vec); break; + case 0b0011: + process_bl_ack_without_fcs(address, vec); + break; case 0b1101: process_supplementary_llc_pdu(address, vec); default: @@ -64,6 +67,13 @@ void LogicalLinkControl::process_bl_data_without_fcs(const AddressType address, mle_->service_user_pdu(address, vec); } +void LogicalLinkControl::process_bl_ack_without_fcs(const AddressType address, BitVector& vec) { + auto n_r = vec.take(1); + std::cout << " N(R) = 0b" << std::bitset<1>(n_r) << std::endl; + + mle_->service_user_pdu(address, vec); +} + void LogicalLinkControl::process_supplementary_llc_pdu(const AddressType address, BitVector& vec) { std::string supplementary_llc_pdu[] = {"AL-X-DATA/AL-X-DATA-AR/AL-X-FINAL/AL-X-FINAL-AR", "AL-X-UDATA/AL-X-UFINAL", "AL-X-UDATA/AL-X-UFINAL", "AL-X-ACK/AL-X-RNR", "Reserved"}; diff --git a/src/l3/mobile_link_entity.cpp b/src/l3/mobile_link_entity.cpp index 9dbc788..5c65647 100644 --- a/src/l3/mobile_link_entity.cpp +++ b/src/l3/mobile_link_entity.cpp @@ -104,6 +104,9 @@ void MobileLinkEntity::service_data_pdu(const AddressType address, BitVector& ve }; auto pdu_type = vec.take(3); + + std::cout << " " << mle_uplink_pdu_type[pdu_type] << " or " << mle_downlink_pdu_type[pdu_type] << std::endl; + std::cout << " " << vec << std::endl; } auto operator<<(std::ostream& stream, const MobileLinkEntity& mle) -> std::ostream& { diff --git a/src/streaming_ordered_output_thread_pool_executor.cpp b/src/streaming_ordered_output_thread_pool_executor.cpp index f9e8c8e..9fc5d1e 100644 --- a/src/streaming_ordered_output_thread_pool_executor.cpp +++ b/src/streaming_ordered_output_thread_pool_executor.cpp @@ -8,9 +8,12 @@ */ #include +#if defined(__linux__) +#include +#endif -#include #include +#include using namespace std::chrono_literals; @@ -18,6 +21,13 @@ template StreamingOrderedOutputThreadPoolExecutor::StreamingOrderedOutputThreadPoolExecutor(int numWorkers) { for (auto i = 0; i < numWorkers; i++) { std::thread t(&StreamingOrderedOutputThreadPoolExecutor::worker, this); + +#if defined(__linux__) + auto handle = t.native_handle(); + auto threadName = "StreamWorker" + std::to_string(i); + pthread_setname_np(handle, threadName.c_str()); +#endif + workers.push_back(std::move(t)); } } @@ -74,28 +84,26 @@ void StreamingOrderedOutputThreadPoolExecutor::queueWork(std::functi template ReturnType StreamingOrderedOutputThreadPoolExecutor::get() { std::optional result{}; - { - std::lock_guard lk(cv_output_item_m); - if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { - result = search->second; - outputMap.erase(search); - outputCounter++; - return *result; - } - } + while (!result.has_value()) { + std::unique_lock lk(cv_output_item_m); + cv_output_item.wait_for(lk, 10ms, [&] { + // find the output item and if found set outputCounter_ to the next item + if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { + result = search->second; + outputMap.erase(search); + outputCounter++; + return true; + } + + return false; + }); - std::unique_lock lk(cv_output_item_m); - cv_output_item.wait_for(lk, 10ms, [&] { - // find the output item and if found set outputCounter_ to the next item if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { result = search->second; outputMap.erase(search); outputCounter++; - return true; } - - return false; - }); + } return *result; } From cb239faebe67ae25ee1266937d45962540e1ccf4 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sat, 12 Aug 2023 10:53:58 +0200 Subject: [PATCH 19/35] update flake.lock to 23.05 --- flake.lock | 32 +++++++++++++++++++++++++------- flake.nix | 2 +- 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/flake.lock b/flake.lock index 2e0418a..ae1e268 100644 --- a/flake.lock +++ b/flake.lock @@ -2,16 +2,16 @@ "nodes": { "nixpkgs": { "locked": { - "lastModified": 1671313200, - "narHash": "sha256-itZTrtHeDJjV696+ur0/TzkTqb5y3Eb57WRLRPK3rwA=", + "lastModified": 1691693223, + "narHash": "sha256-9t8ZY1XNAsWqxAJmXgg+GXqF5chORMVnBT6PSHaRV3I=", "owner": "NixOS", "repo": "nixpkgs", - "rev": "0938d73bb143f4ae037143572f11f4338c7b2d1c", + "rev": "18784aac1013da9b442adf29b6c7c228518b5d3f", "type": "github" }, "original": { "owner": "NixOS", - "ref": "nixos-22.11", + "ref": "nixos-23.05", "repo": "nixpkgs", "type": "github" } @@ -22,13 +22,31 @@ "utils": "utils" } }, + "systems": { + "locked": { + "lastModified": 1681028828, + "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=", + "owner": "nix-systems", + "repo": "default", + "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e", + "type": "github" + }, + "original": { + "owner": "nix-systems", + "repo": "default", + "type": "github" + } + }, "utils": { + "inputs": { + "systems": "systems" + }, "locked": { - "lastModified": 1659877975, - "narHash": "sha256-zllb8aq3YO3h8B/U0/J1WBgAL8EX5yWf5pMj3G0NAmc=", + "lastModified": 1689068808, + "narHash": "sha256-6ixXo3wt24N/melDWjq70UuHQLxGV8jZvooRanIHXw0=", "owner": "numtide", "repo": "flake-utils", - "rev": "c0e246b9b83f637f4681389ecabcb2681b4f3af0", + "rev": "919d646de7be200f3bf08cb76ae1f09402b6f9b4", "type": "github" }, "original": { diff --git a/flake.nix b/flake.nix index 9de057b..cb25116 100644 --- a/flake.nix +++ b/flake.nix @@ -1,6 +1,6 @@ { inputs = { - nixpkgs.url = github:NixOS/nixpkgs/nixos-22.11; + nixpkgs.url = github:NixOS/nixpkgs/nixos-23.05; utils.url = "github:numtide/flake-utils"; }; From 38e9308014f4f3506e22d71686d8816d1b615274 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sat, 12 Aug 2023 12:59:08 +0200 Subject: [PATCH 20/35] implement LLC basic link pdus with fcs --- include/l2/logical_link_control.hpp | 12 ++++ include/utils/bit_vector.hpp | 2 + src/l2/logical_link_control.cpp | 95 ++++++++++++++++++++++++++++- src/utils/bit_vector.cpp | 35 +++++++++++ 4 files changed, 143 insertions(+), 1 deletion(-) diff --git a/include/l2/logical_link_control.hpp b/include/l2/logical_link_control.hpp index ebe12a2..b535848 100644 --- a/include/l2/logical_link_control.hpp +++ b/include/l2/logical_link_control.hpp @@ -35,9 +35,21 @@ class LogicalLinkControl { void process_bl_adata_without_fcs(const AddressType address, BitVector& vec); // Basic link (acknowledged service in connectionless mode) without Frame Check Sequence void process_bl_data_without_fcs(const AddressType address, BitVector& vec); + void process_bl_udata_without_fcs(const AddressType address, BitVector& vec); void process_bl_ack_without_fcs(const AddressType address, BitVector& vec); + + // Basic link (acknowledged service in connectionless mode) with Frame Check Sequence + void process_bl_adata_with_fcs(const AddressType address, BitVector& vec); + // Basic link (acknowledged service in connectionless mode) with Frame Check Sequence + void process_bl_data_with_fcs(const AddressType address, BitVector& vec); + void process_bl_udata_with_fcs(const AddressType address, BitVector& vec); + void process_bl_ack_with_fcs(const AddressType address, BitVector& vec); + void process_supplementary_llc_pdu(const AddressType address, BitVector& vec); + static auto compute_fcs(std::vector const& data) -> uint32_t; + static auto check_fcs(BitVector& vec) -> bool; + std::shared_ptr reporter_{}; std::shared_ptr mle_{}; }; diff --git a/include/utils/bit_vector.hpp b/include/utils/bit_vector.hpp index 8ff8c5c..aaf5bdb 100644 --- a/include/utils/bit_vector.hpp +++ b/include/utils/bit_vector.hpp @@ -24,8 +24,10 @@ class BitVector { ~BitVector() noexcept = default; auto take_vector(std::size_t numberBits) -> std::vector; + auto take_last_vector(std::size_t numberBits) -> std::vector; void append(std::vector bits); auto take(std::size_t numberBits) -> uint64_t; + auto take_last(std::size_t numberBits) -> uint64_t; auto take_last() -> uint8_t; [[nodiscard]] inline auto bits_left() const noexcept -> std::size_t { return data_.size(); }; [[nodiscard]] auto is_mac_padding() const noexcept -> bool; diff --git a/src/l2/logical_link_control.cpp b/src/l2/logical_link_control.cpp index 3145b9d..e03ac52 100644 --- a/src/l2/logical_link_control.cpp +++ b/src/l2/logical_link_control.cpp @@ -3,6 +3,40 @@ #include +auto LogicalLinkControl::compute_fcs(std::vector const& data) -> uint32_t { + uint32_t crc = 0xFFFFFFFF; + if (data.size() < 32) { + crc <<= (32 - data.size()); + } + + for (auto it = data.cbegin(); it != data.cend(); it++) { + uint8_t bit = (*it ^ (crc >> 31)) & 1; + crc <<= 1; + if (bit) { + crc = crc ^ 0x04C11DB7; + } + } + return ~crc; +} + +auto LogicalLinkControl::check_fcs(BitVector& vec) -> bool { + // remove last 32 bits + auto fcs = vec.take_last(32); + auto bits = BitVector(vec).take_vector(vec.bits_left()); + + auto computed_fcs = LogicalLinkControl::compute_fcs(bits); + + if (fcs != computed_fcs) { + std::cout << " FCS error" << std::endl; + std::cout << " FCS 0b" << std::bitset<32>(fcs) << std::endl; + std::cout << " computed FCS: 0b" << std::bitset<32>(computed_fcs) << std::endl; + return false; + } else { + std::cout << " FCS good" << std::endl; + return true; + } +} + void LogicalLinkControl::process(const AddressType address, BitVector& vec) { std::cout << "LLC received: " << std::endl; std::cout << " Address: " << address << std::endl; @@ -31,18 +65,37 @@ void LogicalLinkControl::process(const AddressType address, BitVector& vec) { switch (pdu_type) { case 0b0000: + // BL-ADATA without FCS process_bl_adata_without_fcs(address, vec); break; case 0b0001: + // BL-DATA without FCS process_bl_data_without_fcs(address, vec); break; case 0b0010: // BL-UDATA without FCS - mle_->service_user_pdu(address, vec); + process_bl_udata_without_fcs(address, vec); break; case 0b0011: + // BL-ACK without FCS process_bl_ack_without_fcs(address, vec); break; + case 0b0100: + // BL-ADATA with FCS + process_bl_adata_with_fcs(address, vec); + break; + case 0b0101: + // BL-DATA with FCS + process_bl_data_with_fcs(address, vec); + break; + case 0b0110: + // BL-UDATA with FCS + process_bl_udata_with_fcs(address, vec); + break; + case 0b0111: + // BL-ACK with FCS + process_bl_ack_with_fcs(address, vec); + break; case 0b1101: process_supplementary_llc_pdu(address, vec); default: @@ -67,6 +120,10 @@ void LogicalLinkControl::process_bl_data_without_fcs(const AddressType address, mle_->service_user_pdu(address, vec); } +void LogicalLinkControl::process_bl_udata_without_fcs(const AddressType address, BitVector& vec) { + mle_->service_user_pdu(address, vec); +} + void LogicalLinkControl::process_bl_ack_without_fcs(const AddressType address, BitVector& vec) { auto n_r = vec.take(1); std::cout << " N(R) = 0b" << std::bitset<1>(n_r) << std::endl; @@ -74,6 +131,42 @@ void LogicalLinkControl::process_bl_ack_without_fcs(const AddressType address, B mle_->service_user_pdu(address, vec); } +void LogicalLinkControl::process_bl_adata_with_fcs(const AddressType address, BitVector& vec) { + auto n_r = vec.take(1); + auto n_s = vec.take(1); + + std::cout << " N(R) = 0b" << std::bitset<1>(n_r) << std::endl; + std::cout << " N(S) = 0b" << std::bitset<1>(n_s) << std::endl; + + if (LogicalLinkControl::check_fcs(vec)) { + mle_->service_user_pdu(address, vec); + } +} + +void LogicalLinkControl::process_bl_data_with_fcs(const AddressType address, BitVector& vec) { + auto n_s = vec.take(1); + std::cout << " N(S) = 0b" << std::bitset<1>(n_s) << std::endl; + + if (LogicalLinkControl::check_fcs(vec)) { + mle_->service_user_pdu(address, vec); + } +} + +void LogicalLinkControl::process_bl_udata_with_fcs(const AddressType address, BitVector& vec) { + if (LogicalLinkControl::check_fcs(vec)) { + mle_->service_user_pdu(address, vec); + } +} + +void LogicalLinkControl::process_bl_ack_with_fcs(const AddressType address, BitVector& vec) { + auto n_r = vec.take(1); + std::cout << " N(R) = 0b" << std::bitset<1>(n_r) << std::endl; + + if (LogicalLinkControl::check_fcs(vec)) { + mle_->service_user_pdu(address, vec); + } +} + void LogicalLinkControl::process_supplementary_llc_pdu(const AddressType address, BitVector& vec) { std::string supplementary_llc_pdu[] = {"AL-X-DATA/AL-X-DATA-AR/AL-X-FINAL/AL-X-FINAL-AR", "AL-X-UDATA/AL-X-UFINAL", "AL-X-UDATA/AL-X-UFINAL", "AL-X-ACK/AL-X-RNR", "Reserved"}; diff --git a/src/utils/bit_vector.cpp b/src/utils/bit_vector.cpp index cb6c1af..44545c5 100644 --- a/src/utils/bit_vector.cpp +++ b/src/utils/bit_vector.cpp @@ -29,6 +29,26 @@ auto BitVector::take_vector(const size_t numberBits) -> std::vector { return res; } +auto BitVector::take_last_vector(const size_t numberBits) -> std::vector { + if (numberBits > bits_left()) { + throw std::runtime_error(std::to_string(numberBits) + " bits not left in BitVec (" + + std::to_string(bits_left()) + ")"); + } + + std::vector res; + + auto start = data_.begin(); + // advance to position of last N elements + std::advance(start, bits_left() - numberBits); + + std::copy_n(start, numberBits, std::back_inserter(res)); + + // delete last n entries + std::vector(data_.begin(), data_.begin() + bits_left() - numberBits).swap(data_); + + return res; +} + void BitVector::append(std::vector bits) { data_.insert(data_.end(), bits.begin(), bits.end()); } auto BitVector::take(const size_t numberBits) -> uint64_t { @@ -46,6 +66,21 @@ auto BitVector::take(const size_t numberBits) -> uint64_t { return ret; } +auto BitVector::take_last(const size_t numberBits) -> uint64_t { + std::vector bits = take_last_vector(numberBits); + + uint64_t ret = 0; + + for (auto it = bits.begin(); it != bits.end(); it++) { + if (it != bits.begin()) { + ret <<= 1; + } + ret |= (*it & 0x1); + } + + return ret; +} + auto BitVector::take_last() -> uint8_t { if (1 > bits_left()) { throw std::runtime_error("1 bit not left in BitVec (" + std::to_string(bits_left()) + ")"); From 31946c52f15a9bc9cb637acd8ce94916c583d98a Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 23 Jan 2024 21:36:56 +0100 Subject: [PATCH 21/35] implement a crude way to stop program on CTRL-C or EOF --- include/iq_stream_decoder.hpp | 3 ++- include/signal_handler.hpp | 3 +++ include/streaming_ordered_output_thread_pool_executor.hpp | 4 +++- src/decoder.cpp | 3 +++ src/iq_stream_decoder.cpp | 8 +++++++- src/main.cpp | 3 ++- src/streaming_ordered_output_thread_pool_executor.cpp | 8 +++++++- 7 files changed, 27 insertions(+), 5 deletions(-) create mode 100644 include/signal_handler.hpp diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index 352c55d..81d3474 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -16,6 +16,7 @@ #include #include #include +#include #include /** @@ -27,7 +28,7 @@ class IQStreamDecoder { public: IQStreamDecoder(std::shared_ptr lower_mac, std::shared_ptr bit_stream_decoder, bool is_uplink); - ~IQStreamDecoder() = default; + ~IQStreamDecoder(); void process_complex(std::complex symbol) noexcept; diff --git a/include/signal_handler.hpp b/include/signal_handler.hpp new file mode 100644 index 0000000..7725229 --- /dev/null +++ b/include/signal_handler.hpp @@ -0,0 +1,3 @@ +#pragma once + +volatile extern bool stop; diff --git a/include/streaming_ordered_output_thread_pool_executor.hpp b/include/streaming_ordered_output_thread_pool_executor.hpp index 80db301..6482b89 100644 --- a/include/streaming_ordered_output_thread_pool_executor.hpp +++ b/include/streaming_ordered_output_thread_pool_executor.hpp @@ -18,12 +18,14 @@ #include #include +#include + // thread pool executing work but outputting it the order of the input template class StreamingOrderedOutputThreadPoolExecutor { public: StreamingOrderedOutputThreadPoolExecutor(int numWorkers); - ~StreamingOrderedOutputThreadPoolExecutor() = default; + ~StreamingOrderedOutputThreadPoolExecutor(); // append work to the queue void queueWork(std::function work); diff --git a/src/decoder.cpp b/src/decoder.cpp index fd64699..507fa89 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -88,16 +88,19 @@ void Decoder::main_loop() { auto bytes_read = read(input_fd_, rx_buffer, sizeof(rx_buffer)); if (errno == EINTR) { + stop = 1; return; } else if (bytes_read < 0) { throw std::runtime_error("Read error"); } else if (bytes_read == 0) { + stop = 1; return; } if (output_file_fd_.has_value()) { if (write(*output_file_fd_, rx_buffer, bytes_read) != bytes_read) { // unable to write to output TODO: possible log or fail hard + stop = 1; return; } } diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index f21aea4..d9c8841 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -36,8 +36,14 @@ IQStreamDecoder::IQStreamDecoder(std::shared_ptr lower_mac, #endif } +IQStreamDecoder::~IQStreamDecoder() { + // TODO: replace this crude hack that keeps the StreamingOrderedOutputThreadPoolExecutor<...> get function from blocking on programm stop + threadPool_->queueWork([]() { return std::vector>(); }); + upperMacWorkerThread_.join(); +} + void IQStreamDecoder::upperMacWorker() { - while (true) { + while (!stop) { for (auto func : threadPool_->get()) { func(); } diff --git a/src/main.cpp b/src/main.cpp index ce2ec4a..89cc2a9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -5,8 +5,9 @@ #include #include +#include -static bool stop = false; +volatile bool stop = false; void sigint_handler(int s) { (void)s; diff --git a/src/streaming_ordered_output_thread_pool_executor.cpp b/src/streaming_ordered_output_thread_pool_executor.cpp index 9fc5d1e..0aaba20 100644 --- a/src/streaming_ordered_output_thread_pool_executor.cpp +++ b/src/streaming_ordered_output_thread_pool_executor.cpp @@ -32,8 +32,14 @@ StreamingOrderedOutputThreadPoolExecutor::StreamingOrderedOutputThre } } +template +StreamingOrderedOutputThreadPoolExecutor::~StreamingOrderedOutputThreadPoolExecutor() { + for (auto& t : workers) + t.join(); +} + template void StreamingOrderedOutputThreadPoolExecutor::worker() { - while (true) { + while (!stop) { std::optional>> work{}; { From feba05832792abdfe4c3da4aacb256c5ff56cb5a Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 24 Jan 2024 21:55:08 +0100 Subject: [PATCH 22/35] optimize conv for detection --- include/fixed_queue.hpp | 24 ++++++++------ include/iq_stream_decoder.hpp | 10 +++--- src/iq_stream_decoder.cpp | 61 +++++++++++++---------------------- 3 files changed, 42 insertions(+), 53 deletions(-) diff --git a/include/fixed_queue.hpp b/include/fixed_queue.hpp index 9557f15..73a8260 100644 --- a/include/fixed_queue.hpp +++ b/include/fixed_queue.hpp @@ -2,30 +2,34 @@ #include #include -template > -class FixedQueue : public std::queue { +template > class FixedQueue { + private: + Container queue{}; + public: FixedQueue() { for (auto i = 0; i < MaxLen; i++) { T default_value{}; - std::queue::push(default_value); + queue.push_back(default_value); } } void push(const T& value) { - if (this->size() == MaxLen) { - this->c.pop_front(); + if (queue.size() == MaxLen) { + queue.pop_front(); } - std::queue::push(value); + queue.push_back(value); } void pop(const T& value) { std::logic_error("Function not implemented"); } - typename Container::const_iterator cbegin() { return this->c.cbegin(); } + typename Container::const_iterator cbegin() { return queue.cbegin(); } + + typename Container::const_reverse_iterator crbegin() { return queue.crbegin(); } - typename Container::const_reverse_iterator crbegin() { return this->c.crbegin(); } + typename Container::const_iterator cend() { return queue.cend(); } - typename Container::const_iterator cend() { return this->c.cend(); } + typename Container::const_reverse_iterator crend() { return queue.crend(); } - typename Container::const_reverse_iterator crend() { return this->c.crend(); } + typename Container::const_reference operator[](typename Container::size_type pos) const { return queue[pos]; } }; diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index 81d3474..50cf43c 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -33,19 +33,21 @@ class IQStreamDecoder { void process_complex(std::complex symbol) noexcept; private: + using QueueT = FixedQueue, 300>; + void upperMacWorker(); std::complex hard_decision(std::complex symbol); std::vector symbols_to_bitstream(std::vector> const& stream); - std::vector> convolve_valid(std::vector> const& a, - std::vector> const& b); + void abs_convolve_same_length(const QueueT& queueA, const std::size_t offsetA, const std::complex* itb, + const std::size_t len, float* res); std::vector> channel_estimation(std::vector> const& stream, std::vector> const& pilots); - FixedQueue, 300> symbol_buffer_; - FixedQueue, 300> symbol_buffer_hard_decision_; + QueueT symbol_buffer_; + QueueT symbol_buffer_hard_decision_; // 9.4.4.3.2 Normal training sequence const std::vector> training_seq_n_ = {{-1, -1}, {-1, 1}, {1, 1}, {1, 1}, {-1, -1}, {1, -1}, diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index d9c8841..ce3540f 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -37,7 +37,8 @@ IQStreamDecoder::IQStreamDecoder(std::shared_ptr lower_mac, } IQStreamDecoder::~IQStreamDecoder() { - // TODO: replace this crude hack that keeps the StreamingOrderedOutputThreadPoolExecutor<...> get function from blocking on programm stop + // TODO: replace this crude hack that keeps the StreamingOrderedOutputThreadPoolExecutor<...> get function from + // blocking on programm stop threadPool_->queueWork([]() { return std::vector>(); }); upperMacWorkerThread_.join(); } @@ -100,27 +101,14 @@ std::vector IQStreamDecoder::symbols_to_bitstream(std::vector> IQStreamDecoder::convolve_valid(std::vector> const& a, - std::vector> const& b) { - std::vector> res; - - // make shure a is longer equal than b - if (b.size() > a.size()) { - return convolve_valid(b, a); - } - - for (int i = 0; i < a.size() - b.size() + 1; i++) { - std::complex v{}; - auto ita = a.begin(); - std::advance(ita, i); - auto itb = b.rbegin(); - for (; itb != b.rend(); ita++, itb++) { - v += *ita * *itb; - } - res.push_back(v); +void IQStreamDecoder::abs_convolve_same_length(const QueueT& queueA, const std::size_t offsetA, + const std::complex* const itb, const std::size_t len, + float* res) { + std::complex acc = {0.0, 0.0}; + for (std::size_t i = 0; i < len; ++i) { + acc += queueA[offsetA + i] * itb[i]; } - - return res; + *res = std::abs(acc); } std::vector> IQStreamDecoder::channel_estimation(std::vector> const& stream, @@ -131,6 +119,10 @@ std::vector> IQStreamDecoder::channel_estimation(std::vector void IQStreamDecoder::process_complex(std::complex symbol) noexcept { if (is_uplink_) { + float detectedN; + float detectedP; + float detectedX; + // Control Uplink Burst or Normal Uplink Burst symbol_buffer_.push(symbol); symbol_buffer_hard_decision_.push(hard_decision(symbol)); @@ -140,30 +132,21 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { // // find NUB // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 - auto start_n = symbol_buffer_hard_decision_.cbegin(); - std::advance(start_n, 109); - auto end_n = start_n; - std::advance(end_n, 11); - auto find_n = convolve_valid({start_n, end_n}, training_seq_n_reversed_conj_)[0]; + abs_convolve_same_length(symbol_buffer_hard_decision_, 109, training_seq_n_reversed_conj_.data(), + training_seq_n_reversed_conj_.size(), &detectedN); // find NUB_Split // 2 tail + 108 coded symbols + middle of 11 training sequence (6) = 116 - auto start_p = symbol_buffer_hard_decision_.cbegin(); - std::advance(start_p, 109); - auto end_p = start_p; - std::advance(end_p, 11); - auto find_p = convolve_valid({start_p, end_p}, training_seq_p_reversed_conj_)[0]; + abs_convolve_same_length(symbol_buffer_hard_decision_, 109, training_seq_p_reversed_conj_.data(), + training_seq_p_reversed_conj_.size(), &detectedP); // find CUB // 2 tail + 42 coded symbols + middle of 15 training sequence (8) = 52 - auto start_x = symbol_buffer_hard_decision_.cbegin(); - std::advance(start_x, 44); - auto end_x = start_x; - std::advance(end_x, 15); - auto find_x = convolve_valid({start_x, end_x}, training_seq_x_reversed_conj_)[0]; + abs_convolve_same_length(symbol_buffer_hard_decision_, 44, training_seq_x_reversed_conj_.data(), + training_seq_x_reversed_conj_.size(), &detectedX); // use actual signal for further processing auto start = symbol_buffer_.cbegin(); - if (std::abs(find_x) >= SEQUENCE_DETECTION_THRESHOLD) { + if (detectedX >= SEQUENCE_DETECTION_THRESHOLD) { // std::cout << "Potential CUB found" << std::endl; auto end = start; @@ -174,7 +157,7 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bitstream, BurstType::ControlUplinkBurst)); } - if (std::abs(find_p) >= SEQUENCE_DETECTION_THRESHOLD) { + if (detectedP >= SEQUENCE_DETECTION_THRESHOLD) { // std::cout << "Potential NUB_Split found" << std::endl; auto end = start; @@ -185,7 +168,7 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { BurstType::NormalUplinkBurstSplit)); } - if (std::abs(find_n) >= SEQUENCE_DETECTION_THRESHOLD) { + if (detectedN >= SEQUENCE_DETECTION_THRESHOLD) { // std::cout << "Potential NUB found" << std::endl; auto end = start; From 5c9bed1ea40014598ddfb423bd600d864bbc804a Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Thu, 25 Jan 2024 02:34:31 +0100 Subject: [PATCH 23/35] improve performance by a bit --- include/fixed_queue.hpp | 4 +++ include/iq_stream_decoder.hpp | 4 ++- src/iq_stream_decoder.cpp | 52 ++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 26 deletions(-) diff --git a/include/fixed_queue.hpp b/include/fixed_queue.hpp index 73a8260..000e68a 100644 --- a/include/fixed_queue.hpp +++ b/include/fixed_queue.hpp @@ -7,6 +7,10 @@ template > class Fixe Container queue{}; public: + using const_iterator = typename Container::const_iterator; + using const_reverse_iterator = typename Container::const_reverse_iterator; + using const_reference = typename Container::const_reference; + FixedQueue() { for (auto i = 0; i < MaxLen; i++) { T default_value{}; diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index 50cf43c..2250681 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -38,7 +38,9 @@ class IQStreamDecoder { void upperMacWorker(); std::complex hard_decision(std::complex symbol); - std::vector symbols_to_bitstream(std::vector> const& stream); + + template + static void symbols_to_bitstream(iterator_type it, std::vector& bits, std::size_t len); void abs_convolve_same_length(const QueueT& queueA, const std::size_t offsetA, const std::complex* itb, const std::size_t len, float* res); diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index ce3540f..50c68b2 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -67,10 +67,9 @@ std::complex IQStreamDecoder::hard_decision(std::complex symbol) { } } -std::vector IQStreamDecoder::symbols_to_bitstream(std::vector> const& stream) { - std::vector bits; - - for (auto it = stream.begin(); it != stream.end(); it++) { +template +void IQStreamDecoder::symbols_to_bitstream(iterator_type it, std::vector& bits, std::size_t len) { + for (auto i = 0; i < len; ++it, ++i) { auto real = it->real(); auto imag = it->imag(); @@ -97,8 +96,6 @@ std::vector IQStreamDecoder::symbols_to_bitstream(std::vector symbol) noexcept { abs_convolve_same_length(symbol_buffer_hard_decision_, 44, training_seq_x_reversed_conj_.data(), training_seq_x_reversed_conj_.size(), &detectedX); - // use actual signal for further processing - auto start = symbol_buffer_.cbegin(); - if (detectedX >= SEQUENCE_DETECTION_THRESHOLD) { // std::cout << "Potential CUB found" << std::endl; - auto end = start; - std::advance(end, 103); + auto len = 103; + + std::vector bits; + bits.reserve(len * 2); + + symbols_to_bitstream(symbol_buffer_.cbegin(), bits, len); - auto corrected = channel_estimation({start, end}, training_seq_x_reversed_conj_); - auto bitstream = symbols_to_bitstream(corrected); - threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bitstream, BurstType::ControlUplinkBurst)); + threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bits, BurstType::ControlUplinkBurst)); } if (detectedP >= SEQUENCE_DETECTION_THRESHOLD) { // std::cout << "Potential NUB_Split found" << std::endl; - auto end = start; - std::advance(end, 231); + auto len = 231; - auto corrected = channel_estimation({start, end}, training_seq_p_reversed_conj_); - threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, symbols_to_bitstream(corrected), - BurstType::NormalUplinkBurstSplit)); + std::vector bits; + bits.reserve(len * 2); + + symbols_to_bitstream(symbol_buffer_.cbegin(), bits, len); + + threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bits, BurstType::NormalUplinkBurstSplit)); } if (detectedN >= SEQUENCE_DETECTION_THRESHOLD) { // std::cout << "Potential NUB found" << std::endl; - auto end = start; - std::advance(end, 231); + auto len = 231; + + std::vector bits; + bits.reserve(len * 2); + + symbols_to_bitstream(symbol_buffer_.cbegin(), bits, len); - auto corrected = channel_estimation({start, end}, training_seq_n_reversed_conj_); - threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, symbols_to_bitstream(corrected), - BurstType::NormalUplinkBurst)); + threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bits, BurstType::NormalUplinkBurst)); } } else { - auto bits = symbols_to_bitstream({symbol}); + std::vector> stream = {symbol}; + std::vector bits; + symbols_to_bitstream(stream.cbegin(), bits, 1); for (auto it = bits.begin(); it != bits.end(); ++it) { bit_stream_decoder_->process_bit(*it); } From 2057a1b77f28d4b35b22de2fc77f55d39cfde2ad Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sat, 27 Jan 2024 18:21:44 +0100 Subject: [PATCH 24/35] add more performance improvements --- CMakeLists.txt | 2 +- include/iq_stream_decoder.hpp | 8 +-- src/iq_stream_decoder.cpp | 67 +++++++++++-------- ...ng_ordered_output_thread_pool_executor.cpp | 8 ++- 4 files changed, 52 insertions(+), 33 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 674136d..eeba6da 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -31,7 +31,7 @@ add_executable(tetra-viterbi src/examples/viter_bi_codec.cpp src/examples/tetra_viterbi.cpp) -target_compile_options(tetra PUBLIC -std=c++17 -Wall -Wno-unused-variable -msse4.1) +target_compile_options(tetra PUBLIC -std=c++17 -Wall -Wno-unused-variable -msse4.1 -O3) target_compile_options(tetra-puncturing PUBLIC -std=c++17 -Wall -Wno-unused-variable) target_compile_options(tetra-viterbi PUBLIC -std=c++17 -Wall -Wno-unused-variable) diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index 2250681..e3c327a 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -37,13 +37,13 @@ class IQStreamDecoder { void upperMacWorker(); - std::complex hard_decision(std::complex symbol); + static std::complex hard_decision(std::complex const& symbol); template - static void symbols_to_bitstream(iterator_type it, std::vector& bits, std::size_t len); + static void symbols_to_bitstream(iterator_type it, uint8_t* const bits, const std::size_t len); - void abs_convolve_same_length(const QueueT& queueA, const std::size_t offsetA, const std::complex* itb, - const std::size_t len, float* res); + static void abs_convolve_same_length(const QueueT& queueA, const std::size_t offsetA, + const std::complex* itb, const std::size_t len, float* res); std::vector> channel_estimation(std::vector> const& stream, std::vector> const& pilots); diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index 50c68b2..80353d8 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -37,21 +37,28 @@ IQStreamDecoder::IQStreamDecoder(std::shared_ptr lower_mac, } IQStreamDecoder::~IQStreamDecoder() { + // the last work packet has been passed into the StreamingOrderedOutputThreadPoolExecutor. + // wait until the work is finished and its input and output queue is drained. + // // TODO: replace this crude hack that keeps the StreamingOrderedOutputThreadPoolExecutor<...> get function from // blocking on programm stop - threadPool_->queueWork([]() { return std::vector>(); }); + threadPool_->queueWork([]() { return std::vector>({std::function()}); }); upperMacWorkerThread_.join(); } void IQStreamDecoder::upperMacWorker() { - while (!stop) { - for (auto func : threadPool_->get()) { + for (;;) { + auto work_funcs = threadPool_->get(); + for (auto func : work_funcs) { + // exit when we get the special zero work exit function + if (!func) + return; func(); } } } -std::complex IQStreamDecoder::hard_decision(std::complex symbol) { +std::complex IQStreamDecoder::hard_decision(std::complex const& symbol) { if (symbol.real() > 0) { if (symbol.imag() > 0) { return std::complex(1, 1); @@ -68,33 +75,37 @@ std::complex IQStreamDecoder::hard_decision(std::complex symbol) { } template -void IQStreamDecoder::symbols_to_bitstream(iterator_type it, std::vector& bits, std::size_t len) { +void IQStreamDecoder::symbols_to_bitstream(iterator_type it, uint8_t* const bits, const std::size_t len) { for (auto i = 0; i < len; ++it, ++i) { auto real = it->real(); auto imag = it->imag(); + uint8_t symb0, symb1; if (real > 0.0) { if (imag > 0.0) { // I - bits.push_back(0); - bits.push_back(0); + symb0 = 0; + symb1 = 0; } else { // IV - bits.push_back(1); - bits.push_back(0); + symb0 = 1; + symb1 = 0; } } else { if (imag > 0.0) { // II - bits.push_back(0); - bits.push_back(1); + symb0 = 0; + symb1 = 1; } else { // III - bits.push_back(1); - bits.push_back(1); + symb0 = 1; + symb1 = 1; } } + + bits[i * 2] = symb0; + bits[i * 2 + 1] = symb1; } } @@ -145,12 +156,12 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { auto len = 103; - std::vector bits; - bits.reserve(len * 2); + std::vector bits(len * 2); - symbols_to_bitstream(symbol_buffer_.cbegin(), bits, len); + symbols_to_bitstream(symbol_buffer_.cbegin(), bits.data(), len); - threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bits, BurstType::ControlUplinkBurst)); + auto lower_mac_process_cub = std::bind(&LowerMac::process, lower_mac_, bits, BurstType::ControlUplinkBurst); + threadPool_->queueWork(lower_mac_process_cub); } if (detectedP >= SEQUENCE_DETECTION_THRESHOLD) { @@ -158,12 +169,13 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { auto len = 231; - std::vector bits; - bits.reserve(len * 2); + std::vector bits(len * 2); - symbols_to_bitstream(symbol_buffer_.cbegin(), bits, len); + symbols_to_bitstream(symbol_buffer_.cbegin(), bits.data(), len); - threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bits, BurstType::NormalUplinkBurstSplit)); + auto lower_mac_process_nubs = + std::bind(&LowerMac::process, lower_mac_, bits, BurstType::NormalUplinkBurstSplit); + threadPool_->queueWork(lower_mac_process_nubs); } if (detectedN >= SEQUENCE_DETECTION_THRESHOLD) { @@ -171,17 +183,18 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { auto len = 231; - std::vector bits; - bits.reserve(len * 2); + std::vector bits(len * 2); - symbols_to_bitstream(symbol_buffer_.cbegin(), bits, len); + symbols_to_bitstream(symbol_buffer_.cbegin(), bits.data(), len); - threadPool_->queueWork(std::bind(&LowerMac::process, lower_mac_, bits, BurstType::NormalUplinkBurst)); + auto lower_mac_process_nub = std::bind(&LowerMac::process, lower_mac_, bits, BurstType::NormalUplinkBurst); + threadPool_->queueWork(lower_mac_process_nub); } } else { + // TODO: this path needs to change! std::vector> stream = {symbol}; - std::vector bits; - symbols_to_bitstream(stream.cbegin(), bits, 1); + std::vector bits(2); + symbols_to_bitstream(stream.cbegin(), bits.data(), 1); for (auto it = bits.begin(); it != bits.end(); ++it) { bit_stream_decoder_->process_bit(*it); } diff --git a/src/streaming_ordered_output_thread_pool_executor.cpp b/src/streaming_ordered_output_thread_pool_executor.cpp index 0aaba20..90ae0b9 100644 --- a/src/streaming_ordered_output_thread_pool_executor.cpp +++ b/src/streaming_ordered_output_thread_pool_executor.cpp @@ -39,9 +39,15 @@ StreamingOrderedOutputThreadPoolExecutor::~StreamingOrderedOutputThr } template void StreamingOrderedOutputThreadPoolExecutor::worker() { - while (!stop) { + for (;;) { std::optional>> work{}; + if (stop) { + std::lock_guard lk(cv_input_item_m); + if (inputQueue.size() == 0) + break; + } + { std::lock_guard lk(cv_input_item_m); if (!inputQueue.empty()) { From 722a1faa44646366f14cfe56fb4de719df9cc0f5 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sat, 27 Jan 2024 20:06:37 +0100 Subject: [PATCH 25/35] Refactor lower mac coding --- include/l2/lower_mac.hpp | 15 +- src/l2/lower_mac_coding.cpp | 337 ++++++++++++++++++------------------ 2 files changed, 176 insertions(+), 176 deletions(-) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index aa4b6e0..6229513 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -37,17 +37,18 @@ class LowerMac { std::shared_ptr viter_bi_codec_1614_{}; std::shared_ptr upper_mac_{}; - [[nodiscard]] static auto descramble(const std::vector& data, int len, uint32_t scramblingCode) noexcept - -> std::vector; - [[nodiscard]] static auto deinterleave(const std::vector& data, uint32_t K, uint32_t a) noexcept - -> std::vector; - [[nodiscard]] static auto depuncture23(const std::vector& data, uint32_t len) noexcept + [[nodiscard]] static auto descramble(const std::vector& data, const std::size_t len, + const uint32_t scramblingCode) noexcept -> std::vector; + [[nodiscard]] static auto deinterleave(const std::vector& data, const std::size_t K, + const std::size_t a) noexcept -> std::vector; + [[nodiscard]] static auto depuncture23(const std::vector& data, const uint32_t len) noexcept -> std::vector; [[nodiscard]] static auto reed_muller_3014_decode(const std::vector& data) noexcept -> std::vector; - [[nodiscard]] static auto check_crc_16_ccitt(const std::vector& data, int len) noexcept -> int; + [[nodiscard]] static auto check_crc_16_ccitt(const std::vector& data, const std::size_t len) noexcept + -> bool; - [[nodiscard]] auto viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector; + [[nodiscard]] auto viter_bi_decode_1614(const std::vector& data) const noexcept -> std::vector; }; #endif diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index 9b414c5..35494d9 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -23,24 +23,25 @@ * */ -auto LowerMac::descramble(const std::vector& data, int len, uint32_t scramblingCode) noexcept - -> std::vector { +auto LowerMac::descramble(const std::vector& data, const std::size_t len, + const uint32_t scramblingCode) noexcept -> std::vector { const uint8_t poly[14] = {32, 26, 23, 22, 16, 12, 11, 10, 8, 7, 5, 4, 2, 1}; // Feedback polynomial - see 8.2.5.2 (8.39) - std::vector res; + std::vector res(len); uint32_t lfsr = scramblingCode; // linear feedback shift register initialization (=0 + 3 // for BSCH, calculated from Color code ch 19 otherwise) - for (int i = 0; i < len; i++) { - uint32_t bit = lfsr >> (32 - poly[0]); // apply poly (Xj + ...) - for (int j = 1; j < 14; j++) { + for (std::size_t i = 0; i < len; i++) { + uint32_t bit = 0; + // apply poly (Xj + ...) + for (std::size_t j = 0; j < 14; j++) { bit = bit ^ (lfsr >> (32 - poly[j])); } bit = bit & 1; // finish apply feedback polynomial (+ 1) lfsr = (lfsr >> 1) | (bit << 31); - res.push_back(data[i] ^ (bit & 0xff)); + res[i] = data[i] ^ (bit & 0xff); } return res; @@ -50,13 +51,13 @@ auto LowerMac::descramble(const std::vector& data, int len, uint32_t sc * @brief (K,a) block deinterleaver - 8.2.4 * */ -auto LowerMac::deinterleave(const std::vector& data, const uint32_t K, const uint32_t a) noexcept +auto LowerMac::deinterleave(const std::vector& data, const std::size_t K, const std::size_t a) noexcept -> std::vector { std::vector res(K, 0); // output vector is size K - for (unsigned int idx = 1; idx <= K; idx++) { - uint32_t k = 1 + (a * idx) % K; - res[idx - 1] = data[k - 1]; // to interleave: DataOut[i-1] = DataIn[k-1] + for (std::size_t i = 0; i < K; i++) { + auto k = 1 + (a * (i + 1)) % K; + res[i] = data[k - 1]; // to interleave: DataOut[i-1] = DataIn[k-1] } return res; @@ -89,14 +90,12 @@ auto LowerMac::depuncture23(const std::vector& data, const uint32_t len * - 8.2.3.1.1 * */ - -auto LowerMac::viter_bi_decode_1614(const std::vector& data) noexcept -> std::vector { - std::vector out{}; - for (auto elem : viter_bi_codec_1614_->Decode(data)) { - for (auto i = 0; i < 8; i++) { - out.push_back((elem & (1 << (7 - i))) ? 1 : 0); - } - } +auto LowerMac::viter_bi_decode_1614(const std::vector& data) const noexcept -> std::vector { + auto decoded = viter_bi_codec_1614_->Decode(data); + std::vector out(decoded.size() * 8); + for (auto i = 0; i < decoded.size(); i++) + for (auto j = 0; j < 8; j++) + out[i * 8 + j] = (decoded[i] & (1 << (7 - j))) ? 1 : 0; return out; } @@ -109,182 +108,182 @@ auto LowerMac::viter_bi_decode_1614(const std::vector& data) noexcept - */ auto LowerMac::reed_muller_3014_decode(const std::vector& data) noexcept -> std::vector { - uint8_t q[5]; + uint8_t q[14][5]; std::vector res(14); - q[0] = data[0]; - q[1] = (data[13 + 3] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 11]) % 2; - q[2] = (data[13 + 1] + data[13 + 2] + data[13 + 5] + data[13 + 6] + data[13 + 8] + data[13 + 9]) % 2; - q[3] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 9] + data[13 + 10]) % 2; - q[4] = + q[0][0] = data[0]; + q[0][1] = (data[13 + 3] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 11]) % 2; + q[0][2] = (data[13 + 1] + data[13 + 2] + data[13 + 5] + data[13 + 6] + data[13 + 8] + data[13 + 9]) % 2; + q[0][3] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 9] + data[13 + 10]) % 2; + q[0][4] = (data[13 + 1] + data[13 + 4] + data[13 + 5] + data[13 + 7] + data[13 + 8] + data[13 + 10] + data[13 + 11]) % 2; - res[0] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; + res[0] = (q[0][0] + q[0][1] + q[0][2] + q[0][3] + q[0][4]) >= 3 ? 1 : 0; - q[0] = data[1]; - q[1] = (data[13 + 1] + data[13 + 4] + data[13 + 5] + data[13 + 9] + data[13 + 11]) % 2; - q[2] = (data[13 + 1] + data[13 + 2] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 10]) % 2; - q[3] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 7] + data[13 + 8]) % 2; - q[4] = + q[1][0] = data[1]; + q[1][1] = (data[13 + 1] + data[13 + 4] + data[13 + 5] + data[13 + 9] + data[13 + 11]) % 2; + q[1][2] = (data[13 + 1] + data[13 + 2] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 10]) % 2; + q[1][3] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 7] + data[13 + 8]) % 2; + q[1][4] = (data[13 + 3] + data[13 + 5] + data[13 + 6] + data[13 + 8] + data[13 + 9] + data[13 + 10] + data[13 + 11]) % 2; - res[1] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; + res[1] = (q[1][0] + q[1][1] + q[1][2] + q[1][3] + q[1][4]) >= 3 ? 1 : 0; - q[0] = data[2]; - q[1] = (data[13 + 2] + data[13 + 5] + data[13 + 8] + data[13 + 10] + data[13 + 11]) % 2; - q[2] = (data[13 + 1] + data[13 + 3] + data[13 + 5] + data[13 + 7] + data[13 + 9] + data[13 + 10]) % 2; - q[3] = (data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 9]) % 2; - q[4] = + q[2][0] = data[2]; + q[2][1] = (data[13 + 2] + data[13 + 5] + data[13 + 8] + data[13 + 10] + data[13 + 11]) % 2; + q[2][2] = (data[13 + 1] + data[13 + 3] + data[13 + 5] + data[13 + 7] + data[13 + 9] + data[13 + 10]) % 2; + q[2][3] = (data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 9]) % 2; + q[2][4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 11]) % 2; - res[2] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; + res[2] = (q[2][0] + q[2][1] + q[2][2] + q[2][3] + q[2][4]) >= 3 ? 1 : 0; - q[0] = data[3]; - q[1] = (data[13 + 7] + data[13 + 8] + data[13 + 9] + data[13 + 12] + data[13 + 13] + data[13 + 14]) % 2; - q[2] = + q[3][0] = data[3]; + q[3][1] = (data[13 + 7] + data[13 + 8] + data[13 + 9] + data[13 + 12] + data[13 + 13] + data[13 + 14]) % 2; + q[3][2] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 11] + data[13 + 12] + data[13 + 13] + data[13 + 14]) % 2; - q[3] = (data[13 + 2] + data[13 + 4] + data[13 + 6] + data[13 + 8] + data[13 + 10] + data[13 + 11] + data[13 + 12] + - data[13 + 13] + data[13 + 14]) % - 2; - q[4] = (data[13 + 1] + data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 9] + data[13 + 10] + - data[13 + 12] + data[13 + 13] + data[13 + 14]) % - 2; - res[3] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[4]; - q[1] = + q[3][3] = (data[13 + 2] + data[13 + 4] + data[13 + 6] + data[13 + 8] + data[13 + 10] + data[13 + 11] + + data[13 + 12] + data[13 + 13] + data[13 + 14]) % + 2; + q[3][4] = (data[13 + 1] + data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 9] + data[13 + 10] + + data[13 + 12] + data[13 + 13] + data[13 + 14]) % + 2; + res[3] = (q[3][0] + q[3][1] + q[3][2] + q[3][3] + q[3][4]) >= 3 ? 1 : 0; + + q[4][0] = data[4]; + q[4][1] = (data[13 + 1] + data[13 + 4] + data[13 + 5] + data[13 + 11] + data[13 + 12] + data[13 + 13] + data[13 + 15]) % 2; - q[2] = (data[13 + 3] + data[13 + 5] + data[13 + 6] + data[13 + 8] + data[13 + 10] + data[13 + 11] + data[13 + 12] + - data[13 + 13] + data[13 + 15]) % - 2; - q[3] = (data[13 + 1] + data[13 + 2] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 9] + data[13 + 10] + - data[13 + 12] + data[13 + 13] + data[13 + 15]) % - 2; - q[4] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 7] + data[13 + 8] + data[13 + 9] + - data[13 + 12] + data[13 + 13] + data[13 + 15]) % - 2; - res[4] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[5]; - q[1] = (data[13 + 7] + data[13 + 9] + data[13 + 10] + data[13 + 12] + data[13 + 14] + data[13 + 15]) % 2; - q[2] = + q[4][2] = (data[13 + 3] + data[13 + 5] + data[13 + 6] + data[13 + 8] + data[13 + 10] + data[13 + 11] + + data[13 + 12] + data[13 + 13] + data[13 + 15]) % + 2; + q[4][3] = (data[13 + 1] + data[13 + 2] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 9] + data[13 + 10] + + data[13 + 12] + data[13 + 13] + data[13 + 15]) % + 2; + q[4][4] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 7] + data[13 + 8] + data[13 + 9] + + data[13 + 12] + data[13 + 13] + data[13 + 15]) % + 2; + res[4] = (q[4][0] + q[4][1] + q[4][2] + q[4][3] + q[4][4]) >= 3 ? 1 : 0; + + q[5][0] = data[5]; + q[5][1] = (data[13 + 7] + data[13 + 9] + data[13 + 10] + data[13 + 12] + data[13 + 14] + data[13 + 15]) % 2; + q[5][2] = (data[13 + 2] + data[13 + 4] + data[13 + 6] + data[13 + 11] + data[13 + 12] + data[13 + 14] + data[13 + 15]) % 2; - q[3] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 8] + data[13 + 10] + data[13 + 11] + data[13 + 12] + - data[13 + 14] + data[13 + 15]) % - 2; - q[4] = (data[13 + 1] + data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 9] + - data[13 + 12] + data[13 + 14] + data[13 + 15]) % - 2; - res[5] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[6]; - q[1] = + q[5][3] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 8] + data[13 + 10] + data[13 + 11] + + data[13 + 12] + data[13 + 14] + data[13 + 15]) % + 2; + q[5][4] = (data[13 + 1] + data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 9] + + data[13 + 12] + data[13 + 14] + data[13 + 15]) % + 2; + res[5] = (q[5][0] + q[5][1] + q[5][2] + q[5][3] + q[5][4]) >= 3 ? 1 : 0; + + q[6][0] = data[6]; + q[6][1] = (data[13 + 3] + data[13 + 5] + data[13 + 6] + data[13 + 11] + data[13 + 13] + data[13 + 14] + data[13 + 15]) % 2; - q[2] = (data[13 + 1] + data[13 + 4] + data[13 + 5] + data[13 + 8] + data[13 + 10] + data[13 + 11] + data[13 + 13] + - data[13 + 14] + data[13 + 15]) % - 2; - q[3] = (data[13 + 1] + data[13 + 2] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 9] + - data[13 + 13] + data[13 + 14] + data[13 + 15]) % - 2; - q[4] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 7] + data[13 + 9] + data[13 + 10] + - data[13 + 13] + data[13 + 14] + data[13 + 15]) % - 2; - res[6] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[7]; - q[1] = (data[13 + 2] + data[13 + 5] + data[13 + 7] + data[13 + 9] + data[13 + 12] + data[13 + 13] + data[13 + 14] + - data[13 + 15] + data[13 + 16]) % - 2; - q[2] = (data[13 + 1] + data[13 + 3] + data[13 + 5] + data[13 + 8] + data[13 + 11] + data[13 + 12] + data[13 + 13] + - data[13 + 14] + data[13 + 15] + data[13 + 16]) % - 2; - q[3] = (data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 10] + data[13 + 11] + data[13 + 12] + data[13 + 13] + - data[13 + 14] + data[13 + 15] + data[13 + 16]) % - 2; - q[4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 7] + - data[13 + 8] + data[13 + 9] + data[13 + 10] + data[13 + 12] + data[13 + 13] + data[13 + 14] + - data[13 + 15] + data[13 + 16]) % - 2; - res[7] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[8]; - q[1] = (data[13 + 2] + data[13 + 3] + data[13 + 9] + data[13 + 12] + data[13 + 13] + data[13 + 16]) % 2; - q[2] = + q[6][2] = (data[13 + 1] + data[13 + 4] + data[13 + 5] + data[13 + 8] + data[13 + 10] + data[13 + 11] + + data[13 + 13] + data[13 + 14] + data[13 + 15]) % + 2; + q[6][3] = (data[13 + 1] + data[13 + 2] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 9] + + data[13 + 13] + data[13 + 14] + data[13 + 15]) % + 2; + q[6][4] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 7] + data[13 + 9] + data[13 + 10] + + data[13 + 13] + data[13 + 14] + data[13 + 15]) % + 2; + res[6] = (q[6][0] + q[6][1] + q[6][2] + q[6][3] + q[6][4]) >= 3 ? 1 : 0; + + q[7][0] = data[7]; + q[7][1] = (data[13 + 2] + data[13 + 5] + data[13 + 7] + data[13 + 9] + data[13 + 12] + data[13 + 13] + + data[13 + 14] + data[13 + 15] + data[13 + 16]) % + 2; + q[7][2] = (data[13 + 1] + data[13 + 3] + data[13 + 5] + data[13 + 8] + data[13 + 11] + data[13 + 12] + + data[13 + 13] + data[13 + 14] + data[13 + 15] + data[13 + 16]) % + 2; + q[7][3] = (data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 10] + data[13 + 11] + data[13 + 12] + + data[13 + 13] + data[13 + 14] + data[13 + 15] + data[13 + 16]) % + 2; + q[7][4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 7] + + data[13 + 8] + data[13 + 9] + data[13 + 10] + data[13 + 12] + data[13 + 13] + data[13 + 14] + + data[13 + 15] + data[13 + 16]) % + 2; + res[7] = (q[7][0] + q[7][1] + q[7][2] + q[7][3] + q[7][4]) >= 3 ? 1 : 0; + + q[8][0] = data[8]; + q[8][1] = (data[13 + 2] + data[13 + 3] + data[13 + 9] + data[13 + 12] + data[13 + 13] + data[13 + 16]) % 2; + q[8][2] = (data[13 + 1] + data[13 + 7] + data[13 + 8] + data[13 + 11] + data[13 + 12] + data[13 + 13] + data[13 + 16]) % 2; - q[3] = (data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 10] + data[13 + 11] + data[13 + 12] + - data[13 + 13] + data[13 + 16]) % - 2; - q[4] = (data[13 + 1] + data[13 + 2] + data[13 + 4] + data[13 + 6] + data[13 + 8] + data[13 + 9] + data[13 + 10] + - data[13 + 12] + data[13 + 13] + data[13 + 16]) % - 2; - res[8] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[9]; - q[1] = (data[13 + 1] + data[13 + 3] + data[13 + 8] + data[13 + 12] + data[13 + 14] + data[13 + 16]) % 2; - q[2] = (data[13 + 4] + data[13 + 6] + data[13 + 10] + data[13 + 12] + data[13 + 14] + data[13 + 16]) % 2; - q[3] = + q[8][3] = (data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 10] + data[13 + 11] + + data[13 + 12] + data[13 + 13] + data[13 + 16]) % + 2; + q[8][4] = (data[13 + 1] + data[13 + 2] + data[13 + 4] + data[13 + 6] + data[13 + 8] + data[13 + 9] + data[13 + 10] + + data[13 + 12] + data[13 + 13] + data[13 + 16]) % + 2; + res[8] = (q[8][0] + q[8][1] + q[8][2] + q[8][3] + q[8][4]) >= 3 ? 1 : 0; + + q[9][0] = data[9]; + q[9][1] = (data[13 + 1] + data[13 + 3] + data[13 + 8] + data[13 + 12] + data[13 + 14] + data[13 + 16]) % 2; + q[9][2] = (data[13 + 4] + data[13 + 6] + data[13 + 10] + data[13 + 12] + data[13 + 14] + data[13 + 16]) % 2; + q[9][3] = (data[13 + 2] + data[13 + 7] + data[13 + 9] + data[13 + 11] + data[13 + 12] + data[13 + 14] + data[13 + 16]) % 2; - q[4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 8] + - data[13 + 9] + data[13 + 10] + data[13 + 11] + data[13 + 12] + data[13 + 14] + data[13 + 16]) % - 2; - res[9] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[10]; - q[1] = (data[13 + 1] + data[13 + 2] + data[13 + 7] + data[13 + 13] + data[13 + 14] + data[13 + 16]) % 2; - q[2] = + q[9][4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 8] + + data[13 + 9] + data[13 + 10] + data[13 + 11] + data[13 + 12] + data[13 + 14] + data[13 + 16]) % + 2; + res[9] = (q[9][0] + q[9][1] + q[9][2] + q[9][3] + q[9][4]) >= 3 ? 1 : 0; + + q[10][0] = data[10]; + q[10][1] = (data[13 + 1] + data[13 + 2] + data[13 + 7] + data[13 + 13] + data[13 + 14] + data[13 + 16]) % 2; + q[10][2] = (data[13 + 3] + data[13 + 8] + data[13 + 9] + data[13 + 11] + data[13 + 13] + data[13 + 14] + data[13 + 16]) % 2; - q[3] = (data[13 + 1] + data[13 + 4] + data[13 + 6] + data[13 + 9] + data[13 + 10] + data[13 + 11] + data[13 + 13] + - data[13 + 14] + data[13 + 16]) % - 2; - q[4] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 10] + - data[13 + 13] + data[13 + 14] + data[13 + 16]) % - 2; - res[10] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[11]; - q[1] = (data[13 + 2] + data[13 + 6] + data[13 + 9] + data[13 + 12] + data[13 + 15] + data[13 + 16]) % 2; - q[2] = + q[10][3] = (data[13 + 1] + data[13 + 4] + data[13 + 6] + data[13 + 9] + data[13 + 10] + data[13 + 11] + + data[13 + 13] + data[13 + 14] + data[13 + 16]) % + 2; + q[10][4] = (data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 6] + data[13 + 7] + data[13 + 8] + + data[13 + 10] + data[13 + 13] + data[13 + 14] + data[13 + 16]) % + 2; + res[10] = (q[10][0] + q[10][1] + q[10][2] + q[10][3] + q[10][4]) >= 3 ? 1 : 0; + + q[11][0] = data[11]; + q[11][1] = (data[13 + 2] + data[13 + 6] + data[13 + 9] + data[13 + 12] + data[13 + 15] + data[13 + 16]) % 2; + q[11][2] = (data[13 + 4] + data[13 + 7] + data[13 + 10] + data[13 + 11] + data[13 + 12] + data[13 + 15] + data[13 + 16]) % 2; - q[3] = (data[13 + 1] + data[13 + 3] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 11] + data[13 + 12] + - data[13 + 15] + data[13 + 16]) % - 2; - q[4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 8] + data[13 + 9] + data[13 + 10] + - data[13 + 12] + data[13 + 15] + data[13 + 16]) % - 2; - res[11] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[12]; - q[1] = + q[11][3] = (data[13 + 1] + data[13 + 3] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 11] + + data[13 + 12] + data[13 + 15] + data[13 + 16]) % + 2; + q[11][4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 4] + data[13 + 8] + data[13 + 9] + + data[13 + 10] + data[13 + 12] + data[13 + 15] + data[13 + 16]) % + 2; + res[11] = (q[11][0] + q[11][1] + q[11][2] + q[11][3] + q[11][4]) >= 3 ? 1 : 0; + + q[12][0] = data[12]; + q[12][1] = (data[13 + 5] + data[13 + 8] + data[13 + 10] + data[13 + 11] + data[13 + 13] + data[13 + 15] + data[13 + 16]) % 2; - q[2] = (data[13 + 1] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 11] + data[13 + 13] + - data[13 + 15] + data[13 + 16]) % - 2; - q[3] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 5] + data[13 + 7] + data[13 + 9] + data[13 + 10] + - data[13 + 13] + data[13 + 15] + data[13 + 16]) % - 2; - q[4] = (data[13 + 2] + data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 9] + - data[13 + 13] + data[13 + 15] + data[13 + 16]) % - 2; - res[12] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; - - q[0] = data[13]; - q[1] = (data[13 + 2] + data[13 + 4] + data[13 + 7] + data[13 + 14] + data[13 + 15] + data[13 + 16]) % 2; - q[2] = + q[12][2] = (data[13 + 1] + data[13 + 3] + data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 11] + + data[13 + 13] + data[13 + 15] + data[13 + 16]) % + 2; + q[12][3] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 5] + data[13 + 7] + data[13 + 9] + + data[13 + 10] + data[13 + 13] + data[13 + 15] + data[13 + 16]) % + 2; + q[12][4] = (data[13 + 2] + data[13 + 4] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 9] + + data[13 + 13] + data[13 + 15] + data[13 + 16]) % + 2; + res[12] = (q[12][0] + q[12][1] + q[12][2] + q[12][3] + q[12][4]) >= 3 ? 1 : 0; + + q[13][0] = data[13]; + q[13][1] = (data[13 + 2] + data[13 + 4] + data[13 + 7] + data[13 + 14] + data[13 + 15] + data[13 + 16]) % 2; + q[13][2] = (data[13 + 6] + data[13 + 9] + data[13 + 10] + data[13 + 11] + data[13 + 14] + data[13 + 15] + data[13 + 16]) % 2; - q[3] = (data[13 + 1] + data[13 + 3] + data[13 + 4] + data[13 + 8] + data[13 + 9] + data[13 + 11] + data[13 + 14] + - data[13 + 15] + data[13 + 16]) % - 2; - q[4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 6] + data[13 + 7] + data[13 + 8] + data[13 + 10] + - data[13 + 14] + data[13 + 15] + data[13 + 16]) % - 2; - res[13] = (q[0] + q[1] + q[2] + q[3] + q[4]) >= 3 ? 1 : 0; + q[13][3] = (data[13 + 1] + data[13 + 3] + data[13 + 4] + data[13 + 8] + data[13 + 9] + data[13 + 11] + + data[13 + 14] + data[13 + 15] + data[13 + 16]) % + 2; + q[13][4] = (data[13 + 1] + data[13 + 2] + data[13 + 3] + data[13 + 6] + data[13 + 7] + data[13 + 8] + + data[13 + 10] + data[13 + 14] + data[13 + 15] + data[13 + 16]) % + 2; + res[13] = (q[13][0] + q[13][1] + q[13][2] + q[13][3] + q[13][4]) >= 3 ? 1 : 0; // check deviation from input // int deviation = 0; @@ -305,10 +304,10 @@ auto LowerMac::reed_muller_3014_decode(const std::vector& data) noexcep * @brief Calculated CRC16 ITU-T X.25 - CCITT * */ -auto LowerMac::check_crc_16_ccitt(const std::vector& data, int len) noexcept -> int { +auto LowerMac::check_crc_16_ccitt(const std::vector& data, const std::size_t len) noexcept -> bool { uint16_t crc = 0xFFFF; // CRC16-CCITT initial value - for (int i = 0; i < len; i++) { + for (std::size_t i = 0; i < len; i++) { auto bit = static_cast(data[i]); crc ^= bit << 15; From 53830b8c4f5ee6ed070b48255c0b3d869eed06ef Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 28 Jan 2024 13:44:44 +0100 Subject: [PATCH 26/35] precompute descrambling table --- src/l2/lower_mac_coding.cpp | 40 +++++++++++++++++++++++++------------ 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index 35494d9..87cbf73 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -22,28 +22,42 @@ * @brief Fibonacci LFSR descrambling - 8.2.5 * */ +static void xor_kernel(uint8_t* const res, const uint8_t* const data, const uint8_t* const table, + const std::size_t len) { + for (std::size_t i = 0; i < len; i++) + res[i] = data[i] ^ table[i]; +} auto LowerMac::descramble(const std::vector& data, const std::size_t len, const uint32_t scramblingCode) noexcept -> std::vector { - const uint8_t poly[14] = {32, 26, 23, 22, 16, 12, 11, - 10, 8, 7, 5, 4, 2, 1}; // Feedback polynomial - see 8.2.5.2 (8.39) + static std::vector table; std::vector res(len); - uint32_t lfsr = scramblingCode; // linear feedback shift register initialization (=0 + 3 - // for BSCH, calculated from Color code ch 19 otherwise) - for (std::size_t i = 0; i < len; i++) { - uint32_t bit = 0; - // apply poly (Xj + ...) - for (std::size_t j = 0; j < 14; j++) { - bit = bit ^ (lfsr >> (32 - poly[j])); + assert(len <= 432); + + if (table.size() == 0) { + table.resize(432); + const uint8_t poly[14] = {32, 26, 23, 22, 16, 12, 11, + 10, 8, 7, 5, 4, 2, 1}; // Feedback polynomial - see 8.2.5.2 (8.39) + + uint32_t lfsr = scramblingCode; // linear feedback shift register initialization (=0 + 3 + // for BSCH, calculated from Color code ch 19 otherwise) + for (std::size_t i = 0; i < 432; i++) { + uint32_t bit = 0; + // apply poly (Xj + ...) + for (std::size_t j = 0; j < 14; j++) { + bit = bit ^ (lfsr >> (32 - poly[j])); + } + bit = bit & 1; // finish apply feedback polynomial (+ 1) + lfsr = (lfsr >> 1) | (bit << 31); + + table[i] = bit & 0xff; } - bit = bit & 1; // finish apply feedback polynomial (+ 1) - lfsr = (lfsr >> 1) | (bit << 31); - - res[i] = data[i] ^ (bit & 0xff); } + xor_kernel(res.data(), data.data(), table.data(), len); + return res; } From 643fd8b4d8ae24fe3c67ded64dd77db8a98e0da9 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 28 Jan 2024 15:21:56 +0100 Subject: [PATCH 27/35] fix bug in thread pool executor --- ...ng_ordered_output_thread_pool_executor.cpp | 44 ++++++++++--------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/src/streaming_ordered_output_thread_pool_executor.cpp b/src/streaming_ordered_output_thread_pool_executor.cpp index 90ae0b9..c9060bd 100644 --- a/src/streaming_ordered_output_thread_pool_executor.cpp +++ b/src/streaming_ordered_output_thread_pool_executor.cpp @@ -42,18 +42,13 @@ template void StreamingOrderedOutputThreadPoolExecutor>> work{}; - if (stop) { - std::lock_guard lk(cv_input_item_m); - if (inputQueue.size() == 0) - break; - } - { std::lock_guard lk(cv_input_item_m); if (!inputQueue.empty()) { work = inputQueue.front(); inputQueue.pop_front(); - } + } else if (stop) + break; } if (!work.has_value()) { @@ -94,30 +89,37 @@ void StreamingOrderedOutputThreadPoolExecutor::queueWork(std::functi } template ReturnType StreamingOrderedOutputThreadPoolExecutor::get() { - std::optional result{}; + for (;;) { + std::optional result{}; + + { + std::lock_guard lk(cv_output_item_m); - while (!result.has_value()) { - std::unique_lock lk(cv_output_item_m); - cv_output_item.wait_for(lk, 10ms, [&] { - // find the output item and if found set outputCounter_ to the next item if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { result = search->second; outputMap.erase(search); outputCounter++; - return true; } + } - return false; - }); + if (!result.has_value()) { + std::unique_lock lk(cv_output_item_m); + auto res = cv_output_item.wait_for(lk, 10ms, [&] { + // find the output item and if found set outputCounter_ to the next item + if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { + result = search->second; + outputMap.erase(search); + outputCounter++; + return true; + } - if (auto search = outputMap.find(outputCounter); search != outputMap.end()) { - result = search->second; - outputMap.erase(search); - outputCounter++; + return false; + }); } - } - return *result; + if (result.has_value()) + return *result; + } } template class StreamingOrderedOutputThreadPoolExecutor>>; From 83f80ccc0a01cee5bd6278388ca599143b78b439 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 29 Jan 2024 01:21:35 +0100 Subject: [PATCH 28/35] move to raw pointers in performance critical regions --- include/l2/lower_mac.hpp | 14 ++- src/l2/lower_mac.cpp | 169 ++++++++++++++++-------------------- src/l2/lower_mac_coding.cpp | 14 +-- 3 files changed, 89 insertions(+), 108 deletions(-) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index 6229513..3df3637 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -37,16 +37,14 @@ class LowerMac { std::shared_ptr viter_bi_codec_1614_{}; std::shared_ptr upper_mac_{}; - [[nodiscard]] static auto descramble(const std::vector& data, const std::size_t len, + [[nodiscard]] static auto descramble(const uint8_t* const data, const std::size_t len, const uint32_t scramblingCode) noexcept -> std::vector; - [[nodiscard]] static auto deinterleave(const std::vector& data, const std::size_t K, - const std::size_t a) noexcept -> std::vector; - [[nodiscard]] static auto depuncture23(const std::vector& data, const uint32_t len) noexcept - -> std::vector; - [[nodiscard]] static auto reed_muller_3014_decode(const std::vector& data) noexcept + [[nodiscard]] static auto deinterleave(const uint8_t* const data, const std::size_t K, const std::size_t a) noexcept -> std::vector; - [[nodiscard]] static auto check_crc_16_ccitt(const std::vector& data, const std::size_t len) noexcept - -> bool; + [[nodiscard]] static auto depuncture23(const uint8_t* const data, const uint32_t len) noexcept + -> std::vector; + [[nodiscard]] static auto reed_muller_3014_decode(const uint8_t* const data) noexcept -> std::vector; + [[nodiscard]] static auto check_crc_16_ccitt(const uint8_t* const data, const std::size_t len) noexcept -> bool; [[nodiscard]] auto viter_bi_decode_1614(const std::vector& data) const noexcept -> std::vector; }; diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 735448a..5716dab 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -9,27 +9,17 @@ LowerMac::LowerMac(std::shared_ptr reporter) upper_mac_ = std::make_shared(reporter_); } -static auto vectorExtract(const std::vector& vec, size_t pos, size_t length) -> std::vector { - std::vector res; - - std::copy(vec.begin() + pos, vec.begin() + pos + length, std::back_inserter(res)); - - return res; -} - static auto vectorAppend(const std::vector& vec, std::vector& res, std::size_t pos, - std::size_t length) -> std::vector { + std::size_t length) -> void { std::copy(vec.begin() + pos, vec.begin() + pos + length, std::back_inserter(res)); - - return res; } auto LowerMac::process(const std::vector& frame, BurstType burst_type) -> std::vector> { - std::vector sb; - std::vector bkn1; - std::vector bkn2; - std::vector bb; - std::vector cb; + std::vector sb{}; + std::vector bkn1{}; + std::vector bkn2{}; + std::vector bb{}; + std::vector cb{}; std::vector> functions{}; @@ -41,20 +31,18 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // sb contains BSCH // ✅ done - sb = vectorExtract(frame, 94, 120); - sb = descramble(sb, 120, 0x0003); - sb = deinterleave(sb, 120, 11); - sb = viter_bi_decode_1614(depuncture23(sb, 120)); - if (check_crc_16_ccitt(sb, 76)) { - sb = vectorExtract(sb, 0, 60); + sb = descramble(frame.data() + 94, 120, 0x0003); + sb = deinterleave(sb.data(), 120, 11); + sb = viter_bi_decode_1614(depuncture23(sb.data(), 120)); + if (check_crc_16_ccitt(sb.data(), 76)) { + sb = std::vector(sb.begin(), sb.begin() + 60); upper_mac_->process_BSCH(burst_type, sb); } // bb contains AACH // ✅ done - bb = vectorExtract(frame, 252, 30); - bb = descramble(bb, 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb); + bb = descramble(frame.data() + 252, 30, upper_mac_->scrambling_code()); + bb = reed_muller_3014_decode(bb.data()); upper_mac_->process_AACH(burst_type, bb); // bkn2 block @@ -62,13 +50,12 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // any off SCH/HD, BNCH, STCH // see ETSI EN 300 392-2 V3.8.1 (2016-08) Figure 8.6: Error control // structure for π4DQPSK logical channels (part 2) - bkn2 = vectorExtract(frame, 282, 216); - bkn2 = descramble(bkn2, 216, upper_mac_->scrambling_code()); - bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = viter_bi_decode_1614(depuncture23(bkn2, 216)); + bkn2 = descramble(frame.data() + 282, 216, upper_mac_->scrambling_code()); + bkn2 = deinterleave(bkn2.data(), 216, 101); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); // if the crc does not work, then it might be a BLCH - if (check_crc_16_ccitt(bkn2, 140)) { - bkn2 = vectorExtract(bkn2, 0, 124); + if (check_crc_16_ccitt(bkn2.data(), 140)) { + bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); // SCH/HD or BNCH mapped upper_mac_->process_SCH_HD(burst_type, bkn2); @@ -78,16 +65,16 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // bb contains AACH // ✅ done - bb = vectorExtract(frame, 230, 14); - bb = vectorAppend(frame, bb, 266, 16); - bb = descramble(bb, 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb); + vectorAppend(frame, bb, 230, 14); + vectorAppend(frame, bb, 266, 16); + bb = descramble(bb.data(), 30, upper_mac_->scrambling_code()); + bb = reed_muller_3014_decode(bb.data()); upper_mac_->process_AACH(burst_type, bb); // TCH or SCH/F - bkn1 = vectorExtract(frame, 14, 216); - bkn1 = vectorAppend(frame, bkn1, 282, 216); - bkn1 = descramble(bkn1, 432, upper_mac_->scrambling_code()); + vectorAppend(frame, bkn1, 14, 216); + vectorAppend(frame, bkn1, 282, 216); + bkn1 = descramble(bkn1.data(), 432, upper_mac_->scrambling_code()); if (upper_mac_->downlink_usage() == DownlinkUsage::Traffic && upper_mac_->time_slot() <= 17) { // TODO: handle TCH @@ -96,10 +83,10 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) } else { // control channel // ✅done - bkn1 = deinterleave(bkn1, 432, 103); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 432)); - if (check_crc_16_ccitt(bkn1, 284)) { - bkn1 = vectorExtract(bkn1, 0, 268); + bkn1 = deinterleave(bkn1.data(), 432, 103); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 432)); + if (check_crc_16_ccitt(bkn1.data(), 284)) { + bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 268); upper_mac_->process_SCH_F(burst_type, bkn1); } } @@ -108,32 +95,30 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // bb contains AACH // ✅ done - bb = vectorExtract(frame, 230, 14); - bb = vectorAppend(frame, bb, 266, 16); - bb = descramble(bb, 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb); + vectorAppend(frame, bb, 230, 14); + vectorAppend(frame, bb, 266, 16); + bb = descramble(bb.data(), 30, upper_mac_->scrambling_code()); + bb = reed_muller_3014_decode(bb.data()); upper_mac_->process_AACH(burst_type, bb); // STCH + TCH // STCH + STCH - bkn1 = vectorExtract(frame, 14, 216); - bkn1 = descramble(bkn1, 216, upper_mac_->scrambling_code()); - bkn2 = vectorExtract(frame, 282, 216); - bkn2 = descramble(bkn2, 216, upper_mac_->scrambling_code()); + bkn1 = descramble(frame.data() + 14, 216, upper_mac_->scrambling_code()); + bkn2 = descramble(frame.data() + 282, 216, upper_mac_->scrambling_code()); if (upper_mac_->downlink_usage() == DownlinkUsage::Traffic && upper_mac_->time_slot() <= 17) { - bkn1 = deinterleave(bkn1, 216, 101); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 216)); - if (check_crc_16_ccitt(bkn1, 140)) { - bkn1 = vectorExtract(bkn1, 0, 124); + bkn1 = deinterleave(bkn1.data(), 216, 101); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 216)); + if (check_crc_16_ccitt(bkn1.data(), 140)) { + bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 124); upper_mac_->process_STCH(burst_type, bkn1); } if (upper_mac_->second_slot_stolen()) { - bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = viter_bi_decode_1614(depuncture23(bkn2, 216)); - if (check_crc_16_ccitt(bkn2, 140)) { - bkn2 = vectorExtract(bkn2, 0, 124); + bkn2 = deinterleave(bkn2.data(), 216, 101); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); + if (check_crc_16_ccitt(bkn2.data(), 140)) { + bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); upper_mac_->process_STCH(burst_type, bkn2); } } else { @@ -146,64 +131,62 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // SCH/HD + BNCH // ✅done - bkn1 = deinterleave(bkn1, 216, 101); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 216)); - if (check_crc_16_ccitt(bkn1, 140)) { - bkn1 = vectorExtract(bkn1, 0, 124); + bkn1 = deinterleave(bkn1.data(), 216, 101); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 216)); + if (check_crc_16_ccitt(bkn1.data(), 140)) { + bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 124); upper_mac_->process_SCH_HD(burst_type, bkn1); } // control channel - bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = viter_bi_decode_1614(depuncture23(bkn2, 216)); - if (check_crc_16_ccitt(bkn2, 140)) { - bkn2 = vectorExtract(bkn2, 0, 124); + bkn2 = deinterleave(bkn2.data(), 216, 101); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); + if (check_crc_16_ccitt(bkn2.data(), 140)) { + bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); upper_mac_->process_SCH_HD(burst_type, bkn2); } } } else if (burst_type == BurstType::ControlUplinkBurst) { - cb = vectorExtract(frame, 4, 84); - cb = vectorAppend(frame, cb, 118, 84); - cb = descramble(cb, 168, upper_mac_->scrambling_code()); + vectorAppend(frame, cb, 4, 84); + vectorAppend(frame, cb, 118, 84); + cb = descramble(cb.data(), 168, upper_mac_->scrambling_code()); // XXX: assume to be control channel - cb = deinterleave(cb, 168, 13); - cb = viter_bi_decode_1614(depuncture23(cb, 168)); - if (check_crc_16_ccitt(cb, 108)) { - cb = vectorExtract(cb, 0, 92); + cb = deinterleave(cb.data(), 168, 13); + cb = viter_bi_decode_1614(depuncture23(cb.data(), 168)); + if (check_crc_16_ccitt(cb.data(), 108)) { + cb = std::vector(cb.begin(), cb.begin() + 92); functions.push_back(std::bind(&UpperMac::process_SCH_HU, upper_mac_, burst_type, cb)); } } else if (burst_type == BurstType::NormalUplinkBurst) { - bkn1 = vectorExtract(frame, 4, 216); - bkn1 = vectorAppend(frame, bkn1, 242, 216); - bkn1 = descramble(bkn1, 432, upper_mac_->scrambling_code()); + vectorAppend(frame, bkn1, 4, 216); + vectorAppend(frame, bkn1, 242, 216); + bkn1 = descramble(bkn1.data(), 432, upper_mac_->scrambling_code()); // XXX: assume to be control channel - bkn1 = deinterleave(bkn1, 432, 103); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 432)); - if (check_crc_16_ccitt(bkn1, 284)) { - bkn1 = vectorExtract(bkn1, 0, 268); + bkn1 = deinterleave(bkn1.data(), 432, 103); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 432)); + if (check_crc_16_ccitt(bkn1.data(), 284)) { + bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 268); // fmt::print("NUB Burst crc good\n"); functions.push_back(std::bind(&UpperMac::process_SCH_F, upper_mac_, burst_type, bkn1)); } } else if (burst_type == BurstType::NormalUplinkBurstSplit) { // TODO: finish NormalUplinkBurstSplit implementation - bkn1 = vectorExtract(frame, 4, 216); - bkn1 = descramble(bkn1, 216, upper_mac_->scrambling_code()); - bkn2 = vectorExtract(frame, 242, 216); - bkn2 = descramble(bkn2, 216, upper_mac_->scrambling_code()); - - bkn1 = deinterleave(bkn1, 216, 101); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1, 216)); - if (check_crc_16_ccitt(bkn1, 140)) { - bkn1 = vectorExtract(bkn1, 0, 124); + bkn1 = descramble(frame.data() + 4, 216, upper_mac_->scrambling_code()); + bkn2 = descramble(frame.data() + 242, 216, upper_mac_->scrambling_code()); + + bkn1 = deinterleave(bkn1.data(), 216, 101); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 216)); + if (check_crc_16_ccitt(bkn1.data(), 140)) { + bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 124); // fmt::print("NUB_S 1 Burst crc good\n"); functions.push_back(std::bind(&UpperMac::process_STCH, upper_mac_, burst_type, bkn1)); } - bkn2 = deinterleave(bkn2, 216, 101); - bkn2 = viter_bi_decode_1614(depuncture23(bkn2, 216)); - if (check_crc_16_ccitt(bkn2, 140)) { - bkn2 = vectorExtract(bkn2, 0, 124); + bkn2 = deinterleave(bkn2.data(), 216, 101); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); + if (check_crc_16_ccitt(bkn2.data(), 140)) { + bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); if (upper_mac_->second_slot_stolen()) { // fmt::print("NUB_S 2 Burst crc good\n"); functions.push_back(std::bind(&UpperMac::process_STCH, upper_mac_, burst_type, bkn2)); diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index 87cbf73..9780498 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -28,8 +28,8 @@ static void xor_kernel(uint8_t* const res, const uint8_t* const data, const uint res[i] = data[i] ^ table[i]; } -auto LowerMac::descramble(const std::vector& data, const std::size_t len, - const uint32_t scramblingCode) noexcept -> std::vector { +auto LowerMac::descramble(const uint8_t* const data, const std::size_t len, const uint32_t scramblingCode) noexcept + -> std::vector { static std::vector table; std::vector res(len); @@ -56,7 +56,7 @@ auto LowerMac::descramble(const std::vector& data, const std::size_t le } } - xor_kernel(res.data(), data.data(), table.data(), len); + xor_kernel(res.data(), data, table.data(), len); return res; } @@ -65,7 +65,7 @@ auto LowerMac::descramble(const std::vector& data, const std::size_t le * @brief (K,a) block deinterleaver - 8.2.4 * */ -auto LowerMac::deinterleave(const std::vector& data, const std::size_t K, const std::size_t a) noexcept +auto LowerMac::deinterleave(const uint8_t* const data, const std::size_t K, const std::size_t a) noexcept -> std::vector { std::vector res(K, 0); // output vector is size K @@ -81,7 +81,7 @@ auto LowerMac::deinterleave(const std::vector& data, const std::size_t * @brief Depuncture with 2/3 rate - 8.2.3.1.3 * */ -auto LowerMac::depuncture23(const std::vector& data, const uint32_t len) noexcept -> std::vector { +auto LowerMac::depuncture23(const uint8_t* const data, const uint32_t len) noexcept -> std::vector { const uint8_t P[] = {0, 1, 2, 5}; // 8.2.3.1.3 - P[1..t] std::vector res(4 * len * 2 / 3, 0); // 8.2.3.1.2 with flag 0 for erase bit in Viterbi routine @@ -121,7 +121,7 @@ auto LowerMac::viter_bi_decode_1614(const std::vector& data) const noex * */ -auto LowerMac::reed_muller_3014_decode(const std::vector& data) noexcept -> std::vector { +auto LowerMac::reed_muller_3014_decode(const uint8_t* const data) noexcept -> std::vector { uint8_t q[14][5]; std::vector res(14); @@ -318,7 +318,7 @@ auto LowerMac::reed_muller_3014_decode(const std::vector& data) noexcep * @brief Calculated CRC16 ITU-T X.25 - CCITT * */ -auto LowerMac::check_crc_16_ccitt(const std::vector& data, const std::size_t len) noexcept -> bool { +auto LowerMac::check_crc_16_ccitt(const uint8_t* const data, const std::size_t len) noexcept -> bool { uint16_t crc = 0xFFFF; // CRC16-CCITT initial value for (std::size_t i = 0; i < len; i++) { From 2d34a8e79446c04d2d93d81335e9a3239df4f0b5 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 29 Jan 2024 21:47:37 +0100 Subject: [PATCH 29/35] l2/lower_mac: use fixed size buffer for descrambler result --- include/l2/lower_mac.hpp | 4 +-- src/l2/lower_mac.cpp | 60 ++++++++++++++++++++++--------------- src/l2/lower_mac_coding.cpp | 9 ++---- 3 files changed, 41 insertions(+), 32 deletions(-) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index 3df3637..963d029 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -37,8 +37,8 @@ class LowerMac { std::shared_ptr viter_bi_codec_1614_{}; std::shared_ptr upper_mac_{}; - [[nodiscard]] static auto descramble(const uint8_t* const data, const std::size_t len, - const uint32_t scramblingCode) noexcept -> std::vector; + static auto descramble(const uint8_t* const data, uint8_t* const res, const std::size_t len, + const uint32_t scramblingCode) noexcept -> void; [[nodiscard]] static auto deinterleave(const uint8_t* const data, const std::size_t K, const std::size_t a) noexcept -> std::vector; [[nodiscard]] static auto depuncture23(const uint8_t* const data, const uint32_t len) noexcept diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 5716dab..c855936 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -31,8 +31,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // sb contains BSCH // ✅ done - sb = descramble(frame.data() + 94, 120, 0x0003); - sb = deinterleave(sb.data(), 120, 11); + uint8_t sb_desc[120]; + descramble(frame.data() + 94, sb_desc, 120, 0x0003); + sb = deinterleave(sb_desc, 120, 11); sb = viter_bi_decode_1614(depuncture23(sb.data(), 120)); if (check_crc_16_ccitt(sb.data(), 76)) { sb = std::vector(sb.begin(), sb.begin() + 60); @@ -41,8 +42,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // bb contains AACH // ✅ done - bb = descramble(frame.data() + 252, 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb.data()); + uint8_t bb_desc[30]; + descramble(frame.data() + 252, bb_desc, 30, upper_mac_->scrambling_code()); + bb = reed_muller_3014_decode(bb_desc); upper_mac_->process_AACH(burst_type, bb); // bkn2 block @@ -50,8 +52,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // any off SCH/HD, BNCH, STCH // see ETSI EN 300 392-2 V3.8.1 (2016-08) Figure 8.6: Error control // structure for π4DQPSK logical channels (part 2) - bkn2 = descramble(frame.data() + 282, 216, upper_mac_->scrambling_code()); - bkn2 = deinterleave(bkn2.data(), 216, 101); + uint8_t bkn2_desc[216]; + descramble(frame.data() + 282, bkn2_desc, 216, upper_mac_->scrambling_code()); + bkn2 = deinterleave(bkn2_desc, 216, 101); bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); // if the crc does not work, then it might be a BLCH if (check_crc_16_ccitt(bkn2.data(), 140)) { @@ -67,14 +70,16 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // ✅ done vectorAppend(frame, bb, 230, 14); vectorAppend(frame, bb, 266, 16); - bb = descramble(bb.data(), 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb.data()); + uint8_t bb_des[30]; + descramble(bb.data(), bb_des, 30, upper_mac_->scrambling_code()); + bb = reed_muller_3014_decode(bb_des); upper_mac_->process_AACH(burst_type, bb); // TCH or SCH/F vectorAppend(frame, bkn1, 14, 216); vectorAppend(frame, bkn1, 282, 216); - bkn1 = descramble(bkn1.data(), 432, upper_mac_->scrambling_code()); + uint8_t bkn1_desc[432]; + descramble(bkn1.data(), bkn1_desc, 432, upper_mac_->scrambling_code()); if (upper_mac_->downlink_usage() == DownlinkUsage::Traffic && upper_mac_->time_slot() <= 17) { // TODO: handle TCH @@ -83,7 +88,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) } else { // control channel // ✅done - bkn1 = deinterleave(bkn1.data(), 432, 103); + bkn1 = deinterleave(bkn1_desc, 432, 103); bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 432)); if (check_crc_16_ccitt(bkn1.data(), 284)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 268); @@ -97,17 +102,20 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // ✅ done vectorAppend(frame, bb, 230, 14); vectorAppend(frame, bb, 266, 16); - bb = descramble(bb.data(), 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb.data()); + uint8_t bb_desc[30]; + descramble(bb.data(), bb_desc, 30, upper_mac_->scrambling_code()); + bb = reed_muller_3014_decode(bb_desc); upper_mac_->process_AACH(burst_type, bb); // STCH + TCH // STCH + STCH - bkn1 = descramble(frame.data() + 14, 216, upper_mac_->scrambling_code()); - bkn2 = descramble(frame.data() + 282, 216, upper_mac_->scrambling_code()); + uint8_t bkn1_desc[216]; + uint8_t bkn2_desc[216]; + descramble(frame.data() + 14, bkn1_desc, 216, upper_mac_->scrambling_code()); + descramble(frame.data() + 282, bkn2_desc, 216, upper_mac_->scrambling_code()); if (upper_mac_->downlink_usage() == DownlinkUsage::Traffic && upper_mac_->time_slot() <= 17) { - bkn1 = deinterleave(bkn1.data(), 216, 101); + bkn1 = deinterleave(bkn1_desc, 216, 101); bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 216)); if (check_crc_16_ccitt(bkn1.data(), 140)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 124); @@ -115,7 +123,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) } if (upper_mac_->second_slot_stolen()) { - bkn2 = deinterleave(bkn2.data(), 216, 101); + bkn2 = deinterleave(bkn2_desc, 216, 101); bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); if (check_crc_16_ccitt(bkn2.data(), 140)) { bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); @@ -148,10 +156,11 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) } else if (burst_type == BurstType::ControlUplinkBurst) { vectorAppend(frame, cb, 4, 84); vectorAppend(frame, cb, 118, 84); - cb = descramble(cb.data(), 168, upper_mac_->scrambling_code()); + uint8_t cb_desc[168]; + descramble(cb.data(), cb_desc, 168, upper_mac_->scrambling_code()); // XXX: assume to be control channel - cb = deinterleave(cb.data(), 168, 13); + cb = deinterleave(cb_desc, 168, 13); cb = viter_bi_decode_1614(depuncture23(cb.data(), 168)); if (check_crc_16_ccitt(cb.data(), 108)) { cb = std::vector(cb.begin(), cb.begin() + 92); @@ -160,10 +169,11 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) } else if (burst_type == BurstType::NormalUplinkBurst) { vectorAppend(frame, bkn1, 4, 216); vectorAppend(frame, bkn1, 242, 216); - bkn1 = descramble(bkn1.data(), 432, upper_mac_->scrambling_code()); + uint8_t bkn1_desc[432]; + descramble(bkn1.data(), bkn1_desc, 432, upper_mac_->scrambling_code()); // XXX: assume to be control channel - bkn1 = deinterleave(bkn1.data(), 432, 103); + bkn1 = deinterleave(bkn1_desc, 432, 103); bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 432)); if (check_crc_16_ccitt(bkn1.data(), 284)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 268); @@ -172,10 +182,12 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) } } else if (burst_type == BurstType::NormalUplinkBurstSplit) { // TODO: finish NormalUplinkBurstSplit implementation - bkn1 = descramble(frame.data() + 4, 216, upper_mac_->scrambling_code()); - bkn2 = descramble(frame.data() + 242, 216, upper_mac_->scrambling_code()); + uint8_t bkn1_desc[216]; + uint8_t bkn2_desc[216]; + descramble(frame.data() + 4, bkn1_desc, 216, upper_mac_->scrambling_code()); + descramble(frame.data() + 242, bkn2_desc, 216, upper_mac_->scrambling_code()); - bkn1 = deinterleave(bkn1.data(), 216, 101); + bkn1 = deinterleave(bkn1_desc, 216, 101); bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 216)); if (check_crc_16_ccitt(bkn1.data(), 140)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 124); @@ -183,7 +195,7 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) functions.push_back(std::bind(&UpperMac::process_STCH, upper_mac_, burst_type, bkn1)); } - bkn2 = deinterleave(bkn2.data(), 216, 101); + bkn2 = deinterleave(bkn2_desc, 216, 101); bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); if (check_crc_16_ccitt(bkn2.data(), 140)) { bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index 9780498..bebc352 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -28,11 +28,10 @@ static void xor_kernel(uint8_t* const res, const uint8_t* const data, const uint res[i] = data[i] ^ table[i]; } -auto LowerMac::descramble(const uint8_t* const data, const std::size_t len, const uint32_t scramblingCode) noexcept - -> std::vector { +auto LowerMac::descramble(const uint8_t* const data, uint8_t* const res, const std::size_t len, + const uint32_t scramblingCode) noexcept -> void { static std::vector table; - std::vector res(len); assert(len <= 432); @@ -56,9 +55,7 @@ auto LowerMac::descramble(const uint8_t* const data, const std::size_t len, cons } } - xor_kernel(res.data(), data, table.data(), len); - - return res; + xor_kernel(res, data, table.data(), len); } /** From ec9e2efcd0dd996f3b5cd1789b26831e0f087723 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 29 Jan 2024 21:58:34 +0100 Subject: [PATCH 30/35] l2/lower_mac: use fixed size buffer for reed muller 3014 result --- include/l2/lower_mac.hpp | 2 +- src/l2/lower_mac.cpp | 18 +++++++++++------- src/l2/lower_mac_coding.cpp | 5 +---- 3 files changed, 13 insertions(+), 12 deletions(-) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index 963d029..141904e 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -43,7 +43,7 @@ class LowerMac { -> std::vector; [[nodiscard]] static auto depuncture23(const uint8_t* const data, const uint32_t len) noexcept -> std::vector; - [[nodiscard]] static auto reed_muller_3014_decode(const uint8_t* const data) noexcept -> std::vector; + static auto reed_muller_3014_decode(const uint8_t* const data, uint8_t* const res) noexcept -> void; [[nodiscard]] static auto check_crc_16_ccitt(const uint8_t* const data, const std::size_t len) noexcept -> bool; [[nodiscard]] auto viter_bi_decode_1614(const std::vector& data) const noexcept -> std::vector; diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index c855936..8f54b2b 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -18,7 +18,6 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) std::vector sb{}; std::vector bkn1{}; std::vector bkn2{}; - std::vector bb{}; std::vector cb{}; std::vector> functions{}; @@ -44,8 +43,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // ✅ done uint8_t bb_desc[30]; descramble(frame.data() + 252, bb_desc, 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb_desc); - upper_mac_->process_AACH(burst_type, bb); + std::vector bb_rm(14); + reed_muller_3014_decode(bb_desc, bb_rm.data()); + upper_mac_->process_AACH(burst_type, bb_rm); // bkn2 block // ✅ done @@ -68,12 +68,14 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // bb contains AACH // ✅ done + std::vector bb{}; vectorAppend(frame, bb, 230, 14); vectorAppend(frame, bb, 266, 16); uint8_t bb_des[30]; descramble(bb.data(), bb_des, 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb_des); - upper_mac_->process_AACH(burst_type, bb); + std::vector bb_rm(14); + reed_muller_3014_decode(bb_des, bb_rm.data()); + upper_mac_->process_AACH(burst_type, bb_rm); // TCH or SCH/F vectorAppend(frame, bkn1, 14, 216); @@ -100,12 +102,14 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // bb contains AACH // ✅ done + std::vector bb{}; vectorAppend(frame, bb, 230, 14); vectorAppend(frame, bb, 266, 16); uint8_t bb_desc[30]; descramble(bb.data(), bb_desc, 30, upper_mac_->scrambling_code()); - bb = reed_muller_3014_decode(bb_desc); - upper_mac_->process_AACH(burst_type, bb); + std::vector bb_rm(14); + reed_muller_3014_decode(bb_desc, bb_rm.data()); + upper_mac_->process_AACH(burst_type, bb_rm); // STCH + TCH // STCH + STCH diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index bebc352..e14640b 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -118,9 +118,8 @@ auto LowerMac::viter_bi_decode_1614(const std::vector& data) const noex * */ -auto LowerMac::reed_muller_3014_decode(const uint8_t* const data) noexcept -> std::vector { +auto LowerMac::reed_muller_3014_decode(const uint8_t* const data, uint8_t* const res) noexcept -> void { uint8_t q[14][5]; - std::vector res(14); q[0][0] = data[0]; q[0][1] = (data[13 + 3] + data[13 + 5] + data[13 + 6] + data[13 + 7] + data[13 + 11]) % 2; @@ -307,8 +306,6 @@ auto LowerMac::reed_muller_3014_decode(const uint8_t* const data) noexcept -> st // print_vector(res, 14); // return vector_extract(data, 0, 14); - - return res; } /** From 10f9181f95b6135b8e4dcaeaf68874dcb67ecd40 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 29 Jan 2024 22:19:26 +0100 Subject: [PATCH 31/35] l2/lower_mac: use fixed size buffer for deinterleaver result --- include/l2/lower_mac.hpp | 4 +-- src/l2/lower_mac.cpp | 55 ++++++++++++++++++++++--------------- src/l2/lower_mac_coding.cpp | 8 ++---- 3 files changed, 37 insertions(+), 30 deletions(-) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index 141904e..8e239b3 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -39,8 +39,8 @@ class LowerMac { static auto descramble(const uint8_t* const data, uint8_t* const res, const std::size_t len, const uint32_t scramblingCode) noexcept -> void; - [[nodiscard]] static auto deinterleave(const uint8_t* const data, const std::size_t K, const std::size_t a) noexcept - -> std::vector; + static auto deinterleave(const uint8_t* const data, uint8_t* const res, const std::size_t K, + const std::size_t a) noexcept -> void; [[nodiscard]] static auto depuncture23(const uint8_t* const data, const uint32_t len) noexcept -> std::vector; static auto reed_muller_3014_decode(const uint8_t* const data, uint8_t* const res) noexcept -> void; diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 8f54b2b..5b1f871 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -32,8 +32,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // ✅ done uint8_t sb_desc[120]; descramble(frame.data() + 94, sb_desc, 120, 0x0003); - sb = deinterleave(sb_desc, 120, 11); - sb = viter_bi_decode_1614(depuncture23(sb.data(), 120)); + uint8_t sb_dei[120]; + deinterleave(sb_desc, sb_dei, 120, 11); + sb = viter_bi_decode_1614(depuncture23(sb_dei, 120)); if (check_crc_16_ccitt(sb.data(), 76)) { sb = std::vector(sb.begin(), sb.begin() + 60); upper_mac_->process_BSCH(burst_type, sb); @@ -54,8 +55,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // structure for π4DQPSK logical channels (part 2) uint8_t bkn2_desc[216]; descramble(frame.data() + 282, bkn2_desc, 216, upper_mac_->scrambling_code()); - bkn2 = deinterleave(bkn2_desc, 216, 101); - bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); + uint8_t bkn2_dei[216]; + deinterleave(bkn2_desc, bkn2_dei, 216, 101); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2_dei, 216)); // if the crc does not work, then it might be a BLCH if (check_crc_16_ccitt(bkn2.data(), 140)) { bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); @@ -90,8 +92,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) } else { // control channel // ✅done - bkn1 = deinterleave(bkn1_desc, 432, 103); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 432)); + uint8_t bkn1_dei[432]; + deinterleave(bkn1_desc, bkn1_dei, 432, 103); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1_dei, 432)); if (check_crc_16_ccitt(bkn1.data(), 284)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 268); upper_mac_->process_SCH_F(burst_type, bkn1); @@ -119,16 +122,18 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) descramble(frame.data() + 282, bkn2_desc, 216, upper_mac_->scrambling_code()); if (upper_mac_->downlink_usage() == DownlinkUsage::Traffic && upper_mac_->time_slot() <= 17) { - bkn1 = deinterleave(bkn1_desc, 216, 101); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 216)); + uint8_t bkn1_dei[216]; + deinterleave(bkn1_desc, bkn1_dei, 216, 101); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1_dei, 216)); if (check_crc_16_ccitt(bkn1.data(), 140)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 124); upper_mac_->process_STCH(burst_type, bkn1); } if (upper_mac_->second_slot_stolen()) { - bkn2 = deinterleave(bkn2_desc, 216, 101); - bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); + uint8_t bkn2_dei[216]; + deinterleave(bkn2_desc, bkn2_dei, 216, 101); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2_dei, 216)); if (check_crc_16_ccitt(bkn2.data(), 140)) { bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); upper_mac_->process_STCH(burst_type, bkn2); @@ -143,15 +148,17 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // SCH/HD + BNCH // ✅done - bkn1 = deinterleave(bkn1.data(), 216, 101); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 216)); + uint8_t bkn1_dei[216]; + deinterleave(bkn1_desc, bkn1_dei, 216, 101); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1_dei, 216)); if (check_crc_16_ccitt(bkn1.data(), 140)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 124); upper_mac_->process_SCH_HD(burst_type, bkn1); } // control channel - bkn2 = deinterleave(bkn2.data(), 216, 101); - bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); + uint8_t bkn2_dei[216]; + deinterleave(bkn2_desc, bkn2_dei, 216, 101); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2_dei, 216)); if (check_crc_16_ccitt(bkn2.data(), 140)) { bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); upper_mac_->process_SCH_HD(burst_type, bkn2); @@ -164,8 +171,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) descramble(cb.data(), cb_desc, 168, upper_mac_->scrambling_code()); // XXX: assume to be control channel - cb = deinterleave(cb_desc, 168, 13); - cb = viter_bi_decode_1614(depuncture23(cb.data(), 168)); + uint8_t cb_dei[168]; + deinterleave(cb_desc, cb_dei, 168, 13); + cb = viter_bi_decode_1614(depuncture23(cb_dei, 168)); if (check_crc_16_ccitt(cb.data(), 108)) { cb = std::vector(cb.begin(), cb.begin() + 92); functions.push_back(std::bind(&UpperMac::process_SCH_HU, upper_mac_, burst_type, cb)); @@ -177,8 +185,9 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) descramble(bkn1.data(), bkn1_desc, 432, upper_mac_->scrambling_code()); // XXX: assume to be control channel - bkn1 = deinterleave(bkn1_desc, 432, 103); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 432)); + uint8_t bkn1_dei[432]; + deinterleave(bkn1_desc, bkn1_dei, 432, 103); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1_dei, 432)); if (check_crc_16_ccitt(bkn1.data(), 284)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 268); // fmt::print("NUB Burst crc good\n"); @@ -191,16 +200,18 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) descramble(frame.data() + 4, bkn1_desc, 216, upper_mac_->scrambling_code()); descramble(frame.data() + 242, bkn2_desc, 216, upper_mac_->scrambling_code()); - bkn1 = deinterleave(bkn1_desc, 216, 101); - bkn1 = viter_bi_decode_1614(depuncture23(bkn1.data(), 216)); + uint8_t bkn1_dei[216]; + deinterleave(bkn1_desc, bkn1_dei, 216, 101); + bkn1 = viter_bi_decode_1614(depuncture23(bkn1_dei, 216)); if (check_crc_16_ccitt(bkn1.data(), 140)) { bkn1 = std::vector(bkn1.begin(), bkn1.begin() + 124); // fmt::print("NUB_S 1 Burst crc good\n"); functions.push_back(std::bind(&UpperMac::process_STCH, upper_mac_, burst_type, bkn1)); } - bkn2 = deinterleave(bkn2_desc, 216, 101); - bkn2 = viter_bi_decode_1614(depuncture23(bkn2.data(), 216)); + uint8_t bkn2_dei[216]; + deinterleave(bkn2_desc, bkn2_dei, 216, 101); + bkn2 = viter_bi_decode_1614(depuncture23(bkn2_dei, 216)); if (check_crc_16_ccitt(bkn2.data(), 140)) { bkn2 = std::vector(bkn2.begin(), bkn2.begin() + 124); if (upper_mac_->second_slot_stolen()) { diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index e14640b..1e48ed1 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -62,16 +62,12 @@ auto LowerMac::descramble(const uint8_t* const data, uint8_t* const res, const s * @brief (K,a) block deinterleaver - 8.2.4 * */ -auto LowerMac::deinterleave(const uint8_t* const data, const std::size_t K, const std::size_t a) noexcept - -> std::vector { - std::vector res(K, 0); // output vector is size K - +auto LowerMac::deinterleave(const uint8_t* const data, uint8_t* const res, const std::size_t K, + const std::size_t a) noexcept -> void { for (std::size_t i = 0; i < K; i++) { auto k = 1 + (a * (i + 1)) % K; res[i] = data[k - 1]; // to interleave: DataOut[i-1] = DataIn[k-1] } - - return res; } /** From 80a9b3214a414951338d67609ec710946adc9e1c Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 30 Jan 2024 01:58:03 +0100 Subject: [PATCH 32/35] utils/bit_vector: do less copying and more unsafe pointer handling. shorter executing time. --- include/utils/bit_vector.hpp | 41 +++++++++--- src/l2/logical_link_control.cpp | 19 +----- src/l2/upper_mac.cpp | 27 +++++--- src/l2/upper_mac_fragmentation.cpp | 10 ++- src/l3/circuit_mode_control_entity.cpp | 4 +- src/l3/short_data_service.cpp | 5 +- src/utils/bit_vector.cpp | 92 ++++++++++++++++---------- 7 files changed, 114 insertions(+), 84 deletions(-) diff --git a/include/utils/bit_vector.hpp b/include/utils/bit_vector.hpp index aaf5bdb..c6830e8 100644 --- a/include/utils/bit_vector.hpp +++ b/include/utils/bit_vector.hpp @@ -17,19 +17,42 @@ class BitVector { private: std::vector data_; + std::size_t len_; + std::size_t read_offset_; public: - explicit BitVector(std::vector data) - : data_(std::move(data)){}; + BitVector() + : data_() + , len_(0) + , read_offset_(0){}; + BitVector(const std::vector& vec) + : data_(vec) + , len_(vec.size()) + , read_offset_(0){}; + BitVector(const BitVector& other) + : data_() + , len_(0) + , read_offset_(0) { + append(other); + }; + BitVector(const uint8_t* const data, const std::size_t len) + : data_(data, data + len) + , len_(len) + , read_offset_(0){}; ~BitVector() noexcept = default; - auto take_vector(std::size_t numberBits) -> std::vector; - auto take_last_vector(std::size_t numberBits) -> std::vector; - void append(std::vector bits); - auto take(std::size_t numberBits) -> uint64_t; - auto take_last(std::size_t numberBits) -> uint64_t; - auto take_last() -> uint8_t; - [[nodiscard]] inline auto bits_left() const noexcept -> std::size_t { return data_.size(); }; + auto append(const BitVector& other) -> void; + [[nodiscard]] auto take(const std::size_t numberBits) -> uint64_t; + [[nodiscard]] auto compute_fcs() -> uint32_t; + + [[nodiscard]] auto take_vector(const std::size_t numberBits) -> const uint8_t* const; + private: + [[nodiscard]] auto take_last_vector(const std::size_t numberBits) -> const uint8_t* const; + + public: + [[nodiscard]] auto take_last(const std::size_t numberBits) -> uint64_t; + [[nodiscard]] auto take_last() -> uint8_t; + [[nodiscard]] inline auto bits_left() const noexcept -> std::size_t { return len_; }; [[nodiscard]] auto is_mac_padding() const noexcept -> bool; friend auto operator<<(std::ostream& stream, const BitVector& vec) -> std::ostream&; diff --git a/src/l2/logical_link_control.cpp b/src/l2/logical_link_control.cpp index e03ac52..06f399b 100644 --- a/src/l2/logical_link_control.cpp +++ b/src/l2/logical_link_control.cpp @@ -3,28 +3,11 @@ #include -auto LogicalLinkControl::compute_fcs(std::vector const& data) -> uint32_t { - uint32_t crc = 0xFFFFFFFF; - if (data.size() < 32) { - crc <<= (32 - data.size()); - } - - for (auto it = data.cbegin(); it != data.cend(); it++) { - uint8_t bit = (*it ^ (crc >> 31)) & 1; - crc <<= 1; - if (bit) { - crc = crc ^ 0x04C11DB7; - } - } - return ~crc; -} - auto LogicalLinkControl::check_fcs(BitVector& vec) -> bool { // remove last 32 bits auto fcs = vec.take_last(32); - auto bits = BitVector(vec).take_vector(vec.bits_left()); - auto computed_fcs = LogicalLinkControl::compute_fcs(bits); + auto computed_fcs = vec.compute_fcs(); if (fcs != computed_fcs) { std::cout << " FCS error" << std::endl; diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 403087f..7d74894 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -388,7 +388,8 @@ void UpperMac::process_mac_usignal(BitVector& vec) { second_slot_stolen_ = (vec.take(1) == 1); // TODO: TM-SDU - auto tm_sdu = BitVector(vec.take_vector(vec.bits_left())); + auto bits_left = vec.bits_left(); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << "MAC U-SIGNAL" << std::endl; std::cout << " Second subslot is stolen: " << (second_slot_stolen_ ? "true" : "false") << std::endl; std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; @@ -411,7 +412,8 @@ void UpperMac::process_mac_d_blck(BitVector& vec) { remove_fill_bits(vec); } - auto tm_sdu = BitVector(vec.take_vector(vec.bits_left())); + auto bits_left = vec.bits_left(); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << "MAC D-BLCK" << std::endl; std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; std::cout << " Address: " << address << std::endl; @@ -431,7 +433,8 @@ void UpperMac::process_mac_u_blck(BitVector& vec) { remove_fill_bits(vec); } - auto tm_sdu = BitVector(vec.take_vector(vec.bits_left())); + auto bits_left = vec.bits_left(); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << "MAC U-BLCK" << std::endl; std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; std::cout << " encrypted: 0b" << std::bitset<1>(encrypted) << std::endl; @@ -448,7 +451,8 @@ void UpperMac::process_mac_frag_downlink(BitVector& vec) { remove_fill_bits(vec); } - auto tm_sdu = BitVector(vec.take_vector(vec.bits_left())); + auto bits_left = vec.bits_left(); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << "MAC FRAG" << std::endl; std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; @@ -462,7 +466,8 @@ void UpperMac::process_mac_frag_uplink(BitVector& vec) { remove_fill_bits(vec); } - auto tm_sdu = BitVector(vec.take_vector(vec.bits_left())); + auto bits_left = vec.bits_left(); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << "MAC FRAG" << std::endl; std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; @@ -539,7 +544,7 @@ void UpperMac::process_mac_end_downlink(BitVector& vec) { remove_fill_bits(vec); } - auto tm_sdu = BitVector(vec.take_vector(bits_left)); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << "MAC END" << std::endl; std::cout << " length_indictaion: 0b" << std::bitset<6>(length_indictaion) << std::endl; std::cout << " TM-SDU: size = " << std::to_string(bits_left) << ": " << tm_sdu << std::endl; @@ -561,7 +566,7 @@ void UpperMac::process_mac_end_uplink(BitVector& vec) { remove_fill_bits(vec); } - auto tm_sdu = BitVector(vec.take_vector(bits_left)); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << "MAC END" << std::endl; std::cout << " length_indictaion: 0b" << std::bitset<6>(length_indictaion) << std::endl; std::cout << " TM-SDU: size = " << std::to_string(bits_left) << ": " << tm_sdu << std::endl; @@ -705,7 +710,7 @@ void UpperMac::process_mac_resource(BitVector& vec) { bits_left = vec.bits_left(); } - auto tm_sdu = BitVector(vec.take_vector(bits_left)); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << " TM-SDU: size = " << std::to_string(bits_left) << ": " << tm_sdu << std::endl; std::cout << " mac_header_length = " << std::to_string(mac_header_length) << std::endl; std::cout << " fill_bit_indication: 0b" << std::bitset<1>(fill_bit_indication) << std::endl; @@ -785,7 +790,7 @@ void UpperMac::process_mac_data(BitVector& vec) { } } - auto tm_sdu = BitVector(vec.take_vector(bits_left)); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; std::cout << " Address: " << address << std::endl; @@ -852,7 +857,7 @@ void UpperMac::process_mac_access(BitVector& vec) { bits_left = length_indication * 8 - mac_header_length; } - auto tm_sdu = BitVector(vec.take_vector(bits_left)); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; std::cout << " Address: " << address << std::endl; @@ -885,7 +890,7 @@ void UpperMac::process_mac_end_hu(BitVector& vec) { bits_left = length_indictaion * 8 - mac_header_length; } - auto tm_sdu = BitVector(vec.take_vector(bits_left)); + auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; // XXX: implement combination of uplink and downlink std::cout << " Last fragment sent on reserved subslot. Cannot process!" << std::endl; diff --git a/src/l2/upper_mac_fragmentation.cpp b/src/l2/upper_mac_fragmentation.cpp index 034be2e..880c9a6 100644 --- a/src/l2/upper_mac_fragmentation.cpp +++ b/src/l2/upper_mac_fragmentation.cpp @@ -40,13 +40,12 @@ void UpperMac::fragmentation_end_burst() { fragment_list_.clear(); - auto tm_sdu = BitVector({}); + auto tm_sdu = BitVector(); // combine to tm_sdu for (auto it = fragment_map_[last_address_type_end_hu_].begin(); it != fragment_map_[last_address_type_end_hu_].end(); it++) { - auto bit_vec = *it; - tm_sdu.append(bit_vec.take_vector(bit_vec.bits_left())); + tm_sdu.append(*it); } // remove key from the map @@ -72,12 +71,11 @@ void UpperMac::fragmentation_end_burst() { // frag end received. send to logical link control. if (fragment_end_received_) { - auto tm_sdu = BitVector({}); + auto tm_sdu = BitVector(); // combine to tm_sdu for (auto it = fragment_map_[last_address_type_].begin(); it != fragment_map_[last_address_type_].end(); it++) { - auto bit_vec = *it; - tm_sdu.append(bit_vec.take_vector(bit_vec.bits_left())); + tm_sdu.append(*it); } // remove key from the map diff --git a/src/l3/circuit_mode_control_entity.cpp b/src/l3/circuit_mode_control_entity.cpp index c862613..8fc9e55 100644 --- a/src/l3/circuit_mode_control_entity.cpp +++ b/src/l3/circuit_mode_control_entity.cpp @@ -74,7 +74,7 @@ void CircuitModeControlEntity::process_d_sds_data(const AddressType to_address, auto length_identifier = vec.take(11); std::cout << " length_identifier: " << length_identifier << std::endl; std::cout << " BitsLeft = " << vec.bits_left() << " " << vec << std::endl; - auto sds = BitVector(vec.take_vector(length_identifier)); + auto sds = BitVector(vec.take_vector(length_identifier), length_identifier); sds_->process(to_address, from_address, sds); } else { // XXX: we should take the length_identifier into account... @@ -107,7 +107,7 @@ void CircuitModeControlEntity::process_u_sds_data(const AddressType from_address auto length_identifier = vec.take(11); std::cout << " length_identifier: " << length_identifier << std::endl; std::cout << " BitsLeft = " << vec.bits_left() << " " << vec << std::endl; - auto sds = BitVector(vec.take_vector(length_identifier)); + auto sds = BitVector(vec.take_vector(length_identifier), length_identifier); sds_->process(to_address, from_address, sds); } else { // XXX: we should take the length_identifier into account... diff --git a/src/l3/short_data_service.cpp b/src/l3/short_data_service.cpp index 5fc22e3..bbf1fac 100644 --- a/src/l3/short_data_service.cpp +++ b/src/l3/short_data_service.cpp @@ -21,9 +21,8 @@ void ShortDataService::process(const AddressType to_address, const AddressType f std::cout << " From: " << from_address << "To: " << to_address << std::endl; { - auto vec_data = vec.take_vector(vec.bits_left()); - vec = BitVector(vec_data); - auto vec_copy = BitVector(vec); + auto vec_copy = BitVector(); + vec_copy.append(vec); message_["data"] = nlohmann::json::array(); diff --git a/src/utils/bit_vector.cpp b/src/utils/bit_vector.cpp index 44545c5..6656ab9 100644 --- a/src/utils/bit_vector.cpp +++ b/src/utils/bit_vector.cpp @@ -8,74 +8,96 @@ */ #include +#include #include #include #include -auto BitVector::take_vector(const size_t numberBits) -> std::vector { +auto BitVector::compute_fcs() -> uint32_t { + uint32_t crc = 0xFFFFFFFF; + if (len_ < 32) { + crc <<= (32 - len_); + } + + for (auto i = 0; i < len_; i++) { + uint8_t bit = (data_[read_offset_ + i] ^ (crc >> 31)) & 1; + crc <<= 1; + if (bit) { + crc = crc ^ 0x04C11DB7; + } + } + return ~crc; +} + +auto BitVector::take_vector(const size_t numberBits) -> const uint8_t* const { if (numberBits > bits_left()) { throw std::runtime_error(std::to_string(numberBits) + " bits not left in BitVec (" + std::to_string(bits_left()) + ")"); } - std::vector res; - - std::copy_n(data_.begin(), numberBits, std::back_inserter(res)); + auto res = data_.data() + read_offset_; // delete first n entries - std::vector(data_.begin() + numberBits, data_.end()).swap(data_); + read_offset_ += numberBits; + len_ -= numberBits; return res; } -auto BitVector::take_last_vector(const size_t numberBits) -> std::vector { +auto BitVector::take_last_vector(const size_t numberBits) -> const uint8_t* const { if (numberBits > bits_left()) { throw std::runtime_error(std::to_string(numberBits) + " bits not left in BitVec (" + std::to_string(bits_left()) + ")"); } - std::vector res; - - auto start = data_.begin(); - // advance to position of last N elements - std::advance(start, bits_left() - numberBits); + auto res = data_.data() + bits_left() - numberBits; - std::copy_n(start, numberBits, std::back_inserter(res)); - - // delete last n entries - std::vector(data_.begin(), data_.begin() + bits_left() - numberBits).swap(data_); + // take from the back + len_ -= numberBits; return res; } -void BitVector::append(std::vector bits) { data_.insert(data_.end(), bits.begin(), bits.end()); } +void BitVector::append(const BitVector& other) { + // actually need to do a copy here! + if (read_offset_ > 0) { + // copy to front + std::memcpy(data_.data(), data_.data() + read_offset_, sizeof(uint8_t) * len_); + read_offset_ = 0; + } + // shrink the size + data_.resize(len_); + + // copy in other + data_.resize(len_ + other.len_); + std::memcpy(data_.data() + len_, other.data_.data() + other.read_offset_, sizeof(uint8_t) * other.len_); + len_ += other.len_; +} auto BitVector::take(const size_t numberBits) -> uint64_t { - std::vector bits = take_vector(numberBits); + auto bits = take_vector(numberBits); uint64_t ret = 0; - for (auto it = bits.begin(); it != bits.end(); it++) { - if (it != bits.begin()) { + for (auto i = 0; i < numberBits; i++) { + if (i != 0) ret <<= 1; - } - ret |= (*it & 0x1); + ret |= (bits[i] & 0x1); } return ret; } auto BitVector::take_last(const size_t numberBits) -> uint64_t { - std::vector bits = take_last_vector(numberBits); + auto bits = take_last_vector(numberBits); uint64_t ret = 0; - for (auto it = bits.begin(); it != bits.end(); it++) { - if (it != bits.begin()) { + for (auto i = 0; i < numberBits; i++) { + if (i != 0) ret <<= 1; - } - ret |= (*it & 0x1); + ret |= (bits[i] & 0x1); } return ret; @@ -86,20 +108,20 @@ auto BitVector::take_last() -> uint8_t { throw std::runtime_error("1 bit not left in BitVec (" + std::to_string(bits_left()) + ")"); } - auto last = data_.back(); - data_.pop_back(); + len_ -= 1; - return last; + return data_[read_offset_ + len_]; } auto BitVector::is_mac_padding() const noexcept -> bool { - auto it = data_.begin(); + if (len_ == 0) + return false; - if (*it++ != 1) + if (data_[read_offset_] != 1) return false; - for (; it != data_.end(); it++) { - if (*it != 0) + for (auto i = 1; i < len_; i++) { + if (data_[read_offset_ + i] != 0) return false; } @@ -108,8 +130,8 @@ auto BitVector::is_mac_padding() const noexcept -> bool { auto operator<<(std::ostream& stream, const BitVector& vec) -> std::ostream& { stream << "BitVec: "; - for (unsigned char it : vec.data_) { - stream << std::to_string(it); + for (auto i = 0; i < vec.len_; i++) { + stream << std::to_string(vec.data_[vec.read_offset_ + i]); } return stream; From e85e832361fc0d435a5e06fbc14726b8135c3acb Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 4 Feb 2024 17:16:32 +0100 Subject: [PATCH 33/35] update debug print --- src/l2/upper_mac.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 7d74894..bc40510 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -25,7 +25,7 @@ void UpperMac::incrementTn() { multi_frame_number_ = 1; } - std::cout << "TN: " << time_slot_ << " FN: " << frame_number_ << " MN: " << multi_frame_number_ << std::endl; + std::cout << "[TIME] TN: " << time_slot_ << " FN: " << frame_number_ << " MN: " << multi_frame_number_ << std::endl; } /** @@ -70,7 +70,7 @@ void UpperMac::process_AACH(const BurstType burst_type, const std::vector& data) { assert(burst_type.is_downlink_burst()); - std::cout << "SCH_HD" << std::endl; + std::cout << "[Channel] SCH_HD" << std::endl; process_signalling_channel(burst_type, data, true, false); } @@ -124,7 +124,7 @@ void UpperMac::process_SCH_HU(const BurstType burst_type, const std::vector& data) { - std::cout << "SCH_F" << std::endl; + std::cout << "[Channel] SCH_F" << std::endl; process_signalling_channel(burst_type, data, false, false); } void UpperMac::process_STCH(const BurstType burst_type, const std::vector& data) { - std::cout << "STCH" << std::endl; + std::cout << "[Channel] STCH" << std::endl; second_slot_stolen_ = false; From 633aa81d8c03908960dcc2def8667b2ea61f8100 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 4 Feb 2024 17:20:34 +0100 Subject: [PATCH 34/35] update debug print --- src/l2/upper_mac.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index bc40510..0c70fe4 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -893,7 +893,9 @@ void UpperMac::process_mac_end_hu(BitVector& vec) { auto tm_sdu = BitVector(vec.take_vector(bits_left), bits_left); std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; // XXX: implement combination of uplink and downlink - std::cout << " Last fragment sent on reserved subslot. Cannot process!" << std::endl; + std::cout << " Last fragment sent on reserved subslot. Cannot process! Taking the last MAC-ACCESS or MAC-DATA as " + "the first half." + << std::endl; // XXX: This just takes the last plausible MAC-DATA or MAC-ACCESS as start of fragmentation. this will be buggy, but // at least a start to avoid having to deal with the combination of uplink and downlink processing From 1389d553dd0e2712420535b085385879ab5a3a99 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sun, 4 Feb 2024 18:09:19 +0100 Subject: [PATCH 35/35] fix descrambling precomputation --- src/l2/lower_mac_coding.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/l2/lower_mac_coding.cpp b/src/l2/lower_mac_coding.cpp index 1e48ed1..71fbf7e 100644 --- a/src/l2/lower_mac_coding.cpp +++ b/src/l2/lower_mac_coding.cpp @@ -31,11 +31,12 @@ static void xor_kernel(uint8_t* const res, const uint8_t* const data, const uint auto LowerMac::descramble(const uint8_t* const data, uint8_t* const res, const std::size_t len, const uint32_t scramblingCode) noexcept -> void { - static std::vector table; + static std::map> tableByScramblingCode; assert(len <= 432); - if (table.size() == 0) { + if (tableByScramblingCode.count(scramblingCode) == 0) { + auto& table = tableByScramblingCode[scramblingCode]; table.resize(432); const uint8_t poly[14] = {32, 26, 23, 22, 16, 12, 11, 10, 8, 7, 5, 4, 2, 1}; // Feedback polynomial - see 8.2.5.2 (8.39) @@ -55,7 +56,7 @@ auto LowerMac::descramble(const uint8_t* const data, uint8_t* const res, const s } } - xor_kernel(res, data, table.data(), len); + xor_kernel(res, data, tableByScramblingCode[scramblingCode].data(), len); } /**