Skip to content

Commit

Permalink
refactor lower mac slot processing
Browse files Browse the repository at this point in the history
  • Loading branch information
marenz2569 committed Jun 8, 2024
1 parent 21db7b1 commit c30050e
Show file tree
Hide file tree
Showing 5 changed files with 406 additions and 127 deletions.
28 changes: 28 additions & 0 deletions include/l2/logical_channel.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/*
* Copyright (C) 2024 Transit Live Mapping Solutions
* All rights reserved.
*
* Authors:
* Marenz Schmidl
*/

#pragma once

#include "utils/bit_vector.hpp"

enum class LogicalChannel {
kSignalingChannelHalfDownlink,
kSignalingChannelHalfUplink,
kTrafficChannel,
kSignalingChannelFull,
kStealingChannel
};

struct LogicalChannelDataAndCrc {
/// the logical channel
LogicalChannel channel;
/// the data on the logical channel
BitVector data;
/// true if the crc of the signaling channels is ok
bool crc_ok;
};
10 changes: 6 additions & 4 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 "l2/broadcast_synchronization_channel.hpp"
#include "l2/slot.hpp"
#include "l2/timebase_counter.hpp"
#include <cstddef>
#include <cstdint>
Expand Down Expand Up @@ -63,6 +64,8 @@ class LowerMacPrometheusCounters {
/// The counter for the too many bursts in the downlink lower MAC
prometheus::Counter& lower_mac_burst_too_many_count_;

/// TODO: add gauge for the synchronization burst time

public:
LowerMacPrometheusCounters(std::shared_ptr<PrometheusExporter>& prometheus_exporter)
: burst_received_count_family_(prometheus_exporter->burst_received_count())
Expand Down Expand Up @@ -168,11 +171,10 @@ class LowerMac {
std::optional<uint32_t> scrambling_code = std::nullopt);
~LowerMac() = default;

// 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.
// 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<uint8_t>& frame, BurstType burst_type,
const BroadcastSynchronizationChannel& bsc)
-> std::vector<std::function<void()>>;
const BroadcastSynchronizationChannel& bsc) -> Slots;

/// 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
Expand Down
223 changes: 223 additions & 0 deletions include/l2/slot.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,223 @@
/*
* Copyright (C) 2024 Transit Live Mapping Solutions
* All rights reserved.
*
* Authors:
* Marenz Schmidl
*/

#pragma once

#include "burst_type.hpp"
#include "l2/logical_channel.hpp"
#include <cassert>
#include <set>
#include <vector>

/// describe a slot (full or half) and its content with data and logical channels. it can be non concreate i.e.,
/// multiple logical channels are present and the correct one still needs to be selected
class Slot {
private:
std::vector<LogicalChannelDataAndCrc> data_;

public:
Slot() = delete;

/// construct a slot with a defined channel and data
explicit Slot(LogicalChannelDataAndCrc&& data)
: data_({std::move(data)}){};

/// construct a slot with any of two input data and different channel
explicit Slot(std::vector<LogicalChannelDataAndCrc> data)
: data_(std::move(data)) {
std::set<LogicalChannel> channels_in_data;
for (const auto& channel_data : data_) {
const auto& channel = channel_data.channel;
channels_in_data.insert(channel);
}

if (data_.size() != channels_in_data.size()) {
throw std::runtime_error("Found duplicate entries of channels in initilization of Slot");
}
};

/// if there is only one possibility for a logical channel, the the slot is concreate
[[nodiscard]] auto is_concreate() const noexcept -> bool { return data_.size() == 1; };

/// get the concreate logical channel, data and crc
[[nodiscard]] auto get_logical_channel_data_and_crc() -> LogicalChannelDataAndCrc& {
if (!is_concreate()) {
throw std::runtime_error("Attempted to get a concreate channel that is not concreate.");
}
return data_.front();
}

/// get the set of potential logical channels
[[nodiscard]] auto get_logical_channels() const noexcept -> std::set<LogicalChannel> {
std::set<LogicalChannel> channels;
for (const auto& channel_data : data_) {
const auto& channel = channel_data.channel;
channels.insert(channel);
};
return channels;
}

/// select a specific logical channel and make the slot concreate
auto select_logical_channel(LogicalChannel channel) -> void {
for (auto it = data_.begin(); it != data_.end();) {
if (it->channel != channel) {
it = data_.erase(it);
} else {
++it;
}
}
if (!is_concreate()) {
throw std::runtime_error("Attempted to select a channel that is not availabe.");
}
}
};

/// defines the number and types of slots in a packet
enum class SlotsType { kOneSubslot, kTwoSubslots, kFullSlot };

