From ab91156676e6f5a5b720d2bbbfac223a2b2fdeb1 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 8 Jan 2025 12:14:08 +0100 Subject: [PATCH] use soft decision decoder for iq data --- include/l2/lower_mac.hpp | 46 +++++++++++++++++++++----------- include/utils/viter_bi_codec.hpp | 38 +++++++++++++++++++++++--- src/iq_stream_decoder.cpp | 4 +-- src/utils/viter_bi_codec.cpp | 23 +++++++++++++++- 4 files changed, 89 insertions(+), 22 deletions(-) diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index ee65cd9..5851568 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -31,9 +31,22 @@ class LowerMac { std::optional scrambling_code = std::nullopt); ~LowerMac() = default; + /// Get the correct viterbi decoder either for hard decision for bit or soft decision for soft bits. + /// \targ DataType with bool (bits) the hard decision decoder is returned, with int16_t we have soft bits and return + /// the soft decision decoder + template [[nodiscard]] auto getViterbi() const -> const ViterbiCodec& { + if constexpr (std::is_same_v) { + return viter_bi_codec_1614_hard_decision_; + } + if constexpr (std::is_same_v) { + return viter_bi_codec_1614_soft_decision_; + } + assert(false && "Compiling descramble with template paramater not bool or int16_t"); + } + /// handles the decoding of the synchronization bursts and once synchronized passes the data to the decoding of the /// channels. keeps track of the current network time - /// \targ DataType with bool is selected we have bits, with int16_t we have symbols + /// \targ DataType with bool is selected we have bits, with int16_t we have soft bits template [[nodiscard]] auto process(std::vector frame, BurstType burst_type) -> return_type { // Set to true if there was some decoding error in the lower MAC @@ -61,8 +74,8 @@ class LowerMac { }; auto sb_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(sb_input, 0x0003), 11))); + getViterbi(), LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(sb_input, 0x0003), 11))); if (LowerMacCoding::check_crc_16_ccitt<76>(sb_bits)) { current_sync = BroadcastSynchronizationChannel( @@ -140,8 +153,8 @@ class LowerMac { } auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(bkn2_input, bsc.scrambling_code), 101))); + getViterbi(), LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(bkn2_input, bsc.scrambling_code), 101))); slots = Slots(burst_type, SlotType::kOneSubslot, Slot(LogicalChannelDataAndCrc{ @@ -174,7 +187,7 @@ class LowerMac { auto bkn1_descrambled_bits = LowerMacCoding::softbits_to_bits(bkn1_descrambled); auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, + getViterbi(), LowerMacCoding::depuncture23(LowerMacCoding::deinterleave(bkn1_descrambled, 103))); if (aach.downlink_usage == DownlinkUsage::Traffic) { @@ -216,8 +229,8 @@ class LowerMac { }; auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code), 101))); + getViterbi(), LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code), 101))); std::array bkn2_input{}; for (auto i = 0; i < 216; i++) { @@ -227,7 +240,7 @@ class LowerMac { auto bkn2_deinterleaved = LowerMacCoding::deinterleave(LowerMacCoding::descramble(bkn2_input, bsc.scrambling_code), 101); auto bkn2_deinterleaved_bits = LowerMacCoding::softbits_to_bits(bkn2_deinterleaved); - auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614(viter_bi_codec_1614_, + auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614(getViterbi(), LowerMacCoding::depuncture23(bkn2_deinterleaved)); // Half slot traffic channel defines type 3 bits (deinterleaved) @@ -274,8 +287,8 @@ class LowerMac { }; auto cb_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(cb_input, bsc.scrambling_code), 13))); + getViterbi(), LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(cb_input, bsc.scrambling_code), 13))); // SCH/HU slots = Slots(burst_type, SlotType::kOneSubslot, @@ -297,7 +310,7 @@ class LowerMac { auto bkn1_descrambled_bits = LowerMacCoding::softbits_to_bits(bkn1_descrambled); auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, + getViterbi(), LowerMacCoding::depuncture23(LowerMacCoding::deinterleave(bkn1_descrambled, 103))); slots = Slots( @@ -321,8 +334,8 @@ class LowerMac { } auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code), 101))); + getViterbi(), LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code), 101))); std::array bkn2_input{}; for (auto i = 0; i < 216; i++) { @@ -332,7 +345,7 @@ class LowerMac { auto bkn2_deinterleaved = LowerMacCoding::deinterleave(LowerMacCoding::descramble(bkn2_input, bsc.scrambling_code), 101); auto bkn2_deinterleaved_bits = LowerMacCoding::softbits_to_bits(bkn2_deinterleaved); - auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614(viter_bi_codec_1614_, + auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614(getViterbi(), LowerMacCoding::depuncture23(bkn2_deinterleaved)); // STCH + TCH @@ -361,7 +374,8 @@ class LowerMac { return *slots; } - const ViterbiCodec viter_bi_codec_1614_; + const ViterbiCodecHardDecision viter_bi_codec_1614_hard_decision_; + const ViterbiCodecSoftDecision viter_bi_codec_1614_soft_decision_; std::unique_ptr metrics_; diff --git a/include/utils/viter_bi_codec.hpp b/include/utils/viter_bi_codec.hpp index cfdee60..b7e21bc 100644 --- a/include/utils/viter_bi_codec.hpp +++ b/include/utils/viter_bi_codec.hpp @@ -20,15 +20,26 @@ class ViterbiCodec { public: - ViterbiCodec() = default; - [[nodiscard]] auto Decode(const std::vector& bits) const -> std::vector; + virtual ~ViterbiCodec() = default; static constexpr size_t K = 5; static constexpr size_t R = 4; - private: + [[nodiscard]] virtual auto Decode(const std::vector& bits) const -> std::vector = 0; + + protected: + ViterbiCodec() = default; + const std::vector G = {19, 29, 23, 27}; +}; +class ViterbiCodecHardDecision : public ViterbiCodec { + public: + ViterbiCodecHardDecision() = default; + + [[nodiscard]] auto Decode(const std::vector& bits) const -> std::vector override; + + private: const int16_t soft_decision_high = +1; const int16_t soft_decision_low = -1; const uint16_t max_error = static_cast(soft_decision_high - soft_decision_low) * static_cast(R); @@ -43,3 +54,24 @@ class ViterbiCodec { const ViterbiBranchTable branch_table = ViterbiBranchTable(G.data(), soft_decision_high, soft_decision_low); }; + +class ViterbiCodecSoftDecision : public ViterbiCodec { + public: + ViterbiCodecSoftDecision() = default; + [[nodiscard]] auto Decode(const std::vector& bits) const -> std::vector override; + + private: + const int16_t soft_decision_high = +127; + const int16_t soft_decision_low = -127; + const uint16_t max_error = static_cast(soft_decision_high - soft_decision_low) * static_cast(R); + const uint16_t error_margin = max_error * static_cast(5U); + + 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)}; + + const ViterbiBranchTable branch_table = + ViterbiBranchTable(G.data(), soft_decision_high, soft_decision_low); +}; diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index aa1b996..4fa32d6 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -70,8 +70,8 @@ auto IQStreamDecoder::symbols_to_softstream(iterator_type it) -> std::vector 0 -> 0 -> -1 // real <= 0 -> 1 -> 1 - soft_bits[i * 2] = -1 * it->imag(); - soft_bits[(i * 2) + 1] = -1 * it->real(); + soft_bits[i * 2] = -127 * it->imag(); + soft_bits[(i * 2) + 1] = -127 * it->real(); } return soft_bits; } diff --git a/src/utils/viter_bi_codec.cpp b/src/utils/viter_bi_codec.cpp index 590e235..3640f76 100644 --- a/src/utils/viter_bi_codec.cpp +++ b/src/utils/viter_bi_codec.cpp @@ -9,7 +9,28 @@ #include "utils/viter_bi_codec.hpp" -auto ViterbiCodec::Decode(const std::vector& bits) const -> std::vector { +auto ViterbiCodecHardDecision::Decode(const std::vector& bits) const -> std::vector { + 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; + const size_t total_data_bits = total_bits - total_tail_bits; + + std::vector output_bytes(total_bits / 8); + + vitdec.set_traceback_length(total_data_bits); + + vitdec.reset(); + 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; + + return output_bytes; +} + +auto ViterbiCodecSoftDecision::Decode(const std::vector& bits) const -> std::vector { auto vitdec = ViterbiDecoder_Core(branch_table, config); using Decoder = ViterbiDecoder_SSE_u16;