Skip to content

Commit

Permalink
use softbits as far as possible in the decoding chain
Browse files Browse the repository at this point in the history
  • Loading branch information
marenz2569 committed Jan 8, 2025
1 parent a1cad3a commit ac29912
Show file tree
Hide file tree
Showing 5 changed files with 324 additions and 264 deletions.
3 changes: 3 additions & 0 deletions include/iq_stream_decoder.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,9 @@ class IQStreamDecoder {
template <std::size_t Len, class iterator_type>
static auto symbols_to_bitstream(iterator_type it) -> std::vector<bool>;

template <std::size_t Len, class iterator_type>
static auto symbols_to_softstream(iterator_type it) -> std::vector<int16_t>;

static void abs_convolve_same_length(const QueueT& queueA, std::size_t offsetA, const std::complex<float>* itb,
std::size_t len, float* res);

Expand Down
264 changes: 262 additions & 2 deletions include/l2/lower_mac.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -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<bool>& frame, BurstType burst_type,
const BroadcastSynchronizationChannel& bsc) -> Slots;
/// \targ DataType with bool is selected we have bits, with int16_t we have symbols
template <typename DataType>
[[nodiscard]] auto processChannels(const std::vector<DataType>& frame, BurstType burst_type,
const BroadcastSynchronizationChannel& bsc) -> Slots {
std::optional<Slots> 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<DataType, 30> 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<DataType, 216> 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<DataType, 30> 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<DataType, 432> 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<DataType, 30> 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<DataType, 216> 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<DataType, 216> 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<DataType, 168> 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<DataType, 432> 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<DataType, 216> 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<DataType, 216> 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_;

Expand Down
39 changes: 37 additions & 2 deletions include/l2/lower_mac_coding.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,28 @@
#include <array>
#include <cstdint>
#include <map>
#include <type_traits>
#include <vector>

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 <std::size_t Size, typename InputType>
static auto softbits_to_bits(const std::array<InputType, Size>& input) noexcept -> std::array<bool, Size> {
if constexpr (std::is_same_v<InputType, bool>) {
return input;
} else if constexpr (std::is_same_v<InputType, int16_t>) {
std::array<bool, Size> bits;
for (auto i = 0; i < Size; i++) {
bits[i] = static_cast<bool>(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
*
Expand Down Expand Up @@ -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<Type, bool>) {
if (table[i]) {
output[i] = !input[i];
} else {
output[i] = input[i];
}
} else if constexpr (std::is_same_v<Type, int16_t>) {
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;
Expand Down Expand Up @@ -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<InType, bool>) {
res[k - 1] = data[j - 1] ? 1 : -1;
} else {
res[k - 1] = data[j - 1];
}
}

return res;
Expand Down
Loading

0 comments on commit ac29912

Please sign in to comment.