/// defines the slots in a packet
class Slots {
private:
/// which burst type ths slots originated from
BurstType burst_type_;
/// the number and types of slots
SlotsType slot_type_;
/// the slots, either one half or full slot or two half slots
std::vector<Slot> slots_;

public:
Slots() = delete;

/// constructor for one subslot or a full slot
Slots(BurstType burst_type, SlotsType slot_type, Slot&& slot)
: burst_type_(burst_type)
, slot_type_(slot_type)
, slots_({std::move(slot)}) {
if (slot_type_ == SlotsType::kTwoSubslots) {
throw std::runtime_error("Two subslots need to to have two subslots");
}

/// If we processes the normal uplink burst assume that the slot is signalling and not traffic. We would need
/// the information from the access assignment from the corresponding downlink timeslot to make the right
/// decision here.
if (burst_type_ == BurstType::NormalUplinkBurst) {
get_first_slot().select_logical_channel(LogicalChannel::kSignalingChannelFull);
}

if (!get_first_slot().is_concreate()) {
throw std::runtime_error("The first or only slot is not concreate.");
}
};

/// construct for two half slot
Slots(BurstType burst_type, SlotsType slot_type, Slot&& first_slot, Slot&& second_slot)
: burst_type_(burst_type)
, slot_type_(slot_type)
, slots_({std::move(first_slot), std::move(second_slot)}) {
if (slot_type_ != SlotsType::kTwoSubslots) {
throw std::runtime_error("Only two subslots is allowed to have two subslots");
}

if (!get_first_slot().is_concreate()) {
throw std::runtime_error("The first subslot is not concreate.");
}

const auto& first_slot_data = get_first_slot().get_logical_channel_data_and_crc().data;
if (get_first_slot().get_logical_channel_data_and_crc().channel == LogicalChannel::kStealingChannel) {
// The first subslot is stolen, now we need to decide if the second is also stolen or traffic
auto second_half_slot_stolen = false;

if (burst_type_ == BurstType::NormalUplinkBurstSplit) {
auto pdu_type = first_slot_data.look<2>(0);
// read the stolen flag from the MAC-DATA
// 21.4.2.3 MAC-DATA
if (pdu_type == 0b00) {
auto address_type = first_slot_data.look<2>(4);
auto length_indication_or_capacity_request_offset = 6 + (address_type == 0b01 ? 10 : 24);
auto length_indication_or_capacity_request =
first_slot_data.look<1>(length_indication_or_capacity_request_offset);
if (length_indication_or_capacity_request == 0b0) {
auto length_indication =
first_slot_data.look<6>(length_indication_or_capacity_request_offset + 1);
if (length_indication == 0b111110 || length_indication == 0b111111) {
second_half_slot_stolen = true;
}
}
}

// read the stolen flag from the MAC-U-SIGNAL PDU
// 21.4.5 TMD-SAP: MAC PDU structure for U-plane signalling
if (pdu_type == 0b11) {
if (first_slot_data.look<1>(2)) {
second_half_slot_stolen = true;
};
}
}

if (burst_type_ == BurstType::NormalDownlinkBurstSplit) {
auto pdu_type = first_slot_data.look<2>(0);
// read the sloten flag from the MAC-RESOURCE PDU
// 21.4.3.1 MAC-RESOURCE
if (pdu_type == 0b00) {
auto length_indication = first_slot_data.look<6>(7);
if (length_indication == 0b111110 || length_indication == 0b111111) {
second_half_slot_stolen = true;
}
}

// read the stolen flag from the MAC-U-SIGNAL PDU
// 21.4.5 TMD-SAP: MAC PDU structure for U-plane signalling
if (pdu_type == 0b11) {
if (first_slot_data.look<1>(2)) {
second_half_slot_stolen = true;
};
}
}

get_second_slot().select_logical_channel(second_half_slot_stolen ? LogicalChannel::kStealingChannel
: LogicalChannel::kTrafficChannel);
}

if (!get_second_slot().is_concreate()) {
throw std::runtime_error("The second slot is not concreate.");
}
};

/// access the first slot
[[nodiscard]] auto get_first_slot() noexcept -> Slot& { return slots_.front(); };

/// access the second slot
[[nodiscard]] auto get_second_slot() -> Slot& {
if (!has_second_slot()) {
throw std::runtime_error("Attempted to accesses the second slot, but we do not have two slots.");
}
return slots_.at(1);
};

/// check if we have two subslots
[[nodiscard]] auto has_second_slot() const noexcept -> bool { return slots_.size() == 2; }

// check if there was any crc mismatch on the signalling or stealing channels
[[nodiscard]] auto has_crc_error() -> bool {
auto error = false;

const auto& first_slot = slots_.front().get_logical_channel_data_and_crc();
if (first_slot.channel != LogicalChannel::kTrafficChannel) {
error |= !first_slot.crc_ok;
}

if (has_second_slot()) {
const auto& second_slot = slots_.at(1).get_logical_channel_data_and_crc();
if (second_slot.channel != LogicalChannel::kTrafficChannel) {
error |= !first_slot.crc_ok;
}
}

return error;
};
};
14 changes: 13 additions & 1 deletion include/utils/bit_vector.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,18 @@ class BitVector {
return to_bit_int<N>(bits);
}

/// look at N bits with an offset to the bitvector
template <std::size_t N> [[nodiscard]] auto look(std::size_t offset) const -> unsigned _BitInt(N) {
if (N + offset > bits_left()) {
throw std::runtime_error(std::to_string(N + offset) + " bits not left in BitVec (" +
std::to_string(bits_left()) + ")");
}

const auto bits = data_.begin() + read_offset_ + N + offset;

return to_bit_int<N>(bits);
}

[[nodiscard]] auto compute_fcs() -> uint32_t;

/// bite of a bitvector from the current bitvector
Expand All @@ -94,7 +106,7 @@ class BitVector {

private:
template <std::size_t N>
[[nodiscard]] auto to_bit_int(const std::vector<bool>::const_iterator iterator) -> unsigned _BitInt(N) {
[[nodiscard]] auto to_bit_int(const std::vector<bool>::const_iterator iterator) const -> unsigned _BitInt(N) {
unsigned _BitInt(N) ret = iterator[0];

// This condition is implicitly there on the first iteation of the loop, but not detected by some compilers
Expand Down
Loading

0 comments on commit c30050e

Please sign in to comment.