From 94aa9a5476a479c243d895549e8a6bcb492fb29d Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 11 Jun 2024 15:55:21 +0200 Subject: [PATCH] initial refactor of upper mac --- CMakeLists.txt | 4 +- include/burst_type.hpp | 17 + include/l2/logical_channel.hpp | 15 + include/l2/slot.hpp | 26 +- include/l2/upper_mac.hpp | 97 +-- include/l2/upper_mac_packet.hpp | 445 ++++++++++++ include/l2/upper_mac_packet_builder.hpp | 73 ++ include/l3/mobile_link_entity.hpp | 24 +- include/utils/bit_vector.hpp | 16 +- src/l2/lower_mac.cpp | 35 +- src/l2/slot.cpp | 29 + src/l2/upper_mac.cpp | 916 ------------------------ src/l2/upper_mac_packet.cpp | 345 +++++++++ src/l2/upper_mac_packet_builder.cpp | 815 +++++++++++++++++++++ src/l3/mobile_link_entity.cpp | 75 +- src/utils/bit_vector.cpp | 11 +- 16 files changed, 1805 insertions(+), 1138 deletions(-) create mode 100644 include/l2/upper_mac_packet.hpp create mode 100644 include/l2/upper_mac_packet_builder.hpp create mode 100644 src/l2/slot.cpp delete mode 100644 src/l2/upper_mac.cpp create mode 100644 src/l2/upper_mac_packet.cpp create mode 100644 src/l2/upper_mac_packet_builder.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 51bfd18..9d5c789 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -17,8 +17,10 @@ add_executable(tetra-decoder src/l2/broadcast_synchronization_channel.cpp src/l2/logical_link_control.cpp src/l2/lower_mac.cpp + src/l2/slot.cpp src/l2/timebase_counter.cpp - src/l2/upper_mac.cpp + src/l2/upper_mac_packet.cpp + src/l2/upper_mac_packet_builder.cpp src/l2/upper_mac_fragmentation.cpp src/l3/mobile_link_entity.cpp src/l3/mobile_management.cpp diff --git a/include/burst_type.hpp b/include/burst_type.hpp index 348ba50..e52b2ac 100644 --- a/include/burst_type.hpp +++ b/include/burst_type.hpp @@ -21,6 +21,23 @@ enum class BurstType { SynchronizationBurst, }; +constexpr auto to_string(BurstType type) noexcept -> const char* { + switch (type) { + case BurstType::ControlUplinkBurst: + return "ControlUplinkBurst"; + case BurstType::NormalUplinkBurst: + return "NormalUplinkBurst"; + case BurstType::NormalUplinkBurstSplit: + return "NormalUplinkBurstSplit"; + case BurstType::NormalDownlinkBurst: + return "NormalDownlinkBurst"; + case BurstType::NormalDownlinkBurstSplit: + return "NormalDownlinkBurstSplit"; + case BurstType::SynchronizationBurst: + return "SynchronizationBurst"; + } +}; + [[nodiscard]] inline auto is_uplink_burst(BurstType burst_type) noexcept -> bool { return burst_type == BurstType::ControlUplinkBurst || burst_type == BurstType::NormalUplinkBurst || burst_type == BurstType::NormalUplinkBurstSplit; diff --git a/include/l2/logical_channel.hpp b/include/l2/logical_channel.hpp index ac645e9..1a1c017 100644 --- a/include/l2/logical_channel.hpp +++ b/include/l2/logical_channel.hpp @@ -18,6 +18,21 @@ enum class LogicalChannel { kStealingChannel }; +constexpr auto to_string(LogicalChannel channel) noexcept -> const char* { + switch (channel) { + case LogicalChannel::kSignalingChannelHalfDownlink: + return "SignalingChannelHalfDownlink"; + case LogicalChannel::kSignalingChannelHalfUplink: + return "SignalingChannelHalfUplink"; + case LogicalChannel::kTrafficChannel: + return "TrafficChannel"; + case LogicalChannel::kSignalingChannelFull: + return "SignalingChannelFull"; + case LogicalChannel::kStealingChannel: + return "StealingChannel"; + } +}; + struct LogicalChannelDataAndCrc { /// the logical channel LogicalChannel channel; diff --git a/include/l2/slot.hpp b/include/l2/slot.hpp index b1117de..d2c8c8d 100644 --- a/include/l2/slot.hpp +++ b/include/l2/slot.hpp @@ -75,11 +75,26 @@ class Slot { throw std::runtime_error("Attempted to select a channel that is not availabe."); } } + + friend auto operator<<(std::ostream& stream, const Slot& slot) -> std::ostream&; }; +auto operator<<(std::ostream& stream, const Slot& slot) -> std::ostream&; + /// defines the number and types of slots in a packet enum class SlotsType { kOneSubslot, kTwoSubslots, kFullSlot }; +constexpr auto to_string(SlotsType type) noexcept -> const char* { + switch (type) { + case SlotsType::kOneSubslot: + return "OneSubslot"; + case SlotsType::kTwoSubslots: + return "TwoSubslots"; + case SlotsType::kFullSlot: + return "FullSlot"; + } +}; + /// defines the slots in a packet class Slots { private: @@ -93,6 +108,8 @@ class Slots { public: Slots() = delete; + Slots(const Slots&) = default; + /// constructor for one subslot or a full slot Slots(BurstType burst_type, SlotsType slot_type, Slot&& slot) : burst_type_(burst_type) @@ -220,4 +237,11 @@ class Slots { return error; }; -}; \ No newline at end of file + + /// get the type of the underlying burst + [[nodiscard]] auto get_burst_type() const noexcept -> BurstType { return burst_type_; } + + friend auto operator<<(std::ostream& stream, const Slots& slots) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const Slots& slots) -> std::ostream&; \ No newline at end of file diff --git a/include/l2/upper_mac.hpp b/include/l2/upper_mac.hpp index 9564fff..d719ab5 100644 --- a/include/l2/upper_mac.hpp +++ b/include/l2/upper_mac.hpp @@ -32,50 +32,9 @@ class UpperMac { , logical_link_control_(std::make_unique(reporter_, mobile_link_entity_)){}; ~UpperMac() noexcept = default; - // Signalling CHannel for mapping onto Half-bursts on the Downlink - void process_SCH_HD(BurstType burst_type, BitVector& vec); - // Signalling CHannel for mapping onto Half-bursts on the Uplink - void process_SCH_HU(BurstType burst_type, BitVector& vec); - // Signalling CHannel for mapping onto Full bursts - void process_SCH_F(BurstType burst_type, BitVector& vec); - // STealing CHannel - void process_STCH(BurstType burst_type, BitVector& vec); - - [[nodiscard]] auto downlink_frequency() const noexcept -> int32_t { return downlink_frequency_; } - [[nodiscard]] auto uplink_frequency() const noexcept -> int32_t { return uplink_frequency_; } - [[nodiscard]] auto second_slot_stolen() const noexcept -> bool { return second_slot_stolen_; } - - friend auto operator<<(std::ostream& stream, const UpperMac& upperMac) -> std::ostream&; - private: std::shared_ptr reporter_{}; - void process_signalling_channel(BurstType burst_type, BitVector& vec, bool isHalfChannel, bool isStolenChannel); - void process_signalling_channel_internal(BurstType burst_type, BitVector& vec, bool isHalfChannel, - bool isStolenChannel); - - void process_broadcast(BitVector& vec); - void process_supplementary_mac_pdu(BurstType burst_type, BitVector& vec); - - // TMA-SAP Uplink - void process_mac_access(BitVector& vec); - void process_mac_end_hu(BitVector& vec); - void process_mac_data(BitVector& vec); - void process_mac_frag_uplink(BitVector& vec); - void process_mac_end_uplink(BitVector& vec); - void process_mac_u_blck(BitVector& vec); - // TMA-SAP Downlink - void process_mac_resource(BitVector& vec); - void process_mac_frag_downlink(BitVector& vec); - void process_mac_end_downlink(BitVector& vec); - void process_mac_d_blck(BitVector& vec); - // TMB-SAP broadcast - void process_system_info_pdu(BitVector& vec); - static void process_access_define_pdu(BitVector& vec); - void process_system_info_da(BitVector& vec){}; - // TMD-SAP - void process_mac_usignal(BitVector& vec); - // fragmentation // XXX: might have to delay processing as SSI may only be known after the Null PDU void fragmentation_start_burst(); @@ -85,58 +44,6 @@ class UpperMac { void fragmentation_push_tm_sdu_end(BitVector& vec); void fragmentation_push_tm_sdu_end_hu(BitVector& vec); - void remove_fill_bits(BitVector& vec); - bool remove_fill_bits_{}; - - // SYSINFO PDU - bool system_info_received_ = false; - int32_t downlink_frequency_ = 0; - int32_t uplink_frequency_ = 0; - uint8_t number_secondary_control_channels_main_carrier_{}; - uint8_t ms_txpwr_max_cell_{}; - uint8_t rxlev_access_min_{}; - uint8_t access_parameter_{}; - uint8_t radio_downlink_timeout_{}; - uint8_t hyper_frame_cipher_key_flag_{}; - uint8_t hyper_frame_number_{}; - uint8_t common_cipher_key_identifier_or_static_cipher_key_version_number_{}; - std::optional even_multi_frame_definition_for_ts_mode_; - std::optional odd_multi_frame_definition_for_ts_mode_; - - struct DefaultDefinitionForAccessCodeA { - bool system_info_ = false; - uint8_t immediate_ = 0; - uint8_t waiting_time_ = 0; - uint8_t number_of_random_access_transmissions_on_up_link_ = 0; - uint8_t frame_length_factor_ = 0; - uint8_t timeslot_pointer_ = 0; - uint8_t minimum_pdu_priority_ = 0; - } defaults_for_access_code_a_; - - struct ExtendedServiceBroadcast { - // TODO: I dont know how this works but maybe smart making for the sections - // separate structs which then live in std::optionals - bool system_info_ = false; - uint8_t security_information_ = 0; - uint8_t sdstl_addressing_method_ = 0; - uint8_t gck_supported_ = 0; - - bool system_info_section_1_ = false; - uint8_t data_priority_supported_ = 0; - uint8_t extended_advanced_links_and_max_ublck_supported_ = 0; - uint8_t qos_negotiation_supported_ = 0; - uint8_t d8psk_service_ = 0; - - bool system_info_section_2_ = false; - uint8_t service_25Qam_ = 0; - uint8_t service_50Qam_ = 0; - uint8_t service_100Qam_ = 0; - uint8_t service_150Qam_ = 0; - } extended_service_broadcast_; - - // STCH - bool second_slot_stolen_{}; - std::shared_ptr mobile_link_entity_{}; std::unique_ptr logical_link_control_{}; @@ -148,6 +55,4 @@ class UpperMac { AddressType last_address_type_{}; // save the last MAC-ACCESS or MAC-DATA where reservation_requirement is 0b0000 (1 sublot) for END-HU AddressType last_address_type_end_hu_{}; -}; - -std::ostream& operator<<(std::ostream& stream, const UpperMac& upperMac); \ No newline at end of file +}; \ No newline at end of file diff --git a/include/l2/upper_mac_packet.hpp b/include/l2/upper_mac_packet.hpp new file mode 100644 index 0000000..6124b90 --- /dev/null +++ b/include/l2/upper_mac_packet.hpp @@ -0,0 +1,445 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l2/logical_channel.hpp" +#include "utils/address_type.hpp" +#include "utils/bit_vector.hpp" +#include +#include + +enum class MacPacketType { + // downlink c-plane + kMacResource, + kMacFragmentDownlink, + kMacEndDownlink, + kMacDBlck, + kMacBroadcast, + + // uplink c-plane (SCH/HU) + kMacAccess, + kMacEndHu, + + // uplink c-plane + kMacData, + kMacFragmentUplink, + kMacEndUplink, + kMacUBlck, + + // (uplink and downlink) u-plane signalling + kMacUSignal, +}; + +constexpr auto to_string(MacPacketType type) noexcept -> const char* { + switch (type) { + case MacPacketType::kMacAccess: + return "MacAccess"; + case MacPacketType::kMacResource: + return "MacResource"; + case MacPacketType::kMacFragmentDownlink: + return "MacFragmentDownlink"; + case MacPacketType::kMacEndDownlink: + return "MacEndDownlink"; + case MacPacketType::kMacDBlck: + return "MacDBlck"; + case MacPacketType::kMacEndHu: + return "MacEndHu"; + case MacPacketType::kMacData: + return "MacData"; + case MacPacketType::kMacFragmentUplink: + return "MacFragmentUplink"; + case MacPacketType::kMacEndUplink: + return "MacEndUplink"; + case MacPacketType::kMacUBlck: + return "MacUBlck"; + case MacPacketType::kMacUSignal: + return "MacUSignal"; + case MacPacketType::kMacBroadcast: + return "MacBroadcast"; + } +}; + +struct AccessCodeDefinition { + unsigned _BitInt(4) immediate_; + unsigned _BitInt(4) waiting_time_; + unsigned _BitInt(4) number_of_random_access_transmissions_on_up_link_; + unsigned _BitInt(1) frame_length_factor_; + unsigned _BitInt(4) timeslot_pointer_; + unsigned _BitInt(3) minimum_pdu_priority_; + + AccessCodeDefinition() = delete; + explicit AccessCodeDefinition(BitVector& data) + : immediate_(data.take<4>()) + , waiting_time_(data.take<4>()) + , number_of_random_access_transmissions_on_up_link_(data.take<4>()) + , frame_length_factor_(data.take<1>()) + , timeslot_pointer_(data.take<4>()) + , minimum_pdu_priority_(data.take<3>()) {} + + friend auto operator<<(std::ostream& stream, const AccessCodeDefinition& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const AccessCodeDefinition& element) -> std::ostream&; + +struct ExtendedServiceBroadcastSection1 { + unsigned _BitInt(1) data_priority_supported_; + unsigned _BitInt(1) extended_advanced_links_and_max_ublck_supported_; + unsigned _BitInt(1) qos_negotiation_supported_; + unsigned _BitInt(1) d8psk_service_; + unsigned _BitInt(1) section2_sent_; + unsigned _BitInt(1) section3_sent_; + unsigned _BitInt(1) section4_sent_; + + friend auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection1& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection1& element) -> std::ostream&; + +struct ExtendedServiceBroadcastSection2 { + unsigned _BitInt(1) service_25Qam_; + unsigned _BitInt(1) service_50Qam_; + unsigned _BitInt(1) service_100Qam_; + unsigned _BitInt(1) service_150Qam_; + unsigned _BitInt(3) reserved_; + + friend auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection2& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection2& element) -> std::ostream&; + +struct ExtendedServiceBroadcastSection3 { + unsigned _BitInt(7) reserved_; + + friend auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection3& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection3& element) -> std::ostream&; + +struct ExtendedServiceBroadcastSection4 { + unsigned _BitInt(7) reserved_; + + friend auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection4& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection4& element) -> std::ostream&; + +struct ExtendedServiceBroadcast { + unsigned _BitInt(8) security_information_; + unsigned _BitInt(2) sdstl_addressing_method_; + unsigned _BitInt(1) gck_supported_; + + std::optional section1_; + std::optional section2_; + std::optional section3_; + std::optional section4_; + + friend auto operator<<(std::ostream& stream, const ExtendedServiceBroadcast& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcast& element) -> std::ostream&; + +struct SystemInfo { + int32_t downlink_frequency_ = 0; + int32_t uplink_frequency_ = 0; + unsigned _BitInt(2) number_secondary_control_channels_main_carrier_; + unsigned _BitInt(3) ms_txpwr_max_cell_; + unsigned _BitInt(4) rxlev_access_min_; + unsigned _BitInt(4) access_parameter_; + unsigned _BitInt(4) radio_downlink_timeout_; + std::optional hyper_frame_number_; + std::optional common_cipher_key_identifier_or_static_cipher_key_version_number_; + std::optional even_multi_frame_definition_for_ts_mode_; + std::optional odd_multi_frame_definition_for_ts_mode_; + std::optional defaults_for_access_code_a_; + std::optional extended_service_broadcast_; + + unsigned _BitInt(14) location_area_; + unsigned _BitInt(16) subscriber_class_; + unsigned _BitInt(1) registration_; + unsigned _BitInt(1) deregistration_; + unsigned _BitInt(1) priority_cell_; + unsigned _BitInt(1) minimum_mode_service_; + unsigned _BitInt(1) migration_; + unsigned _BitInt(1) system_wide_service_; + unsigned _BitInt(1) tetra_voice_service_; + unsigned _BitInt(1) circuit_mode_data_service_; + unsigned _BitInt(1) sndcp_service_; + unsigned _BitInt(1) air_interface_encryption_service_; + unsigned _BitInt(1) advanced_link_supported_; + + friend auto operator<<(std::ostream& stream, const SystemInfo& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const SystemInfo& element) -> std::ostream&; + +struct AccessDefine { + unsigned _BitInt(1) common_or_assigned_control_channel_flag_; + unsigned _BitInt(2) access_code_; + AccessCodeDefinition access_code_definition_; + std::optional subscriber_class_bitmap_; + std::optional gssi_; + + AccessDefine() = delete; + explicit AccessDefine(BitVector& data) + : common_or_assigned_control_channel_flag_(data.take<1>()) + , access_code_(data.take<2>()) + , access_code_definition_(data) { + auto optional_field_flag = data.take<2>(); + if (optional_field_flag == 0b01) { + subscriber_class_bitmap_ = data.take<16>(); + } else if (optional_field_flag == 0b10) { + gssi_ = data.take<24>(); + } + auto filler_bits = data.take<3>(); + }; + + friend auto operator<<(std::ostream& stream, const AccessDefine& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const AccessDefine& element) -> std::ostream&; + +struct UpperMacBroadcastPacket { + /// the type of the logical channel on which this packet is sent + LogicalChannel logical_channel_; + /// the type of the mac packet + MacPacketType type_; + + std::optional sysinfo_; + std::optional access_define_; + + friend auto operator<<(std::ostream& stream, const UpperMacBroadcastPacket& packet) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const UpperMacBroadcastPacket& packet) -> std::ostream&; + +/// reconstruct the number of bits from the length indication field +struct LengthIndication { + // Y1 and Z1 octets, for a PDU sent in a subslot (i.e. MAC-ACCESS or MAC-END-HU). + // Y2 and Z2 octets, for a PDU sent in a slot (i.e. MAC-DATA, MAC-RESOURCE or MAC-END). + // These values are only valid for π/4-DQPSK. For values in other modulation schemes look at: + // "Table 21.98: Value of Y1, Z1, Y2 and Z2 in TMA-SAP MAC PDUs" + // The constants below are not in unit of octets, but in bits! + static const std::size_t Y1 = 8; + static const std::size_t Y2 = 8; + static const std::size_t Z1 = 8; + static const std::size_t Z2 = 8; + + static auto from_mac_access(unsigned _BitInt(5) length_indication) -> std::size_t { + if (length_indication < 0b01111) { + return length_indication * Y1; + } + return (14 * Y1 + (length_indication - 14) * Z1); + }; + + static auto from_mac_end_hu(unsigned _BitInt(4) length_indication) -> std::size_t { + return length_indication * Z1; + }; + + static auto from_mac_data(unsigned _BitInt(6) length_indication) -> std::size_t { + if (length_indication < 0b010011) { + return length_indication * Y2; + } + return (18 * Y2 + (length_indication - 18) * Z2); + }; + + static auto from_mac_end_uplink(unsigned _BitInt(6) length_indication) -> std::size_t { + if (length_indication < 0b000111) { + return length_indication * Y2; + } + return (6 * Y2 + (length_indication - 6) * Z2); + }; + + static auto from_mac_resource(unsigned _BitInt(6) length_indication) -> std::size_t { + if (length_indication < 0b010011) { + return length_indication * Y2; + } + return (18 * Y2 + (length_indication - 18) * Z2); + }; + + static auto from_mac_end_downlink(unsigned _BitInt(6) length_indication) -> std::size_t { + if (length_indication < 0b010011) { + return length_indication * Y2; + } + return (18 * Y2 + (length_indication - 18) * Z2); + }; +}; + +struct ExtendedCarrierNumbering { + unsigned _BitInt(4) frequency_band_; + unsigned _BitInt(2) offset_; + unsigned _BitInt(3) duplex_spacing_; + unsigned _BitInt(1) reverse_operation_; + + friend auto operator<<(std::ostream& stream, const ExtendedCarrierNumbering& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const ExtendedCarrierNumbering& element) -> std::ostream&; + +struct AugmentedChannelAllocation { + unsigned _BitInt(2) up_downlink_assigned_; + unsigned _BitInt(3) bandwidth_; + + unsigned _BitInt(3) modulation_mode_; + std::optional maximum_uplink_qam_modulation_level_; + + unsigned _BitInt(3) conforming_channel_status_; + unsigned _BitInt(4) bs_link_imbalance_; + unsigned _BitInt(5) bs_transmit_power_relative_to_main_carrier_; + + unsigned _BitInt(2) napping_status_; + std::optional napping_information_; + + std::optional conditional_element_a_; + std::optional conditional_element_b_; + unsigned _BitInt(1) further_augmentation_flag_; + + friend auto operator<<(std::ostream& stream, const AugmentedChannelAllocation& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const AugmentedChannelAllocation& element) -> std::ostream&; + +struct ChannelAllocationElement { + unsigned _BitInt(2) allocation_type_; + unsigned _BitInt(4) timeslot_assigned_; + unsigned _BitInt(2) up_downlink_assigned_; + unsigned _BitInt(1) clch_permission_; + unsigned _BitInt(1) cell_change_flag_; + + unsigned _BitInt(12) carrier_number_; + std::optional extended_carrier_numbering_; + + unsigned _BitInt(2) monitoring_pattern_; + std::optional frame18_monitoring_pattern_; + + std::optional augmented_channel_allocation_; + + ChannelAllocationElement() = delete; + explicit ChannelAllocationElement(BitVector& data) + : allocation_type_(data.take<2>()) + , timeslot_assigned_(data.take<4>()) + , up_downlink_assigned_(data.take<2>()) + , clch_permission_(data.take<1>()) + , cell_change_flag_(data.take<1>()) + , carrier_number_(data.take<12>()) { + + auto extended_carrier_numbering_flag = data.take<1>(); + if (extended_carrier_numbering_flag == 0b1) { + ExtendedCarrierNumbering extended_carrier_numbering; + extended_carrier_numbering.frequency_band_ = data.take<4>(); + extended_carrier_numbering.offset_ = data.take<2>(); + extended_carrier_numbering.duplex_spacing_ = data.take<3>(); + extended_carrier_numbering.reverse_operation_ = data.take<1>(); + + extended_carrier_numbering_ = extended_carrier_numbering; + } + + monitoring_pattern_ = data.take<2>(); + if (monitoring_pattern_ == 0b00) { + frame18_monitoring_pattern_ = data.take<2>(); + } + + if (up_downlink_assigned_ == 0b00) { + AugmentedChannelAllocation augmented_channel_allocation; + + augmented_channel_allocation.up_downlink_assigned_ = data.take<2>(); + augmented_channel_allocation.bandwidth_ = data.take<3>(); + augmented_channel_allocation.modulation_mode_ = data.take<3>(); + if (augmented_channel_allocation.modulation_mode_ == 0b010) { + augmented_channel_allocation.maximum_uplink_qam_modulation_level_ = data.take<3>(); + auto reserved = data.take<3>(); + } + augmented_channel_allocation.conforming_channel_status_ = data.take<3>(); + augmented_channel_allocation.bs_link_imbalance_ = data.take<4>(); + augmented_channel_allocation.bs_transmit_power_relative_to_main_carrier_ = data.take<5>(); + + augmented_channel_allocation.napping_status_ = data.take<2>(); + if (augmented_channel_allocation.napping_status_ == 0b01) { + augmented_channel_allocation.napping_information_ = data.take<11>(); + } + auto reserved = data.take<4>(); + auto conditional_element_a_flag = data.take<1>(); + if (conditional_element_a_flag == 0b1) { + augmented_channel_allocation.conditional_element_a_ = data.take<16>(); + } + auto conditional_element_b_flag = data.take<1>(); + if (conditional_element_b_flag) { + augmented_channel_allocation.conditional_element_b_ = data.take<16>(); + } + augmented_channel_allocation.further_augmentation_flag_ = data.take<1>(); + + augmented_channel_allocation_ = augmented_channel_allocation; + } + }; + + friend auto operator<<(std::ostream& stream, const ChannelAllocationElement& element) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const ChannelAllocationElement& element) -> std::ostream&; + +struct UpperMacCPlaneSignallingPacket { + /// the type of the logical channel on which this packet is sent + LogicalChannel logical_channel_; + /// the type of the mac packet + MacPacketType type_; + + bool encrypted_ = false; + + AddressType address_; + bool fragmentation_ = false; + + bool fragmentation_on_stealling_channel_ = false; + + std::optional reservation_requirement_; + + /// the tm_sdu that is passed to the LLC + std::optional tm_sdu_; + + // Uplink + std::optional encryption_mode_; + + // Downlink + std::optional immediate_napping_permission_flag_; + std::optional basic_slot_granting_element_; + std::optional position_of_grant_; + std::optional channel_allocation_element_; + std::optional random_access_flag_; + std::optional power_control_element_; + + friend auto operator<<(std::ostream& stream, const UpperMacCPlaneSignallingPacket& packet) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const UpperMacCPlaneSignallingPacket& packet) -> std::ostream&; + +struct UpperMacUPlaneSignallingPacket { + /// the type of the logical channel on which this packet is sent + LogicalChannel logical_channel_; + /// the type of the mac packet + MacPacketType type_; + /// the tm_sdu that is passed to the LLC + BitVector tm_sdu; + + /// all the other relevant fields... + + friend auto operator<<(std::ostream& stream, const UpperMacUPlaneSignallingPacket& packet) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const UpperMacUPlaneSignallingPacket& packet) -> std::ostream&; + +struct UpperMacUPlaneTrafficPacket { + /// the type of the logical channel on which this packet is sent + LogicalChannel logical_channel_; + /// the traffic in the packet + BitVector data; + + /// all the other relevant fields... + + friend auto operator<<(std::ostream& stream, const UpperMacUPlaneTrafficPacket& packet) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const UpperMacUPlaneTrafficPacket& packet) -> std::ostream&; \ No newline at end of file diff --git a/include/l2/upper_mac_packet_builder.hpp b/include/l2/upper_mac_packet_builder.hpp new file mode 100644 index 0000000..b4d6abe --- /dev/null +++ b/include/l2/upper_mac_packet_builder.hpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l2/slot.hpp" +#include "l2/upper_mac_packet.hpp" +#include "utils/bit_vector.hpp" +#include +#include +#include + +struct UpperMacPackets { + std::vector c_plane_signalling_packets_; + std::vector u_plane_signalling_packet_; + std::optional u_plane_traffic_packet_; + std::optional broadcast_packet_; + + auto merge(UpperMacPackets&& other) -> void { + std::move(other.c_plane_signalling_packets_.begin(), other.c_plane_signalling_packets_.end(), + std::back_inserter(c_plane_signalling_packets_)); + std::move(other.u_plane_signalling_packet_.begin(), other.u_plane_signalling_packet_.end(), + std::back_inserter(u_plane_signalling_packet_)); + + if (u_plane_traffic_packet_ && other.u_plane_traffic_packet_) { + throw std::runtime_error("Trying to merge two packets both with a UpperMacUPlaneTrafficPacket"); + } + + if (other.u_plane_traffic_packet_) { + u_plane_traffic_packet_ = std::move(*other.u_plane_traffic_packet_); + } + + if (broadcast_packet_ && other.broadcast_packet_) { + throw std::runtime_error("Trying to merge two packets both with a UpperMacBroadcastPacket"); + } + + if (other.broadcast_packet_) { + broadcast_packet_ = std::move(*other.broadcast_packet_); + } + } + + friend auto operator<<(std::ostream& stream, const UpperMacPackets& packets) -> std::ostream&; +}; + +auto operator<<(std::ostream& stream, const UpperMacPackets& packets) -> std::ostream&; + +class UpperMacPacketBuilder { + private: + [[nodiscard]] static auto parseLogicalChannel(BurstType burst_type, + const LogicalChannelDataAndCrc& logical_channel_data_and_crc) + -> UpperMacPackets; + + [[nodiscard]] static auto parseBroadcast(LogicalChannel channel, BitVector&& data) -> UpperMacBroadcastPacket; + [[nodiscard]] static auto parseCPlaneSignallingPacket(BurstType burst_type, LogicalChannel channel, BitVector& data) + -> UpperMacCPlaneSignallingPacket; + [[nodiscard]] static auto parseCPlaneSignalling(BurstType burst_type, LogicalChannel channel, BitVector&& data) + -> std::vector; + [[nodiscard]] static auto parseUPlaneSignalling(LogicalChannel channel, BitVector&& data) + -> UpperMacUPlaneSignallingPacket; + [[nodiscard]] static auto parseUPlaneTraffic(LogicalChannel channel, BitVector&& data) + -> UpperMacUPlaneTrafficPacket; + + public: + UpperMacPacketBuilder() = default; + + // TODO: make this function take a const Slots reference + static auto parseSlots(Slots& slots) -> UpperMacPackets; +}; \ No newline at end of file diff --git a/include/l3/mobile_link_entity.hpp b/include/l3/mobile_link_entity.hpp index fe446cd..c45b9d2 100644 --- a/include/l3/mobile_link_entity.hpp +++ b/include/l3/mobile_link_entity.hpp @@ -28,30 +28,10 @@ class MobileLinkEntity { , is_downlink_(is_downlink){}; ~MobileLinkEntity() noexcept = default; - void service_DMle_system_info(BitVector& vec); - void service_user_pdu(AddressType address, BitVector& vec); - friend auto operator<<(std::ostream& stream, const MobileLinkEntity& mle) -> std::ostream&; - private: void service_data_pdu(AddressType address, BitVector& vec); - void service_d_network_broadcast(const AddressType address, BitVector& vec); - - bool system_info_received_ = false; - uint16_t location_area_ = 0; - uint16_t subscriber_class_ = 0; - uint8_t registration_ = 0; - uint8_t deregistration_ = 0; - uint8_t priority_cell_ = 0; - uint8_t minimum_mode_service_ = 0; - uint8_t migration_ = 0; - uint8_t system_wide_service_ = 0; - uint8_t tetra_voice_service_ = 0; - uint8_t circuit_mode_data_service_ = 0; - uint8_t sndcp_service_ = 0; - uint8_t air_interface_encryption_service_ = 0; - uint8_t advanced_link_supported_ = 0; std::shared_ptr reporter_{}; std::unique_ptr cmce_{}; @@ -59,6 +39,4 @@ class MobileLinkEntity { // Wheather this MLE is for decoding downlink or uplink data const bool is_downlink_; -}; - -auto operator<<(std::ostream& stream, const MobileLinkEntity& mle) -> std::ostream&; \ No newline at end of file +}; \ No newline at end of file diff --git a/include/utils/bit_vector.hpp b/include/utils/bit_vector.hpp index cfbe23c..d8a6b1b 100644 --- a/include/utils/bit_vector.hpp +++ b/include/utils/bit_vector.hpp @@ -72,7 +72,7 @@ class BitVector { ")"); } - const auto bits = data_.begin() + bits_left() - N; + const auto bits = data_.begin() + read_offset_ + bits_left() - N; // delete last n entries len_ -= N; @@ -87,7 +87,7 @@ class BitVector { std::to_string(bits_left()) + ")"); } - const auto bits = data_.begin() + read_offset_ + N + offset; + const auto bits = data_.begin() + read_offset_ + offset; return to_bit_int(bits); } @@ -102,6 +102,16 @@ class BitVector { [[nodiscard]] auto is_mac_padding() const noexcept -> bool; + /// function to remove fill bits of a bitvector once and only once + auto remove_fill_bits() -> void { + if (!fill_bits_removed_) { + while (take_last<1>() == 0b0) { + } + + fill_bits_removed_ = true; + } + }; + friend auto operator<<(std::ostream& stream, const BitVector& vec) -> std::ostream&; private: @@ -120,6 +130,8 @@ class BitVector { return ret; } + + bool fill_bits_removed_ = false; }; auto operator<<(std::ostream& stream, const BitVector& vec) -> std::ostream&; \ No newline at end of file diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 49c3602..e5eb1d4 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -3,12 +3,16 @@ #include "l2/broadcast_synchronization_channel.hpp" #include "l2/logical_channel.hpp" #include "l2/lower_mac_coding.hpp" +#include "l2/slot.hpp" +#include "l2/upper_mac_packet_builder.hpp" #include "utils/bit_vector.hpp" #include #include #include +#include #include #include +#include #include #include @@ -326,29 +330,16 @@ auto LowerMac::process(const std::vector& frame, BurstType burst_type) // check if we have crc decode errors in the lower mac decode_error |= slots.has_crc_error(); - // construct the callbacks for the slots - std::map logical_channel_to_function_map({ - {LogicalChannel::kSignalingChannelHalfDownlink, &UpperMac::process_SCH_HD}, - {LogicalChannel::kSignalingChannelHalfUplink, &UpperMac::process_SCH_HU}, - {LogicalChannel::kSignalingChannelFull, &UpperMac::process_SCH_F}, - {LogicalChannel::kStealingChannel, &UpperMac::process_STCH}, - }); - - { - auto& first_slot = slots.get_first_slot().get_logical_channel_data_and_crc(); - if (logical_channel_to_function_map.count(first_slot.channel) && first_slot.crc_ok) { - auto& function = logical_channel_to_function_map[first_slot.channel]; - callbacks.emplace_back(std::bind(function, upper_mac_, burst_type, first_slot.data)); + callbacks.emplace_back([slots_ref = slots] { + auto slots = Slots(slots_ref); + std::cout << slots; + try { + auto packets = UpperMacPacketBuilder::parseSlots(slots); + std::cout << packets << std::endl; + } catch (std::runtime_error& e) { + std::cout << "Error decoding packets: " << e.what() << std::endl; } - } - - if (slots.has_second_slot()) { - auto& second_slot = slots.get_second_slot().get_logical_channel_data_and_crc(); - if (logical_channel_to_function_map.count(second_slot.channel) && second_slot.crc_ok) { - auto& function = logical_channel_to_function_map[second_slot.channel]; - callbacks.emplace_back(std::bind(function, upper_mac_, burst_type, second_slot.data)); - } - } + }); } // Update the received burst type metrics diff --git a/src/l2/slot.cpp b/src/l2/slot.cpp new file mode 100644 index 0000000..1d957aa --- /dev/null +++ b/src/l2/slot.cpp @@ -0,0 +1,29 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l2/slot.hpp" + +auto operator<<(std::ostream& stream, const Slot& slot) -> std::ostream& { + for (const auto& data : slot.data_) { + stream << " Channel: " << to_string(data.channel) << std::endl; + stream << " Data: " << data.data << std::endl; + stream << " Crc ok: " << (data.crc_ok ? "true" : "false") << std::endl; + } + return stream; +} + +auto operator<<(std::ostream& stream, const Slots& slots) -> std::ostream& { + stream << "Slots:" << std::endl; + stream << " [BurstType] " << to_string(slots.burst_type_) << std::endl; + stream << " [SlotsType] " << to_string(slots.slot_type_) << std::endl; + for (const auto& slot : slots.slots_) { + stream << " Slot:" << std::endl; + stream << slot; + } + return stream; +} \ No newline at end of file diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp deleted file mode 100644 index 7f3365b..0000000 --- a/src/l2/upper_mac.cpp +++ /dev/null @@ -1,916 +0,0 @@ -#include -#include - -#include -#include - -void UpperMac::process_SCH_HD(const BurstType burst_type, BitVector& vec) { - assert(is_downlink_burst(burst_type)); - - std::cout << "[Channel] SCH_HD" << std::endl; - process_signalling_channel(burst_type, vec, true, false); -} - -void UpperMac::process_SCH_HU(const BurstType burst_type, BitVector& vec) { - assert(vec.bits_left() == 92); - assert(is_uplink_burst(burst_type)); - - try { - fragmentation_start_burst(); - last_address_type_ = AddressType(); - - std::cout << "[Channel] SCH_HU" << std::endl; - - auto pduType = vec.take<1>(); - auto fill_bit_indication = vec.take<1>(); - - remove_fill_bits_ = true; - if (fill_bit_indication == 0b1) { - remove_fill_bits(vec); - } - - if (pduType == 0b0) { - process_mac_access(vec); - } else if (pduType == 0b1) { - process_mac_end_hu(vec); - } - // TODO: one mac may contain multiple mac headers... - // compare with process_signalling_channel - - fragmentation_end_burst(); - } catch (std::exception& e) { - std::cout << "Error with decoding: " << e.what() << std::endl; - } -} - -void UpperMac::process_SCH_F(const BurstType burst_type, BitVector& vec) { - std::cout << "[Channel] SCH_F" << std::endl; - process_signalling_channel(burst_type, vec, false, false); -} - -void UpperMac::process_STCH(const BurstType burst_type, BitVector& vec) { - std::cout << "[Channel] STCH" << std::endl; - - second_slot_stolen_ = false; - - process_signalling_channel(burst_type, vec, true, true); -} - -void UpperMac::process_signalling_channel(const BurstType burst_type, BitVector& vec, bool isHalfChannel, - bool isStolenChannel) { - remove_fill_bits_ = true; - try { - fragmentation_start_burst(); - process_signalling_channel_internal(burst_type, vec, isHalfChannel, isStolenChannel); - fragmentation_end_burst(); - } catch (std::exception& e) { - std::cout << "Error with decoding: " << e.what() << std::endl; - } -} - -void UpperMac::process_signalling_channel_internal(const BurstType burst_type, BitVector& vec, bool isHalfChannel, - bool isStolenChannel) { - auto pduType = vec.take<2>(); - - if (pduType == 0b00) { - // MAC-RESOURCE (downlink) or MAC-DATA (uplink) - // TMA-SAP - if (is_downlink_burst(burst_type)) { - process_mac_resource(vec); - } else { - process_mac_data(vec); - } - } else if (pduType == 0b01) { - // MAC-END or MAC-FRAG - // TMA-SAP - auto subtype = vec.take<1>(); - if (subtype == 0b0) { - if (is_downlink_burst(burst_type)) { - process_mac_frag_downlink(vec); - } else { - process_mac_frag_uplink(vec); - } - } else if (subtype == 0b1) { - if (is_downlink_burst(burst_type)) { - process_mac_end_downlink(vec); - } else { - process_mac_end_uplink(vec); - } - } - } else if (pduType == 0b10) { - // Broadcast - // TMB-SAP - // ✅ done - if (is_uplink_burst(burst_type)) { - 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) - // MAC-U-SIGNAL (only on STCH) - // TMA-SAP or TMD-SAP - if (isHalfChannel && isStolenChannel) { - process_mac_usignal(vec); - } else if ((!isHalfChannel) && (!isStolenChannel)) { - process_supplementary_mac_pdu(burst_type, vec); - } else { - // Reserved - // TODO: print unimplemented error - } - } - - // TODO: one mac may contain multiple mac headers. proccess all the others - // layers first and then continue with the next - if (vec.bits_left() > 0) { - std::cout << "BitsLeft(): " << std::to_string(vec.bits_left()) << " " << vec << std::endl; - if (!vec.is_mac_padding()) { - process_signalling_channel_internal(burst_type, vec, isHalfChannel, isStolenChannel); - } - } -} - -void UpperMac::process_broadcast(BitVector& vec) { - auto broadcastType = vec.take<2>(); - switch (broadcastType) { - case 0b00: - // SYSINFO PDU - process_system_info_pdu(vec); - break; - case 0b01: - // ACCESS-DEFINE PDU - process_access_define_pdu(vec); - break; - case 0b10: - // SYSINFO-DA - // only QAM - process_system_info_da(vec); - break; - case 0b11: - // Reserved - // TODO: print unimplemented error - break; - } -} - -void UpperMac::process_supplementary_mac_pdu(const BurstType burst_type, BitVector& vec) { - auto subtype = vec.take<1>(); - - switch (subtype) { - case 0b0: - if (is_downlink_burst(burst_type)) { - process_mac_d_blck(vec); - } else { - process_mac_u_blck(vec); - } - break; - case 0b1: - // Reserved - // TODO: print unimplemented error - break; - } -} - -void UpperMac::process_system_info_pdu(BitVector& vec) { - auto mainCarrier = vec.take<12>(); - auto frequency_band = vec.take<4>(); - auto offset = vec.take<2>(); - auto duplexSpacing = vec.take<3>(); - auto reverseOperation = vec.take<1>(); - number_secondary_control_channels_main_carrier_ = vec.take<2>(); - ms_txpwr_max_cell_ = vec.take<3>(); - rxlev_access_min_ = vec.take<4>(); - access_parameter_ = vec.take<4>(); - radio_downlink_timeout_ = vec.take<4>(); - hyper_frame_cipher_key_flag_ = vec.take<1>(); - if (hyper_frame_cipher_key_flag_ == 0) { - hyper_frame_number_ = vec.take<16>(); - } else { - common_cipher_key_identifier_or_static_cipher_key_version_number_ = vec.take<16>(); - } - auto optionalFieldFlag = vec.take<2>(); - if (optionalFieldFlag == 0b00) { - *even_multi_frame_definition_for_ts_mode_ = vec.take<20>(); - } else if (optionalFieldFlag == 0b01) { - *odd_multi_frame_definition_for_ts_mode_ = vec.take<20>(); - } else if (optionalFieldFlag == 0b10) { - defaults_for_access_code_a_.immediate_ = vec.take<4>(); - defaults_for_access_code_a_.waiting_time_ = vec.take<4>(); - defaults_for_access_code_a_.number_of_random_access_transmissions_on_up_link_ = vec.take<4>(); - defaults_for_access_code_a_.frame_length_factor_ = vec.take<1>(); - defaults_for_access_code_a_.timeslot_pointer_ = vec.take<4>(); - defaults_for_access_code_a_.minimum_pdu_priority_ = vec.take<3>(); - defaults_for_access_code_a_.system_info_ = true; - } else if (optionalFieldFlag == 0b11) { - extended_service_broadcast_.security_information_ = vec.take<8>(); - extended_service_broadcast_.sdstl_addressing_method_ = vec.take<2>(); - extended_service_broadcast_.gck_supported_ = vec.take<1>(); - auto section = vec.take<2>(); - if (section == 0b00) { - extended_service_broadcast_.data_priority_supported_ = vec.take<1>(); - extended_service_broadcast_.extended_advanced_links_and_max_ublck_supported_ = vec.take<1>(); - extended_service_broadcast_.qos_negotiation_supported_ = vec.take<1>(); - extended_service_broadcast_.d8psk_service_ = vec.take<1>(); - auto _sectionInformation = vec.take<3>(); - extended_service_broadcast_.system_info_section_1_ = true; - } else if (section == 0b01) { - extended_service_broadcast_.service_25Qam_ = vec.take<1>(); - extended_service_broadcast_.service_50Qam_ = vec.take<1>(); - extended_service_broadcast_.service_100Qam_ = vec.take<1>(); - extended_service_broadcast_.service_150Qam_ = vec.take<1>(); - auto _reserved = vec.take<3>(); - extended_service_broadcast_.system_info_section_2_ = true; - } else { - // TODO: Section 2 and 3 are reserved - auto _reserved = vec.take<7>(); - } - extended_service_broadcast_.system_info_ = true; - } - - // downlink main carrier frequency = base frequency + (main carrier × 25 kHz) - // + offset kHz. - const int32_t duplex[4] = {0, 6250, -6250, 12500}; - downlink_frequency_ = frequency_band * 100000000 + mainCarrier * 25000 + duplex[offset]; - - static const int32_t tetra_duplex_spacing[8][16] = { - /* values are in kHz */ - [0] = {-1, 1600, 10000, 10000, 10000, 10000, 10000, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - [1] = {-1, 4500, -1, 36000, 7000, -1, -1, -1, 45000, 45000, -1, -1, -1, -1, -1, -1}, - [2] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - [3] = {-1, -1, -1, 8000, 8000, -1, -1, -1, 18000, 18000, -1, -1, -1, -1, -1, -1}, - [4] = {-1, -1, -1, 18000, 5000, -1, 30000, 30000, -1, 39000, -1, -1, -1, -1, -1, -1}, - [5] = {-1, -1, -1, -1, 9500, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - [6] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - [7] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, - }; - uint32_t duplex_spacing = tetra_duplex_spacing[duplexSpacing][frequency_band]; - /* reserved for future standardization */ - if (duplex_spacing < 0) { - uplink_frequency_ = 0; - } else if (reverseOperation) { - uplink_frequency_ = static_cast(downlink_frequency_) + duplex_spacing * 1000; - } else { - uplink_frequency_ = static_cast(downlink_frequency_) - duplex_spacing * 1000; - } - - // This element shall be present when the PDU is sent using π/8-D8PSK - // modulation. This element shall not be present when the PDU is sent using - // π/4-DQPSK modulation. - // auto reserved = vec.take<28>(); - - mobile_link_entity_->service_DMle_system_info(vec); - - system_info_received_ = true; -} - -void UpperMac::process_access_define_pdu(BitVector& vec) { - auto _ = vec.take<23>(); - auto optional_field_flag = vec.take<2>(); - if (optional_field_flag == 0b01) { - auto _subscriber_class_bitmap = vec.take<16>(); - } else if (optional_field_flag == 0b10) { - auto _gssi = vec.take<24>(); - } - auto _filter_bits = vec.take<3>(); -} - -void UpperMac::process_mac_usignal(BitVector& vec) { - second_slot_stolen_ = (vec.take<1>() == 1); - - // TODO: TM-SDU - auto bits_left = vec.bits_left(); - auto tm_sdu = vec.take_vector(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; -} - -void UpperMac::process_mac_d_blck(BitVector& vec) { - auto fill_bit_indication = vec.take<1>(); - auto encrypted = vec.take<2>(); - auto event_label = vec.take<10>(); - auto address = AddressType(); - address.set_event_label(event_label); - auto immediate_napping_permission_flag = vec.take<1>(); - auto slot_granting_flag = vec.take<1>(); - auto basic_slot_granting_element = 0; - if (slot_granting_flag == 0b1) { - basic_slot_granting_element = vec.take<8>(); - } - - if (fill_bit_indication == 0b1) { - remove_fill_bits(vec); - } - - auto bits_left = vec.bits_left(); - auto tm_sdu = vec.take_vector(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; - - logical_link_control_->process(address, tm_sdu); -} - -void UpperMac::process_mac_u_blck(BitVector& vec) { - auto fill_bit_indication = vec.take<1>(); - auto encrypted = vec.take<1>(); - auto event_label = vec.take<10>(); - auto address = AddressType(); - address.set_event_label(event_label); - auto reservation_requirement = vec.take<4>(); - - if (fill_bit_indication == 0b1) { - remove_fill_bits(vec); - } - - auto bits_left = vec.bits_left(); - auto tm_sdu = vec.take_vector(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; - std::cout << " reservation_requirement: 0b" << std::bitset<4>(reservation_requirement) << std::endl; - std::cout << " Address: " << address << std::endl; - - logical_link_control_->process(address, tm_sdu); -} - -void UpperMac::process_mac_frag_downlink(BitVector& vec) { - auto fill_bit_indication = vec.take<1>(); - - if (fill_bit_indication == 0b1) { - remove_fill_bits(vec); - } - - auto bits_left = vec.bits_left(); - auto tm_sdu = vec.take_vector(bits_left); - std::cout << "MAC FRAG" << std::endl; - std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; - - fragmentation_push_tm_sdu_frag(tm_sdu); -} - -void UpperMac::process_mac_frag_uplink(BitVector& vec) { - auto fill_bit_indication = vec.take<1>(); - - if (fill_bit_indication == 0b1) { - remove_fill_bits(vec); - } - - auto bits_left = vec.bits_left(); - auto tm_sdu = vec.take_vector(bits_left); - std::cout << "MAC FRAG" << std::endl; - std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; - - fragmentation_push_tm_sdu_frag(tm_sdu); -} - -void UpperMac::process_mac_end_downlink(BitVector& vec) { - second_slot_stolen_ = false; - - auto pre_processing_bit_count = vec.bits_left() + 3; - - auto fill_bit_indication = vec.take<1>(); - auto position_of_grant = vec.take<1>(); - auto length_indictaion = vec.take<6>(); - // The immediate napping permission flag shall be present when the PDU is sent - // using π/8-D8PSK or QAM modulation. It shall not be present when the PDU is - // sent using π/4-DQPSK modulation. auto immediateNapping = vec.take<1>(); - auto slot_granting_flag = vec.take<1>(); - // The multiple slot granting flag shall be present when the slot granting - // flag is set to 1 and the PDU is sent using QAM modulation. It shall not be - // present when the slot granting flag is set to 0 or the PDU is sent using - // π/4-DQPSK or π/8-D8PSK modulation auto multipleSlotGranting = vec.take<1>(); - // The basic slot granting element shall be present when the slot granting - // flag is set to 1 and either the PDU is sent using π/4-DQPSK or π/8-D8PSK - // modulation, or the PDU is sent using QAM modulation and the multiple slot - // granting flag is set to 0. - auto basic_slot_granting_element = 0; - if (slot_granting_flag == 0b1) { - basic_slot_granting_element = vec.take<8>(); - } - auto channel_allocation_flag = vec.take<1>(); - if (channel_allocation_flag == 0b1) { - auto channel_allocation1 = vec.take<22>(); - auto extended_carrier_numbering_flag = vec.take<1>(); - if (extended_carrier_numbering_flag == 0b1) { - auto channel_allocation2 = vec.take<9>(); - } - auto monitoring_pattern = vec.take<2>(); - if (monitoring_pattern == 0b00) { - auto frame18_monitoring_pattern = vec.take<2>(); - auto up_downlink_assigned_for_augmented_channel_allocation = vec.take<2>(); - auto bandwidth_of_allocated_channel = vec.take<3>(); - auto modulation_mode_of_allocated_channel = vec.take<3>(); - if (modulation_mode_of_allocated_channel == 0b010) { - auto maximum_uplink_qam_modulation_level = vec.take<3>(); - auto reserved = vec.take<3>(); - } - auto channel_allocation3 = vec.take<12>(); - auto napping_status = vec.take<2>(); - if (napping_status == 0b01) { - auto napping_information = vec.take<11>(); - } - auto reserved = vec.take<4>(); - - // taking optional element a - auto conditional_element_a_flag = vec.take<1>(); - if (conditional_element_a_flag == 0b1) { - auto conditional_element_a = vec.take<16>(); - } - - // taking optional element b - auto conditional_element_b_flag = vec.take<1>(); - if (conditional_element_b_flag == 0b1) { - auto conditional_element_b = vec.take<16>(); - } - auto further_augmentation_flag = vec.take<1>(); - } - } - - auto mac_header_length = pre_processing_bit_count - vec.bits_left(); - auto bits_left = length_indictaion * 8 - mac_header_length; - - if (fill_bit_indication == 0b1) { - remove_fill_bits(vec); - } - - auto tm_sdu = vec.take_vector(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; - std::cout << " mac_header_length = " << std::to_string(mac_header_length) << std::endl; - - fragmentation_push_tm_sdu_end(tm_sdu); -} - -void UpperMac::process_mac_end_uplink(BitVector& vec) { - auto pre_processing_bit_count = vec.bits_left() + 3; - - auto fill_bit_indication = vec.take<1>(); - auto length_indictaion = vec.take<6>(); - - auto mac_header_length = pre_processing_bit_count - vec.bits_left(); - auto bits_left = length_indictaion * 8 - mac_header_length; - - if (fill_bit_indication == 0b1) { - remove_fill_bits(vec); - } - - auto tm_sdu = vec.take_vector(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; - std::cout << " mac_header_length = " << std::to_string(mac_header_length) << std::endl; - - fragmentation_push_tm_sdu_end(tm_sdu); -} - -void UpperMac::process_mac_resource(BitVector& vec) { - std::cout << "MAC RESOURCE" << std::endl; - - auto preprocessing_bit_count = vec.bits_left() + 2; - - auto fill_bit_indication = vec.take<1>(); - auto position_of_grant = vec.take<1>(); - auto encryption_mode = vec.take<2>(); - auto random_access_flag = vec.take<1>(); - auto length_indictaion = vec.take<6>(); - 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(); - std::cout << " address_type: 0b" << std::bitset<3>(address_type) << std::endl; - if (address_type == 0b000) { - remove_fill_bits(vec); - - // std::cout << " NULL PDU" << std::endl; - // std::cout << " fill_bit_indication: 0b" << - // std::bitset<1>(fill_bit_indication) - // << std::endl; - // std::cout << " encryption_mode: 0b" << std::bitset<2>(encryption_mode) - // << std::endl; - // std::cout << " length_indictaion: 0b" << std::bitset<5>(length_indictaion) - // << std::endl; - return; - } else if (address_type == 0b001) { - address.set_ssi(vec.take<24>()); - } else if (address_type == 0b010) { - address.set_event_label(vec.take<10>()); - } else if (address_type == 0b011) { - address.set_ussi(vec.take<24>()); - } else if (address_type == 0b100) { - address.set_smi(vec.take<24>()); - } else if (address_type == 0b101) { - address.set_ssi(vec.take<24>()); - address.set_event_label(vec.take<10>()); - } else if (address_type == 0b110) { - address.set_ssi(vec.take<24>()); - address.set_usage_marker(vec.take<6>()); - } else if (address_type == 0b111) { - address.set_smi(vec.take<24>()); - address.set_event_label(vec.take<10>()); - } - // The immediate napping permission flag shall be present when the PDU is sent - // using π/8-D8PSK or QAM modulation. It shall not be present when the PDU is - // sent using π/4-DQPSK modulation. auto immediateNappingPermisionFlag = - // vec.take<1>(); - auto power_control_flag = vec.take<1>(); - if (power_control_flag == 0b1) { - auto power_control_element = vec.take<4>(); - } - auto slot_granting_flag = vec.take<1>(); - // The multiple slot granting flag shall be present when the slot granting - // flag is set to 1 and the PDU is sent using QAM modulation. It shall not be - // present when the slot granting flag is set to 0 or the PDU is sent using - // π/4-DQPSK or π/8-D8PSK modulation. auto multipleSlotGrantingFlag = - // vec.take<1>(); The basic slot granting element shall be present when the slot - // granting flag is set to 1 and either the PDU is sent using π/4-DQPSK or - // π/8-D8PSK modulation, or the PDU is sent using QAM modulation and the - // multiple slot granting flag is set to 0. - if (slot_granting_flag == 0b1) { - auto basic_slot_granting_element = vec.take<8>(); - } - auto channel_allocation_flag = vec.take<1>(); - std::cout << " channel_allocation_flag = 0b" << std::bitset<1>(channel_allocation_flag) << std::endl; - if (channel_allocation_flag == 0b1) { - auto allocation_type = vec.take<2>(); - auto time_slot_assigned = vec.take<4>(); - auto up_downlink_assigned = vec.take<2>(); - auto clch_permission = vec.take<1>(); - auto cell_change_flag = vec.take<1>(); - auto carrier_number = vec.take<12>(); - auto extended_carrier_numbering_flag = vec.take<1>(); - if (extended_carrier_numbering_flag == 0b1) { - auto channel_allocation_2 = vec.take<10>(); - } - auto monitoring_pattern = vec.take<2>(); - if (monitoring_pattern == 0b00) { - auto frame18_monitoring_pattern = vec.take<2>(); - } - if (up_downlink_assigned == 0b00) { - auto up_downlink_assigned_for_augmented_channel_allocation = vec.take<2>(); - auto bandwidth_of_allocated_channel = vec.take<3>(); - auto modulation_mode_of_allocated_channel = vec.take<3>(); - if (modulation_mode_of_allocated_channel == 0b010) { - auto maximumUplinkQamModulationLevel = vec.take<3>(); - } else { - auto reserved = vec.take<3>(); - } - auto channel_allocation3 = vec.take<12>(); - auto napping_status = vec.take<2>(); - if (napping_status == 0b01) { - auto napping_information = vec.take<11>(); - } - auto reserved = vec.take<4>(); - auto conditional_element_a_flag = vec.take<1>(); - if (conditional_element_a_flag == 0b1) { - auto conditional_element_a = vec.take<16>(); - } - auto conditional_element_b_flag = vec.take<1>(); - if (conditional_element_b_flag == 0b1) { - auto conditional_element_b = vec.take<16>(); - } - auto further_augmentation_flag = vec.take<1>(); - } - } - - auto mac_header_length = preprocessing_bit_count - vec.bits_left(); - auto bits_left = length_indictaion * 8 - mac_header_length; - - // std::cout << "MAC RESOURCE" << std::endl; - // std::cout << " encryption_mode: 0b" << std::bitset<2>(encryption_mode) - // << std::endl; - // std::cout << " fill_bit_indication: 0b" << std::bitset<1>(fill_bit_indication) - // << std::endl; - // std::cout << " length_indictaion: 0b" << std::bitset<5>(length_indictaion) - // << std::endl; - // std::cout << " channel_allocation_flag: 0b" - // << std::bitset<1>(channel_allocation_flag) << std::endl; - - // if (fill_bit_indication == 0b1 && (bits_left != 133) && (address_type != - // 0b001)) { if (fill_bit_indication == 0b1) { - // remove_fill_bits(vec); - // } - - // XXX: check this - if (length_indictaion == 0b111111 || length_indictaion == 0b111110) { - bits_left = vec.bits_left(); - } - - auto tm_sdu = vec.take_vector(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; - std::cout << " Address: " << address << std::endl; - - if (length_indictaion != 0b111111) { - // no fragmentation - logical_link_control_->process(address, tm_sdu); - } else { - fragmentation_push_tm_sdu_start(address, tm_sdu); - } - // fragmentation_push_tm_sdu_start(address, tm_sdu); -} - -void UpperMac::process_mac_data(BitVector& vec) { - auto preprocessing_bit_count = vec.bits_left() + 2; - - auto fill_bit_indication = vec.take<1>(); - auto encrypted_flag = vec.take<1>(); - auto address_type = vec.take<2>(); - - std::cout << "MAC DATA" << std::endl; - std::cout << " encrypted: 0b" << std::bitset<1>(encrypted_flag) << std::endl; - - auto address = AddressType(); - if (address_type == 0b00) { - address.set_ssi(vec.take<24>()); - } else if (address_type == 0b01) { - address.set_event_label(vec.take<10>()); - } else if (address_type == 0b11) { - address.set_ussi(vec.take<24>()); - } else if (address_type == 0b11) { - address.set_smi(vec.take<24>()); - } - - auto length_indication_or_capacity_request = vec.take<1>(); - auto fragmentation = false; - uint64_t length_indication{}; - if (length_indication_or_capacity_request == 0b0) { - length_indication = vec.take<6>(); - std::cout << " length indication: 0b" << std::bitset<6>(length_indication) << std::endl; - if (length_indication == 0b111110) { - 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 { - auto fragmentation_flag = vec.take<1>(); - if (fragmentation_flag == 0b1) { - fragmentation = true; - } - auto reservation_requirement = vec.take<4>(); - auto reserved = vec.take<1>(); - - if (reservation_requirement == 0b0000) { - last_address_type_end_hu_ = address; - } - - std::cout << " fragmentation flag: 0b" << std::bitset<1>(fragmentation_flag) << std::endl; - std::cout << " reservation requirement: 0b" << std::bitset<4>(reservation_requirement) << std::endl; - std::cout << " reserved: 0b" << std::bitset<1>(reserved) << std::endl; - } - - uint64_t bits_left = vec.bits_left(); - - auto null_pdu = false; - if (length_indication_or_capacity_request == 0b0) { - auto mac_header_length = preprocessing_bit_count - vec.bits_left(); - bits_left = length_indication * 8 - mac_header_length; - if (length_indication == 0) { - bits_left = 0; - null_pdu = true; - } - } - - auto tm_sdu = vec.take_vector(bits_left); - std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; - std::cout << " Address: " << address << std::endl; - - if (fragmentation) { - fragmentation_push_tm_sdu_start(address, tm_sdu); - } else if (null_pdu) { - // only write address to last_address_type_ - last_address_type_ = address; - } else { - logical_link_control_->process(address, tm_sdu); - } -} - -void UpperMac::process_mac_access(BitVector& vec) { - auto preprocessing_bit_count = vec.bits_left() + 2; - - std::cout << "MAC-ACCESS" << std::endl; - auto encrypted_flag = vec.take<1>(); - auto address_type = vec.take<2>(); - - std::cout << " encrypted: 0b" << std::bitset<1>(encrypted_flag) << std::endl; - - auto address = AddressType(); - if (address_type == 0b00) { - address.set_ssi(vec.take<24>()); - } else if (address_type == 0b01) { - address.set_event_label(vec.take<10>()); - } else if (address_type == 0b11) { - address.set_ussi(vec.take<24>()); - } else if (address_type == 0b11) { - address.set_smi(vec.take<24>()); - } - - auto optional_field_flag = vec.take<1>(); - uint64_t length_indication{}; - uint64_t length_indication_or_capacity_request{}; - auto fragmentation = false; - - if (optional_field_flag == 0b1) { - length_indication_or_capacity_request = vec.take<1>(); - if (length_indication_or_capacity_request == 0b0) { - length_indication = vec.take<5>(); - std::cout << " length indication: 0b" << std::bitset<5>(length_indication) << std::endl; - } else { - auto fragmentation_flag = vec.take<1>(); - if (fragmentation_flag == 0b1) { - fragmentation = true; - } - auto reservation_requirement = vec.take<4>(); - - if (reservation_requirement == 0b0000) { - last_address_type_end_hu_ = address; - } - - std::cout << " fragmentation flag: 0b" << std::bitset<1>(fragmentation_flag) << std::endl; - std::cout << " reservation requirement: 0b" << std::bitset<4>(reservation_requirement) << std::endl; - } - } - - uint64_t bits_left = vec.bits_left(); - - if (optional_field_flag == 0b1 && length_indication_or_capacity_request == 0b0) { - auto mac_header_length = preprocessing_bit_count - vec.bits_left(); - bits_left = length_indication * 8 - mac_header_length; - } - - auto tm_sdu = vec.take_vector(bits_left); - std::cout << " TM-SDU: size = " << std::to_string(tm_sdu.bits_left()) << ": " << tm_sdu << std::endl; - std::cout << " Address: " << address << std::endl; - - if (!fragmentation) { - logical_link_control_->process(address, tm_sdu); - } else { - fragmentation_push_tm_sdu_start(address, tm_sdu); - } -} - -void UpperMac::process_mac_end_hu(BitVector& vec) { - auto preprocessing_bit_count = vec.bits_left() + 2; - std::cout << "MAC-END-HU" << std::endl; - - auto length_indictaion_or_capacity_request = vec.take<1>(); - uint64_t length_indictaion; - if (length_indictaion_or_capacity_request == 0b0) { - // TODO: parse this - length_indictaion = vec.take<4>(); - std::cout << " length indication: 0b" << std::bitset<4>(length_indictaion) << std::endl; - } else { - auto reservation_requirement = vec.take<4>(); - std::cout << " reservation requirement: 0b" << std::bitset<4>(reservation_requirement) << std::endl; - } - - uint64_t bits_left = vec.bits_left(); - - if (length_indictaion_or_capacity_request == 0b0) { - auto mac_header_length = preprocessing_bit_count - vec.bits_left(); - bits_left = length_indictaion * 8 - mac_header_length; - } - - auto tm_sdu = vec.take_vector(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! 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 - fragmentation_push_tm_sdu_end_hu(tm_sdu); -} - -void UpperMac::remove_fill_bits(BitVector& vec) { - if (remove_fill_bits_) { - while (vec.take_last<1>() == 0b0) - ; - } - remove_fill_bits_ = false; -} - -auto operator<<(std::ostream& stream, const UpperMac& upperMac) -> std::ostream& { - if (upperMac.system_info_received_) { - stream << "SYSINFO:" << std::endl; - stream << " DL " << std::to_string(upperMac.downlink_frequency_) << "Hz UL " - << std::to_string(upperMac.uplink_frequency_) << "Hz" << std::endl; - stream << " Number of common secondary control channels in use on CA main " - "carrier: "; - switch (upperMac.number_secondary_control_channels_main_carrier_) { - case 0b00: - stream << "None"; - break; - case 0b01: - stream << "Timeslot 2 of main carrier"; - break; - case 0b10: - stream << "Timeslots 2 and 3 of main carrier"; - break; - case 0b11: - stream << "Timeslots 2, 3 and 4 of main carrier"; - break; - } - stream << std::endl; - stream << " MS_TXPWR_MAX_CELL: "; - switch (upperMac.ms_txpwr_max_cell_) { - case 0b000: - stream << "Reserved"; - break; - default: - stream << std::to_string(10 + 5 * upperMac.ms_txpwr_max_cell_) << " dBm"; - break; - } - stream << std::endl; - stream << " RXLEV_ACCESS_MIN: " << std::to_string(-125 + 5 * upperMac.rxlev_access_min_) << " dBm" - << std::endl; - stream << " ACCESS_PARAMETER: " << std::to_string(-53 + 2 * upperMac.access_parameter_) << " dBm" << std::endl; - stream << " RADIO_DOWNLINK_TIMEOUT: "; - switch (upperMac.radio_downlink_timeout_) { - case 0b0000: - stream << "Disable radio downlink counter"; - break; - default: - stream << std::to_string(144 * upperMac.radio_downlink_timeout_) << " timeslots"; - break; - } - stream << std::endl; - if (upperMac.hyper_frame_cipher_key_flag_) { - stream << " Common cipher key identifier: " << std::to_string(upperMac.hyper_frame_cipher_key_flag_) - << std::endl; - } else { - stream << " Cyclic count of hyperframes: " << std::to_string(upperMac.hyper_frame_number_) << std::endl; - } - - if (upperMac.even_multi_frame_definition_for_ts_mode_.has_value()) { - stream << " Bit map of common frames for TS mode (even multiframes): 0b" - << std::bitset<20>(*upperMac.even_multi_frame_definition_for_ts_mode_) << std::endl; - } - if (upperMac.odd_multi_frame_definition_for_ts_mode_.has_value()) { - stream << " Bit map of common frames for TS mode (odd multiframes): 0b" - << std::bitset<20>(*upperMac.odd_multi_frame_definition_for_ts_mode_) << std::endl; - } - - if (upperMac.defaults_for_access_code_a_.system_info_) { - stream << " Default definition for access code A information element:" << std::endl; - stream << " Immediate: 0b" << std::bitset<4>(upperMac.extended_service_broadcast_.security_information_) - << std::endl; - stream << " Waiting time: 0b" << std::bitset<4>(upperMac.defaults_for_access_code_a_.waiting_time_) - << std::endl; - stream << " Number of random access transmissions on uplink: 0b" - << std::bitset<4>( - upperMac.defaults_for_access_code_a_.number_of_random_access_transmissions_on_up_link_) - << std::endl; - stream << " Frame-length factor: 0b" - << std::bitset<1>(upperMac.defaults_for_access_code_a_.frame_length_factor_) << std::endl; - stream << " Timeslot pointer: 0b" - << std::bitset<4>(upperMac.defaults_for_access_code_a_.timeslot_pointer_) << std::endl; - stream << " Minimum PDU priority: 0b" - << std::bitset<3>(upperMac.defaults_for_access_code_a_.minimum_pdu_priority_) << std::endl; - } - - if (upperMac.defaults_for_access_code_a_.system_info_) { - stream << " Extended services broadcast:" << std::endl; - stream << " Security information: 0b" - << std::bitset<8>(upperMac.extended_service_broadcast_.security_information_) << std::endl; - stream << " SDS-TL addressing method: 0b" - << std::bitset<2>(upperMac.extended_service_broadcast_.sdstl_addressing_method_) << std::endl; - stream << " GCK supported: 0b" << std::bitset<1>(upperMac.extended_service_broadcast_.gck_supported_) - << std::endl; - if (upperMac.extended_service_broadcast_.system_info_section_1_) { - stream << " Data priority supported: 0b" - << std::bitset<1>(upperMac.extended_service_broadcast_.data_priority_supported_) << std::endl; - stream << " Extended advanced links and MAC-U-BLCK supported: 0b" - << std::bitset<1>( - upperMac.extended_service_broadcast_.extended_advanced_links_and_max_ublck_supported_) - << std::endl; - stream << " QoS negotiation supported: 0b" - << std::bitset<1>(upperMac.extended_service_broadcast_.qos_negotiation_supported_) << std::endl; - stream << " D8PSK service: 0b" << std::bitset<1>(upperMac.extended_service_broadcast_.d8psk_service_) - << std::endl; - } - - if (upperMac.extended_service_broadcast_.system_info_section_2_) { - stream << " 25 kHz QAM service: 0b" - << std::bitset<1>(upperMac.extended_service_broadcast_.service_25Qam_) << std::endl; - stream << " 50 kHz QAM service: 0b" - << std::bitset<1>(upperMac.extended_service_broadcast_.service_50Qam_) << std::endl; - stream << " 100 kHz QAM service: 0b" - << std::bitset<1>(upperMac.extended_service_broadcast_.service_100Qam_) << std::endl; - stream << " 150 kHz QAM service: 0b" - << std::bitset<1>(upperMac.extended_service_broadcast_.service_150Qam_) << std::endl; - } - } - } - - return stream; -} diff --git a/src/l2/upper_mac_packet.cpp b/src/l2/upper_mac_packet.cpp new file mode 100644 index 0000000..e5fe180 --- /dev/null +++ b/src/l2/upper_mac_packet.cpp @@ -0,0 +1,345 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l2/upper_mac_packet.hpp" +#include +#include + +auto operator<<(std::ostream& stream, const AccessCodeDefinition& element) -> std::ostream& { + stream << " Default definition for access code A information element:" << std::endl; + stream << " Immediate: 0b" << std::bitset<4>(element.immediate_) << std::endl; + stream << " Waiting time: 0b" << std::bitset<4>(element.waiting_time_) << std::endl; + stream << " Number of random access transmissions on uplink: 0b" + << std::bitset<4>(element.number_of_random_access_transmissions_on_up_link_) << std::endl; + stream << " Frame-length factor: 0b" << std::bitset<1>(element.frame_length_factor_) << std::endl; + stream << " Timeslot pointer: 0b" << std::bitset<4>(element.timeslot_pointer_) << std::endl; + stream << " Minimum PDU priority: 0b" << std::bitset<3>(element.minimum_pdu_priority_) << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection1& element) -> std::ostream& { + stream << " Section1:" << std::endl; + stream << " Data priority supported: 0b" << std::bitset<1>(element.data_priority_supported_) << std::endl; + stream << " Extended advanced links and MAC-U-BLCK supported: 0b" + << std::bitset<1>(element.extended_advanced_links_and_max_ublck_supported_) << std::endl; + stream << " QoS negotiation supported: 0b" << std::bitset<1>(element.qos_negotiation_supported_) + << std::endl; + stream << " D8PSK service: 0b" << std::bitset<1>(element.d8psk_service_) << std::endl; + stream << " section 2 sent: 0b" << std::bitset<1>(element.section2_sent_) << std::endl; + stream << " section 3 sent: 0b" << std::bitset<1>(element.section3_sent_) << std::endl; + stream << " section 4 sent: 0b" << std::bitset<1>(element.section4_sent_) << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection2& element) -> std::ostream& { + stream << " Section2:" << std::endl; + stream << " 25 kHz QAM service: 0b" << std::bitset<1>(element.service_25Qam_) << std::endl; + stream << " 50 kHz QAM service: 0b" << std::bitset<1>(element.service_50Qam_) << std::endl; + stream << " 100 kHz QAM service: 0b" << std::bitset<1>(element.service_100Qam_) << std::endl; + stream << " 150 kHz QAM service: 0b" << std::bitset<1>(element.service_150Qam_) << std::endl; + stream << " reserved: " << std::bitset<3>(element.reserved_) << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection3& element) -> std::ostream& { + stream << " Section3:" << std::endl; + stream << " reserved: " << std::bitset<7>(element.reserved_) << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcastSection4& element) -> std::ostream& { + stream << " Section4:" << std::endl; + stream << " reserved: " << std::bitset<7>(element.reserved_) << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const ExtendedServiceBroadcast& element) -> std::ostream& { + stream << " Extended services broadcast:" << std::endl; + stream << " Security information: 0b" << std::bitset<8>(element.security_information_) << std::endl; + stream << " SDS-TL addressing method: 0b" << std::bitset<2>(element.sdstl_addressing_method_) << std::endl; + stream << " GCK supported: 0b" << std::bitset<1>(element.gck_supported_) << std::endl; + if (element.section1_) { + stream << *element.section1_; + } + if (element.section2_) { + stream << *element.section2_; + } + if (element.section3_) { + stream << *element.section3_; + } + if (element.section4_) { + stream << *element.section4_; + } + return stream; +} + +auto operator<<(std::ostream& stream, const SystemInfo& element) -> std::ostream& { + stream << " [SYSINFO]" << std::endl; + stream << " DL " << std::to_string(element.downlink_frequency_) << "Hz UL " + << std::to_string(element.uplink_frequency_) << "Hz" << std::endl; + stream << " Number of common secondary control channels in use on CA main " + "carrier: "; + switch (element.number_secondary_control_channels_main_carrier_) { + case 0b00: + stream << "None"; + break; + case 0b01: + stream << "Timeslot 2 of main carrier"; + break; + case 0b10: + stream << "Timeslots 2 and 3 of main carrier"; + break; + case 0b11: + stream << "Timeslots 2, 3 and 4 of main carrier"; + break; + } + stream << std::endl; + stream << " MS_TXPWR_MAX_CELL: "; + switch (element.ms_txpwr_max_cell_) { + case 0b000: + stream << "Reserved"; + break; + default: + stream << std::to_string(10 + 5 * element.ms_txpwr_max_cell_) << " dBm"; + break; + } + stream << std::endl; + stream << " RXLEV_ACCESS_MIN: " << std::to_string(-125 + 5 * element.rxlev_access_min_) << " dBm" << std::endl; + stream << " ACCESS_PARAMETER: " << std::to_string(-53 + 2 * element.access_parameter_) << " dBm" << std::endl; + stream << " RADIO_DOWNLINK_TIMEOUT: "; + switch (element.radio_downlink_timeout_) { + case 0b0000: + stream << "Disable radio downlink counter"; + break; + default: + stream << std::to_string(144 * element.radio_downlink_timeout_) << " timeslots"; + break; + } + stream << std::endl; + if (element.hyper_frame_number_) { + stream << " Cyclic count of hyperframes: " << std::bitset<16>(*element.hyper_frame_number_) << std::endl; + } + if (element.common_cipher_key_identifier_or_static_cipher_key_version_number_) { + stream << " Common cipher key identifier: " + << std::bitset<16>(*element.common_cipher_key_identifier_or_static_cipher_key_version_number_) + << std::endl; + } + if (element.even_multi_frame_definition_for_ts_mode_) { + stream << " Bit map of common frames for TS mode (even multiframes): 0b" + << std::bitset<20>(*element.even_multi_frame_definition_for_ts_mode_) << std::endl; + } + if (element.odd_multi_frame_definition_for_ts_mode_) { + stream << " Bit map of common frames for TS mode (odd multiframes): 0b" + << std::bitset<20>(*element.odd_multi_frame_definition_for_ts_mode_) << std::endl; + } + if (element.defaults_for_access_code_a_) { + stream << *element.defaults_for_access_code_a_; + } + if (element.extended_service_broadcast_) { + stream << *element.extended_service_broadcast_; + } + + stream << " Location Area (LA): " << static_cast(element.location_area_) << std::endl; + stream << " Subscriber Class 1..16 allowed: 0b" << std::bitset<16>(element.subscriber_class_) << std::endl; + stream << " " + << (element.registration_ ? "Registration mandatory on this cell" : "Registration not required on this cell") + << std::endl; + stream << " " + << (element.deregistration_ ? "De-registration requested on this cell" + : "De-registration not required on this cell") + << std::endl; + stream << " " << (element.priority_cell_ ? "Cell is a priority cell" : "Cell is not a priority cell") + << std::endl; + stream << " " << (element.minimum_mode_service_ ? "Cell never uses minimum mode" : "Cell may use minimum mode") + << std::endl; + stream << " " + << (element.migration_ ? "Migration is supported by this cell" : "Migration is not supported by this cell") + << std::endl; + stream << " " + << (element.system_wide_service_ ? "Normal mode (system wide services supported)" + : "System wide services temporarily not supported") + << std::endl; + stream << " " + << (element.tetra_voice_service_ ? "TETRA voice service is supported on this cell" + : "TETRA voice service is not supported on this cell") + << std::endl; + stream << " " + << (element.circuit_mode_data_service_ ? "Circuit mode data service is supported on this cell" + : "Circuit mode data service is not supported on this cell") + << std::endl; + stream << " " + << (element.sndcp_service_ ? "SNDCP service is available on this cell" + : "SNDCP service is not available on this cell") + << std::endl; + stream << " " + << (element.air_interface_encryption_service_ ? "Air interface encryption is available on this cell" + : "Air interface encryption is not available on this cell") + << std::endl; + stream << " " + << (element.advanced_link_supported_ ? "Advanced link is supported on this cell" + : "Advanced link is not supported on this cell") + << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const AccessDefine& element) -> std::ostream& { + stream << " [SYSINFO]" << std::endl; + stream << " " + << (element.common_or_assigned_control_channel_flag_ ? "ACCESS-DEFINE applies to common channel" + : "ACCESS-DEFINE applies to assigned channel") + << std::endl; + stream << " Access code "; + switch (element.access_code_) { + case 0b00: + stream << "A"; + break; + case 0b01: + stream << "B"; + break; + case 0b10: + stream << "C"; + break; + case 0b11: + stream << "D"; + break; + } + stream << std::endl; + stream << element.access_code_definition_; + if (element.subscriber_class_bitmap_) { + stream << " Subscriber class bit map: " << std::bitset<16>(*element.subscriber_class_bitmap_) << std::endl; + } + if (element.gssi_) { + stream << " GSSI: " << std::bitset<24>(*element.gssi_) << std::endl; + } + return stream; +} + +auto operator<<(std::ostream& stream, const UpperMacBroadcastPacket& packet) -> std::ostream& { + stream << " [Broadcast]" << std::endl; + stream << " [LogicalChannel] " << to_string(packet.logical_channel_) << std::endl; + stream << " [PacketType] " << to_string(packet.type_) << std::endl; + if (packet.sysinfo_) { + stream << *packet.sysinfo_; + } + if (packet.access_define_) { + stream << *packet.access_define_; + } + return stream; +} + +auto operator<<(std::ostream& stream, const ExtendedCarrierNumbering& element) -> std::ostream& { + stream << " frequency band: " << std::bitset<4>(element.frequency_band_) << std::endl; + stream << " offset: " << std::bitset<2>(element.offset_) << std::endl; + stream << " duplex spacing: " << std::bitset<3>(element.duplex_spacing_) << std::endl; + stream << " reverse operation: " << std::bitset<1>(element.reverse_operation_); + return stream; +} + +auto operator<<(std::ostream& stream, const AugmentedChannelAllocation& element) -> std::ostream& { + stream << " up/downlink assigned: " << std::bitset<2>(element.up_downlink_assigned_) << std::endl; + stream << " bandwidth: " << std::bitset<3>(element.bandwidth_) << std::endl; + stream << " modulation mode: " << std::bitset<3>(element.modulation_mode_) << std::endl; + if (element.maximum_uplink_qam_modulation_level_) { + stream << " maximum qam modulation level: " << std::bitset<3>(*element.maximum_uplink_qam_modulation_level_) + << std::endl; + } + stream << " conforming channel status: " << std::bitset<3>(element.conforming_channel_status_) << std::endl; + stream << " bs link imbalance: " << std::bitset<4>(element.bs_link_imbalance_) << std::endl; + stream << " bs power relative to main carrier: " + << std::bitset<5>(element.bs_transmit_power_relative_to_main_carrier_) << std::endl; + stream << " napping status: " << std::bitset<2>(element.napping_status_) << std::endl; + if (element.napping_information_) { + stream << " napping information: " << std::bitset<11>(*element.napping_information_) << std::endl; + } + if (element.conditional_element_a_) { + stream << " conditional element a: " << std::bitset<16>(*element.conditional_element_a_) << std::endl; + } + if (element.conditional_element_b_) { + stream << " conditional element b: " << std::bitset<16>(*element.conditional_element_b_) << std::endl; + } + stream << " further augmentation flag: " << std::bitset<1>(element.further_augmentation_flag_); + return stream; +} + +auto operator<<(std::ostream& stream, const ChannelAllocationElement& element) -> std::ostream& { + stream << " allocation type: " << std::bitset<2>(element.allocation_type_) << std::endl; + stream << " timeslot assigned: " << std::bitset<4>(element.timeslot_assigned_) << std::endl; + stream << " up/downlink assigned: " << std::bitset<2>(element.up_downlink_assigned_) << std::endl; + stream << " clch permissions: " << std::bitset<1>(element.clch_permission_) << std::endl; + stream << " cell change flag: " << std::bitset<1>(element.cell_change_flag_) << std::endl; + stream << " carrier number: " << std::bitset<12>(element.carrier_number_) << std::endl; + if (element.extended_carrier_numbering_) { + stream << *element.extended_carrier_numbering_ << std::endl; + } + stream << " monitoring pattern: " << std::bitset<2>(element.monitoring_pattern_) << std::endl; + if (element.frame18_monitoring_pattern_) { + stream << " frame18 monitoring pattern: " << std::bitset<2>(*element.frame18_monitoring_pattern_) + << std::endl; + } + if (element.augmented_channel_allocation_) { + stream << *element.augmented_channel_allocation_ << std::endl; + } + + return stream; +} + +auto operator<<(std::ostream& stream, const UpperMacCPlaneSignallingPacket& packet) -> std::ostream& { + stream << " [CPlaneSignalling]" << std::endl; + stream << " [LogicalChannel] " << to_string(packet.logical_channel_) << std::endl; + stream << " [PacketType] " << to_string(packet.type_) << std::endl; + stream << " encrypted: " << (packet.encrypted_ ? "true" : "false") << std::endl; + if (packet.encryption_mode_) { + stream << " encryption mode: " << std::bitset<2>(*packet.encryption_mode_) << std::endl; + } + stream << " address: " << packet.address_ << std::endl; + stream << " fragmentation: " << (packet.fragmentation_ ? "true" : "false") << std::endl; + stream << " fragmentation on stealing channel: " + << (packet.fragmentation_on_stealling_channel_ ? "true" : "false") << std::endl; + if (packet.reservation_requirement_) { + stream << " reservation requirement: " << std::bitset<4>(*packet.reservation_requirement_) << std::endl; + } + if (packet.immediate_napping_permission_flag_) { + stream << " immediate napping permission flag: " + << (*packet.immediate_napping_permission_flag_ ? "true" : "false") << std::endl; + } + if (packet.basic_slot_granting_element_) { + stream << " basic slot granting element: " << std::bitset<8>(*packet.basic_slot_granting_element_) + << std::endl; + } + if (packet.position_of_grant_) { + stream << " position of grant: " << std::bitset<1>(*packet.position_of_grant_) << std::endl; + } + if (packet.channel_allocation_element_) { + stream << *packet.channel_allocation_element_; + } + if (packet.random_access_flag_) { + stream << " random access flag: " << std::bitset<1>(*packet.random_access_flag_) << std::endl; + } + if (packet.power_control_element_) { + stream << " power control element: " << std::bitset<4>(*packet.power_control_element_) << std::endl; + } + if (packet.tm_sdu_) { + stream << " " << *packet.tm_sdu_ << std::endl; + } + return stream; +} + +auto operator<<(std::ostream& stream, const UpperMacUPlaneSignallingPacket& packet) -> std::ostream& { + stream << " [UPlaneSignalling]" << std::endl; + stream << " [LogicalChannel] " << to_string(packet.logical_channel_) << std::endl; + stream << " [PacketType] " << to_string(packet.type_) << std::endl; + stream << " " << packet.tm_sdu << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const UpperMacUPlaneTrafficPacket& packet) -> std::ostream& { + stream << " [UPlaneTraffic]" << std::endl; + stream << " [LogicalChannel] " << to_string(packet.logical_channel_) << std::endl; + stream << " " << packet.data << std::endl; + return stream; +} \ No newline at end of file diff --git a/src/l2/upper_mac_packet_builder.cpp b/src/l2/upper_mac_packet_builder.cpp new file mode 100644 index 0000000..e834118 --- /dev/null +++ b/src/l2/upper_mac_packet_builder.cpp @@ -0,0 +1,815 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l2/upper_mac_packet_builder.hpp" +#include "burst_type.hpp" +#include "l2/logical_channel.hpp" +#include "l2/upper_mac_packet.hpp" +#include "utils/bit_vector.hpp" +#include +#include +#include + +auto operator<<(std::ostream& stream, const UpperMacPackets& packets) -> std::ostream& { + stream << "[UpperMacPacket]" << std::endl; + for (const auto& packet : packets.c_plane_signalling_packets_) { + stream << packet << std::endl; + } + for (const auto& packet : packets.u_plane_signalling_packet_) { + stream << packet << std::endl; + } + if (packets.u_plane_traffic_packet_) { + stream << *packets.u_plane_traffic_packet_ << std::endl; + } + if (packets.broadcast_packet_) { + stream << *packets.broadcast_packet_ << std::endl; + } + + return stream; +} + +auto UpperMacPacketBuilder::parseSlots(Slots& slots) -> UpperMacPackets { + UpperMacPackets packets; + + { + const auto& first_slot = slots.get_first_slot().get_logical_channel_data_and_crc(); + packets.merge(parseLogicalChannel(slots.get_burst_type(), first_slot)); + } + + if (slots.has_second_slot()) { + const auto& second_slot = slots.get_second_slot().get_logical_channel_data_and_crc(); + packets.merge(parseLogicalChannel(slots.get_burst_type(), second_slot)); + } + + return packets; +} + +auto UpperMacPacketBuilder::parseLogicalChannel(const BurstType burst_type, + const LogicalChannelDataAndCrc& logical_channel_data_and_crc) + -> UpperMacPackets { + const auto& channel = logical_channel_data_and_crc.channel; + auto data = BitVector(logical_channel_data_and_crc.data); + if (channel == LogicalChannel::kTrafficChannel) { + return UpperMacPackets{.u_plane_traffic_packet_ = parseUPlaneTraffic(channel, std::move(data))}; + } + + // filter out signalling packets with a wrong crc + if (!logical_channel_data_and_crc.crc_ok) { + return UpperMacPackets{}; + } + + auto pdu_type = data.look<2>(0); + + // See "Table 21.38: MAC PDU types for SCH/F, SCH/HD, STCH, SCH-P8/F, SCH-P8/HD, SCH-Q/D, SCH-Q/B and SCH-Q/U" on + // how the this check works to filter out the u-plane signalling + if (channel == LogicalChannel::kStealingChannel) { + if (pdu_type == 0b11) { + // process MAC-U-SIGNAL + return UpperMacPackets{.u_plane_signalling_packet_ = {parseUPlaneSignalling(channel, std::move(data))}}; + } + return UpperMacPackets{.c_plane_signalling_packets_ = + parseCPlaneSignalling(burst_type, channel, std::move(data))}; + // throw std::runtime_error("Only MAC-U-SIGNAL may be sent on the stealing channel."); + } + + if (pdu_type == 0b10) { + // Broadcast + // TMB-SAP + if (is_downlink_burst(burst_type)) { + return UpperMacPackets{.broadcast_packet_ = parseBroadcast(channel, std::move(data))}; + } + throw std::runtime_error("Broadcast may only be sent on downlink."); + } + + return UpperMacPackets{.c_plane_signalling_packets_ = parseCPlaneSignalling(burst_type, channel, std::move(data))}; +} + +auto UpperMacPacketBuilder::parseBroadcast(LogicalChannel channel, BitVector&& data) -> UpperMacBroadcastPacket { + UpperMacBroadcastPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacBroadcast}; + + auto pdu_type = data.take<2>(); + auto broadcast_type = data.take<2>(); + + if (broadcast_type == 0b00) { + // SYSINFO PDU + SystemInfo sysinfo; + + auto main_carrier = data.take<12>(); + auto frequency_band = data.take<4>(); + auto offset = data.take<2>(); + auto duplex_spacing_field = data.take<3>(); + auto reverse_operation = data.take<1>(); + sysinfo.number_secondary_control_channels_main_carrier_ = data.take<2>(); + sysinfo.ms_txpwr_max_cell_ = data.take<3>(); + sysinfo.rxlev_access_min_ = data.take<4>(); + sysinfo.access_parameter_ = data.take<4>(); + sysinfo.radio_downlink_timeout_ = data.take<4>(); + auto hyper_frame_cipher_key_flag = data.take<1>(); + if (hyper_frame_cipher_key_flag == 0) { + sysinfo.hyper_frame_number_ = data.take<16>(); + } else { + sysinfo.common_cipher_key_identifier_or_static_cipher_key_version_number_ = data.take<16>(); + } + auto optional_field_flag = data.take<2>(); + if (optional_field_flag == 0b00) { + sysinfo.even_multi_frame_definition_for_ts_mode_ = data.take<20>(); + } else if (optional_field_flag == 0b01) { + sysinfo.odd_multi_frame_definition_for_ts_mode_ = data.take<20>(); + } else if (optional_field_flag == 0b10) { + sysinfo.defaults_for_access_code_a_ = AccessCodeDefinition(data); + } else if (optional_field_flag == 0b11) { + ExtendedServiceBroadcast extended_service_broadcast; + extended_service_broadcast.security_information_ = data.take<8>(); + extended_service_broadcast.sdstl_addressing_method_ = data.take<2>(); + extended_service_broadcast.gck_supported_ = data.take<1>(); + auto section = data.take<2>(); + if (section == 0b00) { + ExtendedServiceBroadcastSection1 section; + section.data_priority_supported_ = data.take<1>(); + section.extended_advanced_links_and_max_ublck_supported_ = data.take<1>(); + section.qos_negotiation_supported_ = data.take<1>(); + section.d8psk_service_ = data.take<1>(); + section.section2_sent_ = data.take<1>(); + section.section3_sent_ = data.take<1>(); + section.section4_sent_ = data.take<1>(); + extended_service_broadcast.section1_ = section; + } else if (section == 0b01) { + ExtendedServiceBroadcastSection2 section; + section.service_25Qam_ = data.take<1>(); + section.service_50Qam_ = data.take<1>(); + section.service_100Qam_ = data.take<1>(); + section.service_150Qam_ = data.take<1>(); + section.reserved_ = data.take<3>(); + extended_service_broadcast.section2_ = section; + } else if (section == 0b10) { + ExtendedServiceBroadcastSection3 section; + section.reserved_ = data.take<3>(); + extended_service_broadcast.section3_ = section; + } else { + ExtendedServiceBroadcastSection4 section; + section.reserved_ = data.take<3>(); + extended_service_broadcast.section4_ = section; + } + sysinfo.extended_service_broadcast_ = extended_service_broadcast; + } + + // downlink main carrier frequency = base frequency + (main carrier × 25 kHz) + // + offset kHz. + const int32_t duplex[4] = {0, 6250, -6250, 12500}; + sysinfo.downlink_frequency_ = frequency_band * 100000000 + main_carrier * 25000 + duplex[offset]; + + static const int32_t tetra_duplex_spacing[8][16] = { + /* values are in kHz */ + [0] = {-1, 1600, 10000, 10000, 10000, 10000, 10000, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + [1] = {-1, 4500, -1, 36000, 7000, -1, -1, -1, 45000, 45000, -1, -1, -1, -1, -1, -1}, + [2] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + [3] = {-1, -1, -1, 8000, 8000, -1, -1, -1, 18000, 18000, -1, -1, -1, -1, -1, -1}, + [4] = {-1, -1, -1, 18000, 5000, -1, 30000, 30000, -1, 39000, -1, -1, -1, -1, -1, -1}, + [5] = {-1, -1, -1, -1, 9500, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + [6] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + [7] = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}, + }; + const uint32_t duplex_spacing = tetra_duplex_spacing[duplex_spacing_field][frequency_band]; + /* reserved for future standardization */ + if (duplex_spacing < 0) { + sysinfo.uplink_frequency_ = 0; + } else if (reverse_operation) { + sysinfo.uplink_frequency_ = static_cast(sysinfo.downlink_frequency_) + duplex_spacing * 1000; + } else { + sysinfo.uplink_frequency_ = static_cast(sysinfo.downlink_frequency_) - duplex_spacing * 1000; + } + + // This element shall be present when the PDU is sent using π/8-D8PSK + // modulation. This element shall not be present when the PDU is sent using + // π/4-DQPSK modulation. + // auto reserved = vec.take<28>(); + + // Location area (14) + sysinfo.location_area_ = data.take<14>(); + // Subscriber class (16) + sysinfo.subscriber_class_ = data.take<16>(); + // BS service details (12) + sysinfo.registration_ = data.take<1>(); + sysinfo.deregistration_ = data.take<1>(); + sysinfo.priority_cell_ = data.take<1>(); + sysinfo.minimum_mode_service_ = data.take<1>(); + sysinfo.migration_ = data.take<1>(); + sysinfo.system_wide_service_ = data.take<1>(); + sysinfo.tetra_voice_service_ = data.take<1>(); + sysinfo.circuit_mode_data_service_ = data.take<1>(); + auto reserved = data.take<1>(); + sysinfo.sndcp_service_ = data.take<1>(); + sysinfo.air_interface_encryption_service_ = data.take<1>(); + sysinfo.advanced_link_supported_ = data.take<1>(); + + packet.sysinfo_ = sysinfo; + } else if (broadcast_type == 0b01) { + // ACCESS-DEFINE PDU + packet.access_define_ = AccessDefine(data); + } else if (broadcast_type == 0b10) { + throw std::runtime_error("SYSINFO-DA is not implemented."); + } else { + throw std::runtime_error("Reserved broadcast type"); + } + + if (data.bits_left() != 0) { + std::cout << packet; + std::cout << data.bits_left() << " bits left over." << std::endl; + std::cout << data << std::endl; + throw std::runtime_error("Reseved broadcast PDU but there are bits left after parsing."); + } + + return packet; +} + +auto UpperMacPacketBuilder::parseCPlaneSignallingPacket(BurstType burst_type, LogicalChannel channel, BitVector& data) + -> UpperMacCPlaneSignallingPacket { + if (channel == LogicalChannel::kSignalingChannelHalfUplink) { + if (is_downlink_burst(burst_type)) { + throw std::runtime_error("SignalingChannelHalfUplink may only be set on uplink."); + } + + auto preprocessing_bit_count = data.bits_left(); + + auto pdu_type = data.take<1>(); + auto fill_bit_indication = data.take<1>(); + + if (pdu_type == 0b0) { + // process MAC-ACCESS + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacAccess}; + + packet.encrypted_ = (data.take<1>() == 1U); + auto address_type = data.take<2>(); + + auto& address = packet.address_; + if (address_type == 0b00) { + address.set_ssi(data.take<24>()); + } else if (address_type == 0b01) { + address.set_event_label(data.take<10>()); + } else if (address_type == 0b11) { + address.set_ussi(data.take<24>()); + } else if (address_type == 0b11) { + address.set_smi(data.take<24>()); + } + + auto optional_field_flag = data.take<1>(); + std::optional length_indication; + if (optional_field_flag == 0b1) { + auto length_indication_or_capacity_request = data.take<1>(); + if (length_indication_or_capacity_request == 0b0) { + length_indication = data.take<5>(); + } else { + packet.fragmentation_ = (data.take<1>() == 1U); + packet.reservation_requirement_ = data.take<4>(); + } + } + + auto bits_left = data.bits_left(); + const auto mac_header_length = preprocessing_bit_count - bits_left; + + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + } + + if (length_indication.has_value()) { + if (length_indication == 0b00000) { + bits_left = 0; + } else { + bits_left = LengthIndication::from_mac_access(*length_indication) - mac_header_length; + if (fill_bit_indication == 0b1) { + // The fill bit indication shall indicate if there are any fill bits, which shall be added + // whenever the + // size of the TM-SDU is less than the available capacity of the MAC block or less than the size + // of the TM-SDU indicated by the length indication field. The TM-SDU length is equal to the MAC + // PDU length minus the MAC PDU header length. + if (bits_left > data.bits_left()) { + // cap the number of bits left to the maximum available. this should only happen if the + // tm_sdu size + mac header size is not alligned to octect boundary + if (bits_left - data.bits_left() >= 8) { + throw std::runtime_error( + "Fill bits were indicated and the length indication shows a size that does not fit " + "in the MAC, but the length indication is more than 7 bits apart."); + } + bits_left = data.bits_left(); + } + } + } + } else { + bits_left = data.bits_left(); + } + + if (bits_left != 0) { + packet.tm_sdu_ = data.take_vector(bits_left); + } + + return packet; + } + + { + // process MAC-END-HU + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacEndHu}; + + auto length_indictaion_or_capacity_request = data.take<1>(); + std::optional length_indication; + if (length_indictaion_or_capacity_request == 0b0) { + length_indication = data.take<4>(); + } else { + packet.reservation_requirement_ = data.take<4>(); + } + + auto bits_left = data.bits_left(); + const auto mac_header_length = preprocessing_bit_count - bits_left; + + if (length_indication.has_value()) { + bits_left = LengthIndication::from_mac_end_hu(*length_indication) - mac_header_length; + if (fill_bit_indication == 0b1) { + // The fill bit indication shall indicate if there are any fill bits, which shall be added + // whenever the + // size of the TM-SDU is less than the available capacity of the MAC block or less than the size + // of the TM-SDU indicated by the length indication field. The TM-SDU length is equal to the MAC + // PDU length minus the MAC PDU header length. + if (bits_left > data.bits_left()) { + // cap the number of bits left to the maximum available. this should only happen if the + // tm_sdu size + mac header size is not alligned to octect boundary + if (bits_left - data.bits_left() >= 8) { + throw std::runtime_error( + "Fill bits were indicated and the length indication shows a size that does not fit " + "in the MAC, but the length indication is more than 7 bits apart."); + } + bits_left = data.bits_left(); + } + } + } else { + bits_left = data.bits_left(); + } + + packet.tm_sdu_ = data.take_vector(bits_left); + + return packet; + } + } + + if (is_uplink_burst(burst_type)) { + // process the SCH/F and STCH of the uplink + auto preprocessing_bit_count = data.bits_left(); + + auto pdu_type = data.take<2>(); + + if (pdu_type == 0b00) { + // MAC-DATA (uplink) + // TMA-SAP + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacData}; + + auto fill_bit_indication = data.take<1>(); + + packet.encrypted_ = (data.take<1>() == 1U); + auto address_type = data.take<2>(); + + auto& address = packet.address_; + if (address_type == 0b00) { + address.set_ssi(data.take<24>()); + } else if (address_type == 0b01) { + address.set_event_label(data.take<10>()); + } else if (address_type == 0b11) { + address.set_ussi(data.take<24>()); + } else if (address_type == 0b11) { + address.set_smi(data.take<24>()); + } + + auto length_indication_or_capacity_request = data.take<1>(); + std::optional length_indication; + if (length_indication_or_capacity_request == 0b0) { + length_indication = data.take<6>(); + if (length_indication == 0b111111) { + packet.fragmentation_on_stealling_channel_ = true; + } + } else { + packet.fragmentation_ = (data.take<1>() == 1U); + packet.reservation_requirement_ = data.take<4>(); + auto reserved = data.take<1>(); + } + + auto bits_left = data.bits_left(); + const auto mac_header_length = preprocessing_bit_count - bits_left; + + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + } + + if (length_indication.has_value()) { + if (length_indication == 0b000000) { + bits_left = 0; + } else if (length_indication == 0b111111 || length_indication == 0b111110) { + bits_left = data.bits_left(); + } else { + bits_left = LengthIndication::from_mac_data(*length_indication) - mac_header_length; + if (fill_bit_indication == 0b1) { + // The fill bit indication shall indicate if there are any fill bits, which shall be added + // whenever the + // size of the TM-SDU is less than the available capacity of the MAC block or less than the size + // of the TM-SDU indicated by the length indication field. The TM-SDU length is equal to the MAC + // PDU length minus the MAC PDU header length. + if (bits_left > data.bits_left()) { + // cap the number of bits left to the maximum available. this should only happen if the + // tm_sdu size + mac header size is not alligned to octect boundary + if (bits_left - data.bits_left() >= 8) { + throw std::runtime_error( + "Fill bits were indicated and the length indication shows a size that does not fit " + "in the MAC, but the length indication is more than 7 bits apart."); + } + bits_left = data.bits_left(); + } + } + } + } else { + bits_left = data.bits_left(); + } + + if (bits_left != 0) { + packet.tm_sdu_ = data.take_vector(bits_left); + } + + return packet; + } + + if (pdu_type == 0b01) { + // MAC-END or MAC-FRAG + // TMA-SAP + auto subtype = data.take<1>(); + if (subtype == 0b0) { + if (channel == LogicalChannel::kStealingChannel) { + throw std::runtime_error("MAC-FRAG may not be sent on stealing channel."); + } + + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, + .type_ = MacPacketType::kMacFragmentUplink}; + + auto fill_bit_indication = data.take<1>(); + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + } + + packet.tm_sdu_ = data.take_vector(data.bits_left()); + + return packet; + } + + { + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, + .type_ = MacPacketType::kMacEndUplink}; + + auto fill_bit_indication = data.take<1>(); + + auto length_indictaion_or_reservation_requirement = data.take<6>(); + + auto bits_left = data.bits_left(); + const auto mac_header_length = preprocessing_bit_count - bits_left; + + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + } + + if (length_indictaion_or_reservation_requirement >= 0b110000) { + // reservation requirement + packet.reservation_requirement_ = length_indictaion_or_reservation_requirement & 0x0f; + bits_left = data.bits_left(); + } else { + // length indication + bits_left = LengthIndication::from_mac_end_uplink(length_indictaion_or_reservation_requirement) - + mac_header_length; + if (fill_bit_indication == 0b1) { + // The fill bit indication shall indicate if there are any fill bits, which shall be added + // whenever the + // size of the TM-SDU is less than the available capacity of the MAC block or less than the size + // of the TM-SDU indicated by the length indication field. The TM-SDU length is equal to the MAC + // PDU length minus the MAC PDU header length. + if (bits_left > data.bits_left()) { + // cap the number of bits left to the maximum available. this should only happen if the + // tm_sdu size + mac header size is not alligned to octect boundary + if (bits_left - data.bits_left() >= 8) { + throw std::runtime_error( + "Fill bits were indicated and the length indication shows a size that does not fit " + "in the MAC, but the length indication is more than 7 bits apart."); + } + bits_left = data.bits_left(); + } + } + } + + packet.tm_sdu_ = data.take_vector(bits_left); + + return packet; + } + } + + if (pdu_type == 0b10) { + throw std::runtime_error("Broadcast PDU should not be handled in parseCPlaneSignallingPacket function!"); + } + + if (pdu_type == 0b11) { + // Supplementary MAC PDU (not on STCH, SCH/HD or SCH-P8/HD) + auto subtype = data.take<1>(); + + if (subtype == 0b1) { + throw std::runtime_error("Supplementary MAC PDU subtype 0b1 is reserved."); + } + + if (channel != LogicalChannel::kSignalingChannelFull) { + throw std::runtime_error("MAC-U-BLCK may only be sent on SCH/F."); + } + + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacUBlck}; + + auto fill_bit_indication = data.take<1>(); + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + } + + packet.encrypted_ = (data.take<1>() == 1U); + auto event_label = data.take<10>(); + packet.address_.set_event_label(event_label); + packet.reservation_requirement_ = data.take<4>(); + + return packet; + } + } else { + // process SCH/F, SCH/HD and STCH on the downlink + // process the SCH/F and STCH of the uplink + const auto preprocessing_bit_count = data.bits_left(); + + auto pdu_type = data.take<2>(); + + if (pdu_type == 0b00) { + // MAC-RESOURCE (downlink) + // TMA-SAP + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacResource}; + + auto fill_bit_indication = data.take<1>(); + + packet.position_of_grant_ = data.take<1>(); + + auto encryption_mode = data.take<2>(); + if (encryption_mode > 0b00) { + packet.encrypted_ = true; + packet.encryption_mode_ = encryption_mode; + } + + packet.random_access_flag_ = data.take<1>(); + + auto length_indication = data.take<6>(); + + if (length_indication == 0b111111) { + packet.fragmentation_ = true; + } + + auto address_type = data.take<3>(); + auto& address = packet.address_; + if (address_type == 0b001) { + address.set_ssi(data.take<24>()); + } else if (address_type == 0b010) { + address.set_event_label(data.take<10>()); + } else if (address_type == 0b011) { + address.set_ussi(data.take<24>()); + } else if (address_type == 0b100) { + address.set_smi(data.take<24>()); + } else if (address_type == 0b101) { + address.set_ssi(data.take<24>()); + address.set_event_label(data.take<10>()); + } else if (address_type == 0b110) { + address.set_ssi(data.take<24>()); + address.set_usage_marker(data.take<6>()); + } else if (address_type == 0b111) { + address.set_smi(data.take<24>()); + address.set_event_label(data.take<10>()); + } + + if (address_type != 0b000) { + // NOTE 3: The immediate napping permission flag shall be present when the PDU is sent using π/8-D8PSK + // or QAM modulation. It shall not be present when the PDU is sent using π/4-DQPSK modulation. + auto power_control_flag = data.take<1>(); + if (power_control_flag == 0b1) { + packet.power_control_element_ = data.take<4>(); + } + auto slot_granting_flag = data.take<1>(); + // The multiple slot granting flag shall be present when the slot granting + // flag is set to 1 and the PDU is sent using QAM modulation. It shall not be + // present when the slot granting flag is set to 0 or the PDU is sent using + // π/4-DQPSK or π/8-D8PSK modulation auto multipleSlotGranting = vec.take<1>(); + // The basic slot granting element shall be present when the slot granting + // flag is set to 1 and either the PDU is sent using π/4-DQPSK or π/8-D8PSK + // modulation, or the PDU is sent using QAM modulation and the multiple slot + // granting flag is set to 0. + if (slot_granting_flag == 0b1) { + packet.basic_slot_granting_element_ = data.take<8>(); + } + auto channel_allocation_flag = data.take<1>(); + if (channel_allocation_flag == 0b1) { + packet.channel_allocation_element_ = ChannelAllocationElement(data); + } + + auto bits_left = data.bits_left(); + const auto mac_header_length = preprocessing_bit_count - bits_left; + + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + } + + if (length_indication < 0b111110) { + bits_left = LengthIndication::from_mac_resource(length_indication) - mac_header_length; + if (fill_bit_indication == 0b1) { + // The fill bit indication shall indicate if there are any fill bits, which shall be added + // whenever the + // size of the TM-SDU is less than the available capacity of the MAC block or less than the size + // of the TM-SDU indicated by the length indication field. The TM-SDU length is equal to the MAC + // PDU length minus the MAC PDU header length. + if (bits_left > data.bits_left()) { + // cap the number of bits left to the maximum available. this should only happen if the + // tm_sdu size + mac header size is not alligned to octect boundary + if (bits_left - data.bits_left() >= 8) { + throw std::runtime_error( + "Fill bits were indicated and the length indication shows a size that does not fit " + "in the MAC, but the length indication is more than 7 bits apart."); + } + bits_left = data.bits_left(); + } + } + } else { + bits_left = data.bits_left(); + } + + packet.tm_sdu_ = data.take_vector(bits_left); + } else { + // The null PDU, if it appears in a MAC block, shall always be the last PDU in that block. Any spare + // capacity after the null PDU shall be filled with fill bits. + data.remove_fill_bits(); + } + + return packet; + } + + if (pdu_type == 0b01) { + // MAC-END or MAC-FRAG + // TMA-SAP + auto subtype = data.take<1>(); + if (subtype == 0b0) { + if (channel == LogicalChannel::kStealingChannel) { + throw std::runtime_error("MAC-FRAG may not be sent on stealing channel."); + } + + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, + .type_ = MacPacketType::kMacFragmentDownlink}; + + auto fill_bit_indication = data.take<1>(); + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + } + + packet.tm_sdu_ = data.take_vector(data.bits_left()); + + return packet; + } + + { + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, + .type_ = MacPacketType::kMacEndDownlink}; + + auto fill_bit_indication = data.take<1>(); + + packet.position_of_grant_ = data.take<1>(); + auto length_indication = data.take<6>(); + // The immediate napping permission flag shall be present when the PDU is sent + // using π/8-D8PSK or QAM modulation. It shall not be present when the PDU is + // sent using π/4-DQPSK modulation. auto immediateNapping = vec.take<1>(); + auto slot_granting_flag = data.take<1>(); + // The multiple slot granting flag shall be present when the slot granting + // flag is set to 1 and the PDU is sent using QAM modulation. It shall not be + // present when the slot granting flag is set to 0 or the PDU is sent using + // π/4-DQPSK or π/8-D8PSK modulation auto multipleSlotGranting = vec.take<1>(); + // The basic slot granting element shall be present when the slot granting + // flag is set to 1 and either the PDU is sent using π/4-DQPSK or π/8-D8PSK + // modulation, or the PDU is sent using QAM modulation and the multiple slot + // granting flag is set to 0. + if (slot_granting_flag == 0b1) { + packet.basic_slot_granting_element_ = data.take<8>(); + } + auto channel_allocation_flag = data.take<1>(); + if (channel_allocation_flag == 0b1) { + packet.channel_allocation_element_ = ChannelAllocationElement(data); + } + + auto mac_header_length = preprocessing_bit_count - data.bits_left(); + auto bits_left = LengthIndication::from_mac_end_downlink(length_indication) - mac_header_length; + + // The fill bit indication shall indicate if there are any fill bits, which shall be added whenever the + // size of the TM-SDU is less than the available capacity of the MAC block or less than the size of the + // TM-SDU indicated by the length indication field. The TM-SDU length is equal to the MAC PDU length + // minus the MAC PDU header length. + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + if (bits_left > data.bits_left()) { + // cap the number of bits left to the maximum available. this should only happen if the tm_sdu + // size + mac header size is not alligned to octect boundary + if (bits_left - data.bits_left() >= 8) { + throw std::runtime_error( + "Fill bits were indicated and the length indication shows a size that does not fit " + "in the MAC, but the length indication is more than 7 bits apart."); + } + bits_left = data.bits_left(); + } + } + + auto tm_sdu = data.take_vector(bits_left); + + return packet; + } + } + + if (pdu_type == 0b10) { + throw std::runtime_error("Broadcast PDU should not be handled in parseCPlaneSignallingPacket function!"); + } + + if (pdu_type == 0b11) { + // Supplementary MAC PDU (not on STCH, SCH/HD or SCH-P8/HD) + auto subtype = data.take<1>(); + + if (subtype == 0b1) { + throw std::runtime_error("Supplementary MAC PDU subtype 0b1 is reserved."); + } + + if (channel != LogicalChannel::kSignalingChannelFull) { + throw std::runtime_error("MAC-D-BLCK may only be sent on SCH/F."); + } + + UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacDBlck}; + + auto fill_bit_indication = data.take<1>(); + if (fill_bit_indication == 0b1) { + data.remove_fill_bits(); + } + + auto encryption_mode = data.take<2>(); + if (encryption_mode > 0b00) { + packet.encrypted_ = true; + packet.encryption_mode_ = encryption_mode; + } + auto event_label = data.take<10>(); + packet.address_.set_event_label(event_label); + + packet.immediate_napping_permission_flag_ = (data.take<1>() == 1U); + auto slot_granting_flag = data.take<1>(); + if (slot_granting_flag == 0b1) { + packet.basic_slot_granting_element_ = data.take<8>(); + } + + packet.tm_sdu_ = data.take_vector(data.bits_left()); + + return packet; + } + } +} + +auto UpperMacPacketBuilder::parseCPlaneSignalling(const BurstType burst_type, const LogicalChannel channel, + BitVector&& data) -> std::vector { + + std::vector packets; + + while (data.bits_left() > 0) { + std::cout << data << std::endl; + if (data.is_mac_padding()) { + std::cout << "Found padding, skipping: " << data << std::endl; + break; + } + auto packet = parseCPlaneSignallingPacket(burst_type, channel, data); + std::cout << packet; + packets.emplace_back(std::move(packet)); + } + + // TODO: one mac may contain multiple mac headers. proccess all the others + // layers first and then continue with the next + return packets; +} + +auto UpperMacPacketBuilder::parseUPlaneSignalling(const LogicalChannel channel, BitVector&& data) + -> UpperMacUPlaneSignallingPacket { + // the only valid packet here is MAC-U-SIGNAL + + auto pdu_type = data.take<2>(); + auto second_slot_stolen = data.take<1>(); + + if (pdu_type != 0b11) { + throw std::runtime_error("UPlane Signalling Packet may only be MAC-U-SIGNAL"); + } + + return UpperMacUPlaneSignallingPacket{ + .logical_channel_ = channel, .type_ = MacPacketType::kMacUSignal, .tm_sdu = std::move(data)}; +} + +auto UpperMacPacketBuilder::parseUPlaneTraffic(const LogicalChannel channel, BitVector&& data) + -> UpperMacUPlaneTrafficPacket { + return UpperMacUPlaneTrafficPacket{.logical_channel_ = channel, .data = std::move(data)}; +} \ No newline at end of file diff --git a/src/l3/mobile_link_entity.cpp b/src/l3/mobile_link_entity.cpp index c91c7aa..689d0fa 100644 --- a/src/l3/mobile_link_entity.cpp +++ b/src/l3/mobile_link_entity.cpp @@ -1,33 +1,7 @@ -#include #include #include -void MobileLinkEntity::service_DMle_system_info(BitVector& vec) { - assert(vec.bits_left() == 42); - - // Location area (14) - location_area_ = vec.take<14>(); - // Subscriber class (16) - subscriber_class_ = vec.take<16>(); - // BS service details (12) - registration_ = vec.take<1>(); - deregistration_ = vec.take<1>(); - priority_cell_ = vec.take<1>(); - minimum_mode_service_ = vec.take<1>(); - migration_ = vec.take<1>(); - system_wide_service_ = vec.take<1>(); - tetra_voice_service_ = vec.take<1>(); - circuit_mode_data_service_ = vec.take<1>(); - sndcp_service_ = vec.take<1>(); - air_interface_encryption_service_ = vec.take<1>(); - advanced_link_supported_ = vec.take<1>(); - - system_info_received_ = true; - - // std::cout << *this; -} - void MobileLinkEntity::service_user_pdu(const AddressType address, BitVector& vec) { std::string mle_pdu[] = {"Reserved", "MM protocol", @@ -91,51 +65,4 @@ void MobileLinkEntity::service_data_pdu(const AddressType address, BitVector& ve 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& { - if (mle.system_info_received_) { - stream << "D-MLE-SYSINFO:" << std::endl; - stream << " Location Area (LA): " << mle.location_area_ << std::endl; - stream << " Subscriber Class 1..16 allowed: 0b" << std::bitset<16>(mle.subscriber_class_) << std::endl; - stream << " " - << (mle.registration_ ? "Registration mandatory on this cell" : "Registration not required on this cell") - << std::endl; - stream << " " - << (mle.deregistration_ ? "De-registration requested on this cell" - : "De-registration not required on this cell") - << std::endl; - stream << " " << (mle.priority_cell_ ? "Cell is a priority cell" : "Cell is not a priority cell") << std::endl; - stream << " " << (mle.minimum_mode_service_ ? "Cell never uses minimum mode" : "Cell may use minimum mode") - << std::endl; - stream << " " - << (mle.migration_ ? "Migration is supported by this cell" : "Migration is not supported by this cell") - << std::endl; - stream << " " - << (mle.system_wide_service_ ? "Normal mode (system wide services supported)" - : "System wide services temporarily not supported") - << std::endl; - stream << " " - << (mle.tetra_voice_service_ ? "TETRA voice service is supported on this cell" - : "TETRA voice service is not supported on this cell") - << std::endl; - stream << " " - << (mle.circuit_mode_data_service_ ? "Circuit mode data service is supported on this cell" - : "Circuit mode data service is not supported on this cell") - << std::endl; - stream << " " - << (mle.sndcp_service_ ? "SNDCP service is available on this cell" - : "SNDCP service is not available on this cell") - << std::endl; - stream << " " - << (mle.air_interface_encryption_service_ ? "Air interface encryption is available on this cell" - : "Air interface encryption is not available on this cell") - << std::endl; - stream << " " - << (mle.advanced_link_supported_ ? "Advanced link is supported on this cell" - : "Advanced link is not supported on this cell") - << std::endl; - } - - return stream; -} +} \ No newline at end of file diff --git a/src/utils/bit_vector.cpp b/src/utils/bit_vector.cpp index 8ed3562..4df2a1b 100644 --- a/src/utils/bit_vector.cpp +++ b/src/utils/bit_vector.cpp @@ -88,15 +88,20 @@ auto BitVector::take_all() -> uint64_t { }; auto BitVector::is_mac_padding() const noexcept -> bool { - if (len_ == 0) + if (len_ == 0) { return false; + } - if (data_[read_offset_] != 1) + // first bit must be true + if (!data_[read_offset_]) { return false; + } + // all other bits must be false for (auto i = 1; i < len_; i++) { - if (data_[read_offset_ + i] != 0) + if (data_[read_offset_ + i]) { return false; + } } return true;