From ac29912b7268fdcf07581b6ff3774b50bb0b0160 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 8 Jan 2025 03:43:45 +0100 Subject: [PATCH] use softbits as far as possible in the decoding chain --- include/iq_stream_decoder.hpp | 3 + include/l2/lower_mac.hpp | 264 +++++++++++++++++++++++++++++++- include/l2/lower_mac_coding.hpp | 39 ++++- src/iq_stream_decoder.cpp | 28 +++- src/l2/lower_mac.cpp | 254 ------------------------------ 5 files changed, 324 insertions(+), 264 deletions(-) diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index 74e0859..66809dd 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -44,6 +44,9 @@ class IQStreamDecoder { template static auto symbols_to_bitstream(iterator_type it) -> std::vector; + template + static auto symbols_to_softstream(iterator_type it) -> std::vector; + static void abs_convolve_same_length(const QueueT& queueA, std::size_t offsetA, const std::complex* itb, std::size_t len, float* res); diff --git a/include/l2/lower_mac.hpp b/include/l2/lower_mac.hpp index 90bc952..ee65cd9 100644 --- a/include/l2/lower_mac.hpp +++ b/include/l2/lower_mac.hpp @@ -10,6 +10,7 @@ #pragma once #include "burst_type.hpp" +#include "l2/access_assignment_channel.hpp" #include "l2/broadcast_synchronization_channel.hpp" #include "l2/lower_mac_coding.hpp" #include "l2/lower_mac_metrics.hpp" @@ -98,8 +99,267 @@ class LowerMac { private: // does the signal processing and then returns the slots containing the correct logical channels and their // associated data to be passed to the upper mac and further processed in a sequential order. - [[nodiscard]] auto processChannels(const std::vector& frame, BurstType burst_type, - const BroadcastSynchronizationChannel& bsc) -> Slots; + /// \targ DataType with bool is selected we have bits, with int16_t we have symbols + template + [[nodiscard]] auto processChannels(const std::vector& frame, BurstType burst_type, + const BroadcastSynchronizationChannel& bsc) -> Slots { + std::optional slots; + + // 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. + // + // if any of the downlink functions need some data from the upper mac, + // except for the scrambling code, preprocess everything and then do + // the access to the data in the upper mac in the correct order! + // + // The scrambling code has a special handling, see the specific comments where + // it is used. + + if (burst_type == BurstType::SynchronizationBurst) { + // bb contains AACH + // ✅ done + std::array bb_input{}; + for (auto i = 0; i < 30; i++) { + bb_input[i] = frame[252 + i]; + } + + auto bb_rm = LowerMacCoding::reed_muller_3014_decode( + LowerMacCoding::softbits_to_bits(LowerMacCoding::descramble(bb_input, bsc.scrambling_code))); + auto _aach = + AccessAssignmentChannel(burst_type, bsc.time, BitVector(std::vector(bb_rm.cbegin(), bb_rm.cend()))); + + // bkn2 block + // ✅ done + // 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) + std::array bkn2_input{}; + for (auto i = 0; i < 216; i++) { + bkn2_input[i] = frame[282 + i]; + } + + auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614( + viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(bkn2_input, bsc.scrambling_code), 101))); + + slots = Slots(burst_type, SlotType::kOneSubslot, + Slot(LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kSignallingChannelHalfDownlink, + .data = BitVector(std::vector(bkn2_bits.cbegin(), bkn2_bits.cbegin() + 124)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn2_bits), + })); + } else if (burst_type == BurstType::NormalDownlinkBurst) { + // bb contains AACH + // ✅ + std::array bb_input{}; + for (auto i = 0; i < 30; i++) { + auto offset = i > 14 ? 266 - 14 : 230; + bb_input[i] = frame[offset + i]; + } + + auto bb_rm = LowerMacCoding::reed_muller_3014_decode( + LowerMacCoding::softbits_to_bits(LowerMacCoding::descramble(bb_input, bsc.scrambling_code))); + auto aach = + AccessAssignmentChannel(burst_type, bsc.time, BitVector(std::vector(bb_rm.cbegin(), bb_rm.cend()))); + + // TCH or SCH/F + std::array bkn1_input{}; + for (auto i = 0; i < 432; i++) { + auto offset = i > 216 ? 282 - 216 : 14; + bkn1_input[i] = frame[offset + i]; + }; + + auto bkn1_descrambled = LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code); + auto bkn1_descrambled_bits = LowerMacCoding::softbits_to_bits(bkn1_descrambled); + + auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( + viter_bi_codec_1614_, + LowerMacCoding::depuncture23(LowerMacCoding::deinterleave(bkn1_descrambled, 103))); + + if (aach.downlink_usage == DownlinkUsage::Traffic) { + // Full slot traffic channel defined type 4 bits (only descrambling) + slots = Slots( + burst_type, SlotType::kFullSlot, + Slot(LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kTrafficChannel, + .data = BitVector(std::vector(bkn1_descrambled_bits.cbegin(), bkn1_descrambled_bits.cend())), + .crc_ok = true, + })); + } else { + // control channel + // ✅done + slots = Slots(burst_type, SlotType::kFullSlot, + Slot(LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kSignallingChannelFull, + .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 268)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<284>(bkn1_bits), + })); + } + } else if (burst_type == BurstType::NormalDownlinkBurstSplit) { + // bb contains AACH + // ✅ done + std::array bb_input{}; + for (auto i = 0; i < 30; i++) { + auto offset = i > 14 ? 266 - 14 : 230; + bb_input[i] = frame[offset + i]; + } + + auto bb_rm = LowerMacCoding::reed_muller_3014_decode( + LowerMacCoding::softbits_to_bits(LowerMacCoding::descramble(bb_input, bsc.scrambling_code))); + auto aach = + AccessAssignmentChannel(burst_type, bsc.time, BitVector(std::vector(bb_rm.cbegin(), bb_rm.cend()))); + + std::array bkn1_input{}; + for (auto i = 0; i < 216; i++) { + bkn1_input[i] = frame[14 + i]; + }; + + auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( + viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code), 101))); + + std::array bkn2_input{}; + for (auto i = 0; i < 216; i++) { + bkn2_input[i] = frame[282 + i]; + } + + 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_, + LowerMacCoding::depuncture23(bkn2_deinterleaved)); + + // Half slot traffic channel defines type 3 bits (deinterleaved) + if (aach.downlink_usage == DownlinkUsage::Traffic) { + // STCH + TCH + // STCH + STCH + slots = Slots(burst_type, SlotType::kTwoSubslots, + Slot(LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kStealingChannel, + .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 124)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn1_bits), + }), + Slot({LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kStealingChannel, + .data = BitVector(std::vector(bkn2_bits.cbegin(), bkn2_bits.cbegin() + 124)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn2_bits), + }, + LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kTrafficChannel, + .data = BitVector(std::vector(bkn2_deinterleaved_bits.cbegin(), + bkn2_deinterleaved_bits.cend())), + .crc_ok = true, + }})); + } else { + // SCH/HD + SCH/HD + // SCH/HD + BNCH + slots = Slots(burst_type, SlotType::kTwoSubslots, + Slot(LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kSignallingChannelHalfDownlink, + .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 124)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn1_bits), + }), + Slot(LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kSignallingChannelHalfDownlink, + .data = BitVector(std::vector(bkn2_bits.cbegin(), bkn2_bits.cbegin() + 124)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn2_bits), + })); + } + } else if (burst_type == BurstType::ControlUplinkBurst) { + std::array cb_input{}; + for (auto i = 0; i < 168; i++) { + auto offset = i > 84 ? 118 - 84 : 4; + cb_input[i] = frame[offset + i]; + }; + + auto cb_bits = LowerMacCoding::viter_bi_decode_1614( + viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(cb_input, bsc.scrambling_code), 13))); + + // SCH/HU + slots = Slots(burst_type, SlotType::kOneSubslot, + Slot(LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kSignallingChannelHalfUplink, + .data = BitVector(std::vector(cb_bits.cbegin(), cb_bits.cbegin() + 92)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<108>(cb_bits), + })); + } else if (burst_type == BurstType::NormalUplinkBurst) { + std::array bkn1_input{}; + for (auto i = 0; i < 432; i++) { + auto offset = i > 216 ? 242 - 216 : 4; + bkn1_input[i] = frame[offset + i]; + } + + // TODO: this can either be a SCH_H or a TCH, depending on the uplink usage marker, but the uplink + // and downlink processing are seperated. We assume a SCH_H here. + auto bkn1_descrambled = LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code); + auto bkn1_descrambled_bits = LowerMacCoding::softbits_to_bits(bkn1_descrambled); + + auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( + viter_bi_codec_1614_, + LowerMacCoding::depuncture23(LowerMacCoding::deinterleave(bkn1_descrambled, 103))); + + slots = Slots( + burst_type, SlotType::kFullSlot, + Slot({ + LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kSignallingChannelFull, + .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 268)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<284>(bkn1_bits), + }, + LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kTrafficChannel, + .data = BitVector(std::vector(bkn1_descrambled_bits.cbegin(), bkn1_descrambled_bits.cend())), + .crc_ok = true, + }, + })); + } else if (burst_type == BurstType::NormalUplinkBurstSplit) { + std::array bkn1_input{}; + for (auto i = 0; i < 216; i++) { + bkn1_input[i] = frame[4 + i]; + } + + auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( + viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( + LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code), 101))); + + std::array bkn2_input{}; + for (auto i = 0; i < 216; i++) { + bkn2_input[i] = frame[242 + i]; + }; + + 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_, + LowerMacCoding::depuncture23(bkn2_deinterleaved)); + + // STCH + TCH + // STCH + STCH + slots = Slots(burst_type, SlotType::kTwoSubslots, + Slot(LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kStealingChannel, + .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 124)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn1_bits), + }), + Slot({LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kStealingChannel, + .data = BitVector(std::vector(bkn2_bits.cbegin(), bkn2_bits.cbegin() + 124)), + .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn2_bits), + }, + LogicalChannelDataAndCrc{ + .channel = LogicalChannel::kTrafficChannel, + .data = BitVector( + std::vector(bkn2_deinterleaved_bits.cbegin(), bkn2_deinterleaved_bits.cend())), + .crc_ok = true, + }})); + } else { + throw std::runtime_error("LowerMac does not implement the burst type supplied"); + } + + return *slots; + } const ViterbiCodec viter_bi_codec_1614_; diff --git a/include/l2/lower_mac_coding.hpp b/include/l2/lower_mac_coding.hpp index f7565db..0bd841f 100644 --- a/include/l2/lower_mac_coding.hpp +++ b/include/l2/lower_mac_coding.hpp @@ -23,10 +23,28 @@ #include #include #include +#include #include struct LowerMacCoding { + /// Convert a type to bits. If we already input bits, we push them straight through, if we input a soft symbol we + /// decode it to aa bit first. + template + static auto softbits_to_bits(const std::array& input) noexcept -> std::array { + if constexpr (std::is_same_v) { + return input; + } else if constexpr (std::is_same_v) { + std::array bits; + for (auto i = 0; i < Size; i++) { + bits[i] = static_cast(input[i] > 0); + } + return bits; + } else { + assert(false && "Compiling descramble with template paramater not bool or int16_t"); + } + } + /** * @brief Fibonacci LFSR descrambling - 8.2.5 * @@ -63,7 +81,20 @@ struct LowerMacCoding { auto& table = table_by_scrambling_code[scrambling_code]; for (std::size_t i = 0; i < Size; i++) { - output[i] = input[i] ^ table[i]; + if constexpr (std::is_same_v) { + if (table[i]) { + output[i] = !input[i]; + } else { + output[i] = input[i]; + } + } else if constexpr (std::is_same_v) { + output[i] = input[i]; + if (table[i]) { + output[i] *= -1; + } + } else { + assert(false && "Compiling descramble with template paramater not bool or int16_t"); + } } return output; @@ -102,7 +133,11 @@ struct LowerMacCoding { 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] ? 1 : -1; + if constexpr (std::is_same_v) { + res[k - 1] = data[j - 1] ? 1 : -1; + } else { + res[k - 1] = data[j - 1]; + } } return res; diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index d09b7cb..aa1b996 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -60,6 +60,22 @@ auto IQStreamDecoder::symbols_to_bitstream(iterator_type it) -> std::vector +auto IQStreamDecoder::symbols_to_softstream(iterator_type it) -> std::vector { + std::vector soft_bits(Len * 2); + for (std::size_t i = 0; i < Len; ++it, ++i) { + // symbol 0: + // imag > 0 -> 0 -> -1 + // imag <= 0 -> 1 -> 1 + // symbol 1: + // real > 0 -> 0 -> -1 + // real <= 0 -> 1 -> 1 + soft_bits[i * 2] = -1 * it->imag(); + soft_bits[(i * 2) + 1] = -1 * it->real(); + } + return soft_bits; +} + 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) { @@ -154,30 +170,30 @@ void IQStreamDecoder::process_complex(std::complex symbol) noexcept { auto channel = solve_channel<3>(training_seq_x_, symbol_buffer_hard_decision_, 44); std::cout << channel << std::endl; - auto bits = symbols_to_bitstream<103>(symbol_buffer_.cbegin()); + auto softbits = symbols_to_softstream<103>(symbol_buffer_.cbegin()); auto lower_mac_process_cub = - std::bind(&LowerMac::process, lower_mac_, bits, BurstType::ControlUplinkBurst); + std::bind(&LowerMac::process, lower_mac_, softbits, BurstType::ControlUplinkBurst); lower_mac_worker_queue_->queue_work(lower_mac_process_cub); } if (detectedP >= SEQUENCE_DETECTION_THRESHOLD) { // std::cout << "Potential NUB_Split found" << std::endl; - auto bits = symbols_to_bitstream<231>(symbol_buffer_.cbegin()); + auto softbits = symbols_to_softstream<231>(symbol_buffer_.cbegin()); auto lower_mac_process_nubs = - std::bind(&LowerMac::process, lower_mac_, bits, BurstType::NormalUplinkBurstSplit); + std::bind(&LowerMac::process, lower_mac_, softbits, BurstType::NormalUplinkBurstSplit); lower_mac_worker_queue_->queue_work(lower_mac_process_nubs); } if (detectedN >= SEQUENCE_DETECTION_THRESHOLD) { // std::cout << "Potential NUB found" << std::endl; - auto bits = symbols_to_bitstream<231>(symbol_buffer_.cbegin()); + auto softbits = symbols_to_softstream<231>(symbol_buffer_.cbegin()); auto lower_mac_process_nub = - std::bind(&LowerMac::process, lower_mac_, bits, BurstType::NormalUplinkBurst); + std::bind(&LowerMac::process, lower_mac_, softbits, BurstType::NormalUplinkBurst); lower_mac_worker_queue_->queue_work(lower_mac_process_nub); } } else { diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 5b035c4..b52be69 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -8,20 +8,12 @@ */ #include "l2/lower_mac.hpp" -#include "burst_type.hpp" -#include "l2/access_assignment_channel.hpp" #include "l2/broadcast_synchronization_channel.hpp" -#include "l2/logical_channel.hpp" -#include "l2/lower_mac_coding.hpp" -#include "l2/slot.hpp" -#include "utils/bit_vector.hpp" -#include #include #include #include #include #include -#include LowerMac::LowerMac(const std::shared_ptr& prometheus_exporter, std::optional scrambling_code) { @@ -35,250 +27,4 @@ LowerMac::LowerMac(const std::shared_ptr& prometheus_exporte if (prometheus_exporter) { metrics_ = std::make_unique(prometheus_exporter); } -} - -auto LowerMac::processChannels(const std::vector& frame, BurstType burst_type, - const BroadcastSynchronizationChannel& bsc) -> Slots { - std::optional slots; - - // 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. - // - // if any of the downlink functions need some data from the upper mac, - // except for the scrambling code, preprocess everything and then do - // the access to the data in the upper mac in the correct order! - // - // The scrambling code has a special handling, see the specific comments where - // it is used. - - if (burst_type == BurstType::SynchronizationBurst) { - // bb contains AACH - // ✅ done - std::array bb_input{}; - for (auto i = 0; i < 30; i++) { - bb_input[i] = frame[252 + i]; - } - - auto bb_rm = LowerMacCoding::reed_muller_3014_decode(LowerMacCoding::descramble(bb_input, bsc.scrambling_code)); - auto _aach = - AccessAssignmentChannel(burst_type, bsc.time, BitVector(std::vector(bb_rm.cbegin(), bb_rm.cend()))); - - // bkn2 block - // ✅ done - // 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) - std::array bkn2_input{}; - for (auto i = 0; i < 216; i++) { - bkn2_input[i] = frame[282 + i]; - } - - auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(bkn2_input, bsc.scrambling_code), 101))); - - slots = Slots(burst_type, SlotType::kOneSubslot, - Slot(LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kSignallingChannelHalfDownlink, - .data = BitVector(std::vector(bkn2_bits.cbegin(), bkn2_bits.cbegin() + 124)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn2_bits), - })); - } else if (burst_type == BurstType::NormalDownlinkBurst) { - // bb contains AACH - // ✅ - std::array bb_input{}; - for (auto i = 0; i < 30; i++) { - auto offset = i > 14 ? 266 - 14 : 230; - bb_input[i] = frame[offset + i]; - } - - auto bb_rm = LowerMacCoding::reed_muller_3014_decode(LowerMacCoding::descramble(bb_input, bsc.scrambling_code)); - auto aach = AccessAssignmentChannel(burst_type, bsc.time, BitVector(std::vector(bb_rm.cbegin(), bb_rm.cend()))); - - // TCH or SCH/F - std::array bkn1_input{}; - for (auto i = 0; i < 432; i++) { - auto offset = i > 216 ? 282 - 216 : 14; - bkn1_input[i] = frame[offset + i]; - }; - - auto bkn1_descrambled = LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code); - - auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave(bkn1_descrambled, 103))); - - if (aach.downlink_usage == DownlinkUsage::Traffic) { - // Full slot traffic channel defined type 4 bits (only descrambling) - slots = Slots(burst_type, SlotType::kFullSlot, - Slot(LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kTrafficChannel, - .data = BitVector(std::vector(bkn1_descrambled.cbegin(), bkn1_descrambled.cend())), - .crc_ok = true, - })); - } else { - // control channel - // ✅done - slots = Slots(burst_type, SlotType::kFullSlot, - Slot(LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kSignallingChannelFull, - .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 268)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<284>(bkn1_bits), - })); - } - } else if (burst_type == BurstType::NormalDownlinkBurstSplit) { - // bb contains AACH - // ✅ done - std::array bb_input{}; - for (auto i = 0; i < 30; i++) { - auto offset = i > 14 ? 266 - 14 : 230; - bb_input[i] = frame[offset + i]; - } - - auto bb_rm = LowerMacCoding::reed_muller_3014_decode(LowerMacCoding::descramble(bb_input, bsc.scrambling_code)); - auto aach = AccessAssignmentChannel(burst_type, bsc.time, BitVector(std::vector(bb_rm.cbegin(), bb_rm.cend()))); - - std::array bkn1_input{}; - for (auto i = 0; i < 216; i++) { - bkn1_input[i] = frame[14 + i]; - }; - - auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code), 101))); - - std::array bkn2_input{}; - for (auto i = 0; i < 216; i++) { - bkn2_input[i] = frame[282 + i]; - } - - auto bkn2_deinterleaved = - LowerMacCoding::deinterleave(LowerMacCoding::descramble(bkn2_input, bsc.scrambling_code), 101); - auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614(viter_bi_codec_1614_, - LowerMacCoding::depuncture23(bkn2_deinterleaved)); - - // Half slot traffic channel defines type 3 bits (deinterleaved) - if (aach.downlink_usage == DownlinkUsage::Traffic) { - // STCH + TCH - // STCH + STCH - slots = - Slots(burst_type, SlotType::kTwoSubslots, - Slot(LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kStealingChannel, - .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 124)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn1_bits), - }), - Slot({LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kStealingChannel, - .data = BitVector(std::vector(bkn2_bits.cbegin(), bkn2_bits.cbegin() + 124)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn2_bits), - }, - LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kTrafficChannel, - .data = BitVector(std::vector(bkn2_deinterleaved.cbegin(), bkn2_deinterleaved.cend())), - .crc_ok = true, - }})); - } else { - // SCH/HD + SCH/HD - // SCH/HD + BNCH - slots = Slots(burst_type, SlotType::kTwoSubslots, - Slot(LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kSignallingChannelHalfDownlink, - .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 124)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn1_bits), - }), - Slot(LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kSignallingChannelHalfDownlink, - .data = BitVector(std::vector(bkn2_bits.cbegin(), bkn2_bits.cbegin() + 124)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn2_bits), - })); - } - } else if (burst_type == BurstType::ControlUplinkBurst) { - std::array cb_input{}; - for (auto i = 0; i < 168; i++) { - auto offset = i > 84 ? 118 - 84 : 4; - cb_input[i] = frame[offset + i]; - }; - - auto cb_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(cb_input, bsc.scrambling_code), 13))); - - // SCH/HU - slots = Slots(burst_type, SlotType::kOneSubslot, - Slot(LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kSignallingChannelHalfUplink, - .data = BitVector(std::vector(cb_bits.cbegin(), cb_bits.cbegin() + 92)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<108>(cb_bits), - })); - } else if (burst_type == BurstType::NormalUplinkBurst) { - std::array bkn1_input{}; - for (auto i = 0; i < 432; i++) { - auto offset = i > 216 ? 242 - 216 : 4; - bkn1_input[i] = frame[offset + i]; - } - - // TODO: this can either be a SCH_H or a TCH, depending on the uplink usage marker, but the uplink - // and downlink processing are seperated. We assume a SCH_H here. - auto bkn1_descrambled = LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code); - - auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave(bkn1_descrambled, 103))); - - slots = Slots(burst_type, SlotType::kFullSlot, - Slot({ - LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kSignallingChannelFull, - .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 268)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<284>(bkn1_bits), - }, - LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kTrafficChannel, - .data = BitVector(std::vector(bkn1_descrambled.cbegin(), bkn1_descrambled.cend())), - .crc_ok = true, - }, - })); - } else if (burst_type == BurstType::NormalUplinkBurstSplit) { - std::array bkn1_input{}; - for (auto i = 0; i < 216; i++) { - bkn1_input[i] = frame[4 + i]; - } - - auto bkn1_bits = LowerMacCoding::viter_bi_decode_1614( - viter_bi_codec_1614_, LowerMacCoding::depuncture23(LowerMacCoding::deinterleave( - LowerMacCoding::descramble(bkn1_input, bsc.scrambling_code), 101))); - - std::array bkn2_input{}; - for (auto i = 0; i < 216; i++) { - bkn2_input[i] = frame[242 + i]; - }; - - auto bkn2_deinterleaved = - LowerMacCoding::deinterleave(LowerMacCoding::descramble(bkn2_input, bsc.scrambling_code), 101); - auto bkn2_bits = LowerMacCoding::viter_bi_decode_1614(viter_bi_codec_1614_, - LowerMacCoding::depuncture23(bkn2_deinterleaved)); - - // STCH + TCH - // STCH + STCH - slots = Slots(burst_type, SlotType::kTwoSubslots, - Slot(LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kStealingChannel, - .data = BitVector(std::vector(bkn1_bits.cbegin(), bkn1_bits.cbegin() + 124)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn1_bits), - }), - Slot({LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kStealingChannel, - .data = BitVector(std::vector(bkn2_bits.cbegin(), bkn2_bits.cbegin() + 124)), - .crc_ok = LowerMacCoding::check_crc_16_ccitt<140>(bkn2_bits), - }, - LogicalChannelDataAndCrc{ - .channel = LogicalChannel::kTrafficChannel, - .data = BitVector(std::vector(bkn2_deinterleaved.cbegin(), bkn2_deinterleaved.cend())), - .crc_ok = true, - }})); - } else { - throw std::runtime_error("LowerMac does not implement the burst type supplied"); - } - - return *slots; } \ No newline at end of file