From 4c9f8b84d79de0ad8a3abfb9e78e3de58bf85d0d Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Thu, 20 Jun 2024 01:26:33 +0200 Subject: [PATCH 01/34] add termination flags to the thread pool and upper mac --- include/decoder.hpp | 16 ++-- include/l2/upper_mac.hpp | 8 +- ...ng_ordered_output_thread_pool_executor.hpp | 85 ++++++++++++------- src/decoder.cpp | 10 ++- src/l2/upper_mac.cpp | 17 ++-- 5 files changed, 89 insertions(+), 47 deletions(-) diff --git a/include/decoder.hpp b/include/decoder.hpp index b3e3a4e..a728800 100644 --- a/include/decoder.hpp +++ b/include/decoder.hpp @@ -9,16 +9,15 @@ #pragma once +#include "bit_stream_decoder.hpp" +#include "iq_stream_decoder.hpp" +#include "l2/lower_mac.hpp" #include "l2/upper_mac.hpp" +#include #include #include #include -#include -#include -#include -#include - /** * Tetra downlink decoder for PI/4-DQPSK modulation * @@ -48,6 +47,13 @@ class Decoder { void main_loop(); private: + /// This flag is set when the program should termiate. It is pass down to the next stage in the chain when + /// processing is done in the current stage. + std::atomic_bool termination_flag_ = false; + + /// This flag is passed for the StreamingOrderedOutputThreadPoolExecutor to the upper mac. + std::atomic_bool upper_mac_termination_flag_ = false; + /// The worker queue for the lower mac std::shared_ptr> lower_mac_work_queue_; diff --git a/include/l2/upper_mac.hpp b/include/l2/upper_mac.hpp index e36503f..271c905 100644 --- a/include/l2/upper_mac.hpp +++ b/include/l2/upper_mac.hpp @@ -18,6 +18,7 @@ #include "prometheus.h" #include "reporter.hpp" #include "streaming_ordered_output_thread_pool_executor.hpp" +#include #include #include @@ -26,11 +27,14 @@ class UpperMac { UpperMac() = delete; /// /// \param queue the input queue from the lower mac + /// \param termination_flag the flag that indicates that the worker thread should stop execution after all work is + /// finished /// \param prometheus_exporter the reference to the prometheus exporter that is used for the metrics in the upper /// mac /// \param is_downlink true if this channel is on the downlink UpperMac(const std::shared_ptr>& input_queue, - const std::shared_ptr& prometheus_exporter, Reporter&& reporter, bool is_downlink); + std::atomic_bool& termination_flag, const std::shared_ptr& prometheus_exporter, + Reporter&& reporter, bool is_downlink); ~UpperMac(); private: @@ -48,6 +52,8 @@ class UpperMac { /// The input queue std::shared_ptr> input_queue_; + /// The termination flag + std::atomic_bool& termination_flag_; /// The prometheus metrics std::unique_ptr metrics_; diff --git a/include/streaming_ordered_output_thread_pool_executor.hpp b/include/streaming_ordered_output_thread_pool_executor.hpp index fa1d86f..0dfaba0 100644 --- a/include/streaming_ordered_output_thread_pool_executor.hpp +++ b/include/streaming_ordered_output_thread_pool_executor.hpp @@ -9,6 +9,7 @@ #pragma once +#include #include #include #include @@ -29,11 +30,14 @@ struct TerminationToken {}; template class StreamingOrderedOutputThreadPoolExecutor { public: - using ReturnTypeOrTerminationToken = std::variant; + using OptionalReturnType = std::optional; StreamingOrderedOutputThreadPoolExecutor() = delete; - explicit StreamingOrderedOutputThreadPoolExecutor(int num_workers) { + StreamingOrderedOutputThreadPoolExecutor(std::atomic_bool& input_termination_flag, + std::atomic_bool& output_termination_flag, int num_workers) + : input_termination_flag_(input_termination_flag) + , output_termination_flag_(output_termination_flag) { for (auto i = 0; i < num_workers; i++) { std::thread t(&StreamingOrderedOutputThreadPoolExecutor::worker, this); @@ -54,7 +58,7 @@ template class StreamingOrderedOutputThreadPoolExecutor { }; // append work to the queuetemplate - void queue_work(std::function work) { + void queue_work(std::function work) { { std::lock_guard lock(cv_input_item_mutex_); input_queue_.emplace_back(std::make_pair(input_counter_++, work)); @@ -62,58 +66,66 @@ template class StreamingOrderedOutputThreadPoolExecutor { cv_input_item_.notify_one(); }; - // wait and get a finished item - auto get() -> ReturnTypeOrTerminationToken { + // get a finished item of a nullopt + auto get_or_null() -> OptionalReturnType { using namespace std::chrono_literals; - for (;;) { - std::optional result{}; + OptionalReturnType result{}; - { - std::lock_guard lk(cv_output_item_mutex_); + { + std::lock_guard lk(cv_output_item_mutex_); + + if (auto search = output_map_.find(output_counter_); search != output_map_.end()) { + result = search->second; + output_map_.erase(search); + output_counter_++; + } + } + if (!result.has_value()) { + std::unique_lock lk(cv_output_item_mutex_); + + auto res = cv_output_item_.wait_for(lk, 10ms, [&] { + // find the output item and if found set outputCounter_ to the next item if (auto search = output_map_.find(output_counter_); search != output_map_.end()) { result = search->second; output_map_.erase(search); output_counter_++; + return true; } - } - if (!result.has_value()) { - std::unique_lock lk(cv_output_item_mutex_); + return false; + }); + } - auto res = cv_output_item_.wait_for(lk, 10ms, [&] { - // find the output item and if found set outputCounter_ to the next item - if (auto search = output_map_.find(output_counter_); search != output_map_.end()) { - result = search->second; - output_map_.erase(search); - output_counter_++; - return true; - } + /// propagate the flag if all processing threads have terminated + if (!result.has_value() && alive_thread_count_.load() == 0) { + output_termination_flag_.store(true); + } - return false; - }); - } + return result; + }; - if (result.has_value()) { - return *result; - } - } + auto empty() -> bool { + std::unique_lock lk(cv_output_item_mutex_); + return output_map_.empty(); }; private: auto worker() -> void { using namespace std::chrono_literals; + alive_thread_count_++; + for (;;) { - std::optional>> work{}; + std::optional>> work{}; { std::lock_guard lk(cv_input_item_mutex_); if (!input_queue_.empty()) { work = input_queue_.front(); input_queue_.pop_front(); - } else if (stop) { + } else if (input_termination_flag_.load()) { break; } } @@ -144,8 +156,19 @@ template class StreamingOrderedOutputThreadPoolExecutor { cv_output_item_.notify_all(); } } + + alive_thread_count_--; }; + /// the termination flag on the input + std::atomic_bool& input_termination_flag_; + /// the termination flag on the input for the next stage + std::atomic_bool& output_termination_flag_; + + /// the counter that decrements to 0 if all thread terminated. It is used for the last thread to propagate the + /// termiantion signal. + std::atomic_int alive_thread_count_ = 0; + // locks for input to worker threads std::condition_variable cv_input_item_; std::mutex cv_input_item_mutex_; @@ -155,10 +178,10 @@ template class StreamingOrderedOutputThreadPoolExecutor { std::mutex cv_output_item_mutex_; // queue_ of work with and incrementing index - std::deque>> input_queue_{}; + std::deque>> input_queue_{}; // output queue_. this is a map so we can do a lookup on the current index for ordered output - std::map output_map_{}; + std::map output_map_{}; // contains the value of the next input item uint64_t input_counter_ = 0; diff --git a/src/decoder.cpp b/src/decoder.cpp index db53421..b84093a 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -27,13 +27,15 @@ Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::op std::optional output_file, bool iq_or_bit_stream, std::optional uplink_scrambling_code, const std::shared_ptr& prometheus_exporter) - : lower_mac_work_queue_(std::make_shared>(4)) + : lower_mac_work_queue_(std::make_shared>( + termination_flag_, upper_mac_termination_flag_, 4)) , packed_(packed) , uplink_scrambling_code_(uplink_scrambling_code) , iq_or_bit_stream_(iq_or_bit_stream) { auto is_uplink = uplink_scrambling_code_.has_value(); auto lower_mac = std::make_shared(prometheus_exporter, uplink_scrambling_code); - upper_mac_ = std::make_unique(lower_mac_work_queue_, prometheus_exporter, Reporter(send_port), + upper_mac_ = std::make_unique(lower_mac_work_queue_, upper_mac_termination_flag_, prometheus_exporter, + Reporter(send_port), /*is_downlink=*/!is_uplink); bit_stream_decoder_ = std::make_shared(lower_mac_work_queue_, lower_mac, uplink_scrambling_code_.has_value()); @@ -78,8 +80,8 @@ Decoder::~Decoder() { if (output_file_fd_.has_value()) { close(*output_file_fd_); } - /// Send the termination token to the upper mac worker - lower_mac_work_queue_->queue_work([]() { return TerminationToken{}; }); + /// Terminate the lower mac work queue + termination_flag_ = true; } void Decoder::main_loop() { diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 899087e..5cc85b7 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -23,9 +23,10 @@ #endif UpperMac::UpperMac(const std::shared_ptr>& input_queue, - const std::shared_ptr& prometheus_exporter, Reporter&& reporter, - bool is_downlink) + std::atomic_bool& termination_flag, const std::shared_ptr& prometheus_exporter, + Reporter&& reporter, bool is_downlink) : input_queue_(input_queue) + , termination_flag_(termination_flag) , logical_link_control_(prometheus_exporter, std::move(reporter), is_downlink) { if (prometheus_exporter) { metrics_ = std::make_unique(prometheus_exporter); @@ -47,13 +48,17 @@ UpperMac::~UpperMac() { worker_thread_.join(); } void UpperMac::worker() { for (;;) { - const auto return_value = input_queue_->get(); + const auto return_value = input_queue_->get_or_null(); - if (std::holds_alternative(return_value)) { - return; + if (!return_value) { + if (termination_flag_.load() && input_queue_->empty()) { + break; + } + + continue; } - const auto slots = std::get(return_value); + const auto& slots = *return_value; if (slots) { this->process(*slots); } From d40edfca7512c12ff4c8530a9725066bd10318ff Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Fri, 28 Jun 2024 17:34:10 +0200 Subject: [PATCH 02/34] refactor llc packet parsing and start interfacing with mle. --- include/l2/logical_link_control.hpp | 40 +--- include/l2/logical_link_control_packet.hpp | 124 ++++++++++++ .../logical_link_control_packet_builder.hpp | 47 +++++ include/l2/upper_mac_packet.hpp | 2 + include/l3/mobile_link_entity.hpp | 5 +- src/l2/logical_link_control.cpp | 181 ++---------------- src/l2/upper_mac.cpp | 4 +- src/l3/mobile_link_entity.cpp | 69 +++---- 8 files changed, 232 insertions(+), 240 deletions(-) create mode 100644 include/l2/logical_link_control_packet.hpp create mode 100644 include/l2/logical_link_control_packet_builder.hpp diff --git a/include/l2/logical_link_control.hpp b/include/l2/logical_link_control.hpp index c87030d..828e333 100644 --- a/include/l2/logical_link_control.hpp +++ b/include/l2/logical_link_control.hpp @@ -8,21 +8,18 @@ #pragma once +#include "l2/logical_link_control_packet_builder.hpp" +#include "l2/upper_mac_packet.hpp" #include "l3/mobile_link_entity.hpp" -#include "utils/address.hpp" -#include "utils/bit_vector.hpp" #include "utils/packet_counter_metrics.hpp" -#include -#include #include -#include class LogicalLinkControl { public: LogicalLinkControl() = delete; LogicalLinkControl(const std::shared_ptr& prometheus_exporter, Reporter&& reporter, bool is_downlink) - : mle_(prometheus_exporter, std::move(reporter), is_downlink) { + : packet_builder_(prometheus_exporter, std::move(reporter), is_downlink) { llc_pdu_description_ = {"BL-ADATA without FCS", "BL-DATA without FCS", "BL-UDATA without FCS", @@ -64,9 +61,7 @@ class LogicalLinkControl { }; ~LogicalLinkControl() noexcept = default; - void process(Address address, BitVector& vec); - - friend auto operator<<(std::ostream& stream, const LogicalLinkControl& llc) -> std::ostream&; + auto process(const UpperMacCPlaneSignallingPacket& packet) -> std::unique_ptr; private: static const auto kSupplementaryLlcPdu = 13; @@ -76,29 +71,6 @@ class LogicalLinkControl { std::array supplementary_llc_pdu_description_; std::array layer_2_signalling_pdu_description_; - // Basic link (acknowledged service in connectionless mode) without Frame Check Sequence - void process_bl_adata_without_fcs(Address address, BitVector& vec); - // Basic link (acknowledged service in connectionless mode) without Frame Check Sequence - void process_bl_data_without_fcs(Address address, BitVector& vec); - void process_bl_udata_without_fcs(Address address, BitVector& vec); - void process_bl_ack_without_fcs(Address address, BitVector& vec); - - // Basic link (acknowledged service in connectionless mode) with Frame Check Sequence - void process_bl_adata_with_fcs(Address address, BitVector& vec); - // Basic link (acknowledged service in connectionless mode) with Frame Check Sequence - void process_bl_data_with_fcs(Address address, BitVector& vec); - void process_bl_udata_with_fcs(Address address, BitVector& vec); - void process_bl_ack_with_fcs(Address address, BitVector& vec); - - void process_supplementary_llc_pdu(Address address, BitVector& vec); - void process_layer_2_signalling_pdu(Address address, BitVector& vec); - - static auto compute_fcs(std::vector const& data) -> uint32_t; - static auto check_fcs(BitVector& vec) -> bool; - - MobileLinkEntity mle_; - + LogicalLinkControlPacketBuilder packet_builder_; std::unique_ptr metrics_; -}; - -auto operator<<(std::ostream& stream, const LogicalLinkControl& llc) -> std::ostream&; \ No newline at end of file +}; \ No newline at end of file diff --git a/include/l2/logical_link_control_packet.hpp b/include/l2/logical_link_control_packet.hpp new file mode 100644 index 0000000..88205e9 --- /dev/null +++ b/include/l2/logical_link_control_packet.hpp @@ -0,0 +1,124 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l2/upper_mac_packet.hpp" +#include "utils/bit_vector.hpp" +#include +#include + +/// The type of the basic link packet. +enum class BasicLinkType { + kBlAdataWithoutFcs, + kBlDataWithoutFcs, + kBlUdataWithoutFcs, + kBlAckWithoutFcs, + kBlAdataWithFcs, + kBlDataWithFcs, + kBlUdataWithFcs, + kBlAckWithFcs, +}; + +constexpr auto to_string(BasicLinkType type) noexcept -> const char* { + switch (type) { + case BasicLinkType::kBlAdataWithoutFcs: + return "BL-ADATA without FCS"; + case BasicLinkType::kBlDataWithoutFcs: + return "BL-DATA without FCS"; + case BasicLinkType::kBlUdataWithoutFcs: + return "BL-UDATA without FCS"; + case BasicLinkType::kBlAckWithoutFcs: + return "BL-ACK without FCS"; + case BasicLinkType::kBlAdataWithFcs: + return "BL-ADATA with FCS"; + case BasicLinkType::kBlDataWithFcs: + return "BL-DATA with FCS"; + case BasicLinkType::kBlUdataWithFcs: + return "BL-UDATA with FCS"; + case BasicLinkType::kBlAckWithFcs: + return "BL-ACK with FCS"; + } +}; + +struct BasicLinkInformation { + BasicLinkType basic_link_type_; + std::optional n_r_; + std::optional n_s_; + std::optional fcs_good_; + + BasicLinkInformation() = delete; + + /// construct a BasicLinkInformation from a BitVector + explicit BasicLinkInformation(BitVector& data) { + auto pdu_type = data.take<4>(); + + switch (pdu_type) { + case 0b0000: + basic_link_type_ = BasicLinkType::kBlAdataWithoutFcs; + n_r_ = data.take<1>(); + n_s_ = data.take<1>(); + break; + case 0b0001: + basic_link_type_ = BasicLinkType::kBlDataWithoutFcs; + n_s_ = data.take<1>(); + break; + case 0b0010: + basic_link_type_ = BasicLinkType::kBlUdataWithoutFcs; + break; + case 0b0011: + basic_link_type_ = BasicLinkType::kBlAckWithoutFcs; + n_r_ = data.take<1>(); + break; + case 0b0100: + basic_link_type_ = BasicLinkType::kBlAdataWithFcs; + n_r_ = data.take<1>(); + n_s_ = data.take<1>(); + break; + case 0b0101: + basic_link_type_ = BasicLinkType::kBlDataWithFcs; + n_s_ = data.take<1>(); + break; + case 0b0110: + basic_link_type_ = BasicLinkType::kBlUdataWithFcs; + break; + case 0b0111: + basic_link_type_ = BasicLinkType::kBlAckWithFcs; + n_r_ = data.take<1>(); + break; + default: + throw std::runtime_error( + "Did not expect something other than basic link in LogicalLinkControlPacket parser!"); + } + + if (pdu_type >= 0b0100) { + auto fcs = data.take_last<32>(); + auto computed_fcs = data.compute_fcs(); + fcs_good_ = fcs == computed_fcs; + } + } +}; + +auto operator<<(std::ostream& stream, const BasicLinkInformation& llc) -> std::ostream&; + +/// The packet that is parsed in the logical link control layer. Currently we only implement basic link. +struct LogicalLinkControlPacket : public UpperMacCPlaneSignallingPacket { + std::optional basic_link_information_; + BitVector tl_sdu_; + + LogicalLinkControlPacket() = delete; + + explicit LogicalLinkControlPacket(const UpperMacCPlaneSignallingPacket& packet) + : UpperMacCPlaneSignallingPacket(packet) { + auto data = BitVector(*tm_sdu_); + basic_link_information_ = BasicLinkInformation(data); + tl_sdu_ = data; + } +}; + +auto operator<<(std::ostream& stream, const LogicalLinkControlPacket& llc) -> std::ostream&; \ No newline at end of file diff --git a/include/l2/logical_link_control_packet_builder.hpp b/include/l2/logical_link_control_packet_builder.hpp new file mode 100644 index 0000000..b5cf56d --- /dev/null +++ b/include/l2/logical_link_control_packet_builder.hpp @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l2/logical_link_control_packet.hpp" +#include "l2/upper_mac_packet.hpp" +#include "l3/mobile_link_entity.hpp" +#include +#include + +/// The packet that is parsed in the logical link control layer. Currently we only implement basic link. +class LogicalLinkControlPacketBuilder { + private: + MobileLinkEntity mle_; + + public: + LogicalLinkControlPacketBuilder() = delete; + + explicit LogicalLinkControlPacketBuilder(const std::shared_ptr& prometheus_exporter, + Reporter&& reporter, bool is_downlink) + : mle_(prometheus_exporter, std::move(reporter), is_downlink){}; + + [[nodiscard]] auto parse_c_plane_signalling(const UpperMacCPlaneSignallingPacket& packet) + -> std::unique_ptr { + auto pdu_type = packet.tm_sdu_->look<4>(0); + + /// We only implemented packet parsing for Basic Link PDUs at this point in time + if (pdu_type <= 0b1000) { + auto llc_packet = LogicalLinkControlPacket(packet); + + /// Contains optional TL-SDU pass to MLE for further parsing. + if (llc_packet.tl_sdu_.bits_left() > 0) { + return mle_.process(llc_packet); + } + + return std::make_unique(llc_packet); + } + + return std::make_unique(packet); + }; +}; \ No newline at end of file diff --git a/include/l2/upper_mac_packet.hpp b/include/l2/upper_mac_packet.hpp index ed54d4e..a0f82ae 100644 --- a/include/l2/upper_mac_packet.hpp +++ b/include/l2/upper_mac_packet.hpp @@ -409,6 +409,8 @@ struct UpperMacCPlaneSignallingPacket { (type_ == MacPacketType::kMacFragmentUplink) || (type_ == MacPacketType::kMacEndUplink); }; + UpperMacCPlaneSignallingPacket(const UpperMacCPlaneSignallingPacket&) = default; + friend auto operator<<(std::ostream& stream, const UpperMacCPlaneSignallingPacket& packet) -> std::ostream&; }; diff --git a/include/l3/mobile_link_entity.hpp b/include/l3/mobile_link_entity.hpp index 2464fcf..ad36026 100644 --- a/include/l3/mobile_link_entity.hpp +++ b/include/l3/mobile_link_entity.hpp @@ -9,6 +9,7 @@ #pragma once +#include "l2/logical_link_control_packet.hpp" #include "l3/circuit_mode_control_entity.hpp" #include "l3/mobile_management.hpp" #include "prometheus.h" @@ -60,11 +61,9 @@ class MobileLinkEntity { }; ~MobileLinkEntity() noexcept = default; - void service_user_pdu(Address address, BitVector& vec); + auto process(const LogicalLinkControlPacket& packet) -> std::unique_ptr; private: - void service_data_pdu(Address address, BitVector& vec); - CircuitModeControlEntity cmce_; MobileManagement mm_; diff --git a/src/l2/logical_link_control.cpp b/src/l2/logical_link_control.cpp index 2488464..15b19c0 100644 --- a/src/l2/logical_link_control.cpp +++ b/src/l2/logical_link_control.cpp @@ -7,32 +7,14 @@ */ #include "l2/logical_link_control.hpp" -#include +#include "l2/logical_link_control_packet_builder.hpp" +#include "l2/upper_mac_packet.hpp" #include +#include -auto LogicalLinkControl::check_fcs(BitVector& vec) -> bool { - // remove last 32 bits - std::bitset<32> fcs = vec.take_last<32>(); - - std::bitset<32> computed_fcs = vec.compute_fcs(); - - if (fcs != computed_fcs) { - std::cout << " FCS error" << std::endl; - std::cout << " FCS 0b" << fcs << std::endl; - std::cout << " computed FCS: 0b" << computed_fcs << std::endl; - return false; - } - - std::cout << " FCS good" << std::endl; - return true; -} - -void LogicalLinkControl::process(const Address address, BitVector& vec) { - std::cout << "LLC received: " << std::endl; - std::cout << " Address: " << address << std::endl; - std::cout << " Data: " << vec << std::endl; - - auto pdu_type = vec.take<4>(); +auto LogicalLinkControl::process(const UpperMacCPlaneSignallingPacket& packet) + -> std::unique_ptr { + auto pdu_type = packet.tm_sdu_->look<4>(0); const auto& pdu_name = llc_pdu_description_.at(pdu_type); // Skip incrementing the metrics for Supplementary LLC PDU and Layer 2 signalling PDU @@ -40,146 +22,23 @@ void LogicalLinkControl::process(const Address address, BitVector& vec) { metrics_->increment(pdu_name); } - std::cout << " " << pdu_name << std::endl; - - switch (pdu_type) { - case 0b0000: - // BL-ADATA without FCS - process_bl_adata_without_fcs(address, vec); - break; - case 0b0001: - // BL-DATA without FCS - process_bl_data_without_fcs(address, vec); - break; - case 0b0010: - // BL-UDATA without FCS - process_bl_udata_without_fcs(address, vec); - break; - case 0b0011: - // BL-ACK without FCS - process_bl_ack_without_fcs(address, vec); - break; - case 0b0100: - // BL-ADATA with FCS - process_bl_adata_with_fcs(address, vec); - break; - case 0b0101: - // BL-DATA with FCS - process_bl_data_with_fcs(address, vec); - break; - case 0b0110: - // BL-UDATA with FCS - process_bl_udata_with_fcs(address, vec); - break; - case 0b0111: - // BL-ACK with FCS - process_bl_ack_with_fcs(address, vec); - break; - case 0b1101: - process_supplementary_llc_pdu(address, vec); - break; - case 0b1110: - process_layer_2_signalling_pdu(address, vec); - break; - default: - break; - } -} - -void LogicalLinkControl::process_bl_adata_without_fcs(const Address address, BitVector& vec) { - auto n_r = vec.take<1>(); - auto n_s = vec.take<1>(); - - std::cout << " N(R) = 0b" << std::bitset<1>(n_r) << std::endl; - std::cout << " N(S) = 0b" << std::bitset<1>(n_s) << std::endl; - - mle_.service_user_pdu(address, vec); -} - -void LogicalLinkControl::process_bl_data_without_fcs(const Address address, BitVector& vec) { - auto n_s = vec.take<1>(); - std::cout << " N(S) = 0b" << std::bitset<1>(n_s) << std::endl; - - mle_.service_user_pdu(address, vec); -} - -void LogicalLinkControl::process_bl_udata_without_fcs(const Address address, BitVector& vec) { - mle_.service_user_pdu(address, vec); -} - -void LogicalLinkControl::process_bl_ack_without_fcs(const Address address, BitVector& vec) { - auto n_r = vec.take<1>(); - std::cout << " N(R) = 0b" << std::bitset<1>(n_r) << std::endl; - - mle_.service_user_pdu(address, vec); -} - -void LogicalLinkControl::process_bl_adata_with_fcs(const Address address, BitVector& vec) { - auto n_r = vec.take<1>(); - auto n_s = vec.take<1>(); - - std::cout << " N(R) = 0b" << std::bitset<1>(n_r) << std::endl; - std::cout << " N(S) = 0b" << std::bitset<1>(n_s) << std::endl; - - if (LogicalLinkControl::check_fcs(vec)) { - mle_.service_user_pdu(address, vec); - } -} - -void LogicalLinkControl::process_bl_data_with_fcs(const Address address, BitVector& vec) { - auto n_s = vec.take<1>(); - std::cout << " N(S) = 0b" << std::bitset<1>(n_s) << std::endl; - - if (LogicalLinkControl::check_fcs(vec)) { - mle_.service_user_pdu(address, vec); - } -} - -void LogicalLinkControl::process_bl_udata_with_fcs(const Address address, BitVector& vec) { - if (LogicalLinkControl::check_fcs(vec)) { - mle_.service_user_pdu(address, vec); - } -} - -void LogicalLinkControl::process_bl_ack_with_fcs(const Address address, BitVector& vec) { - auto n_r = vec.take<1>(); - std::cout << " N(R) = 0b" << std::bitset<1>(n_r) << std::endl; + if (pdu_type == kSupplementaryLlcPdu) { + auto pdu_type = packet.tm_sdu_->look<2>(4); + const auto& pdu_name = supplementary_llc_pdu_description_.at(pdu_type); - if (LogicalLinkControl::check_fcs(vec)) { - mle_.service_user_pdu(address, vec); + if (metrics_) { + metrics_->increment(pdu_name); + } } -} -void LogicalLinkControl::process_supplementary_llc_pdu(const Address address, BitVector& vec) { - auto pdu_type = vec.take<2>(); - const auto& pdu_name = supplementary_llc_pdu_description_.at(pdu_type); + if (pdu_type == kLayer2SignallingPdu) { + auto pdu_type = packet.tm_sdu_->look<4>(4); + const auto& pdu_name = layer_2_signalling_pdu_description_.at(pdu_type); - std::cout << " " << pdu_name << std::endl; - std::cout << " Data: " << vec << std::endl; - - if (metrics_) { - metrics_->increment(pdu_name); - } - - switch (pdu_type) { - case 0b00: - break; - case 0b01: - break; - case 0b10: - break; - case 0b11: - break; - } -} - -void LogicalLinkControl::process_layer_2_signalling_pdu(const Address address, BitVector& vec) { - auto pdu_type = vec.take<4>(); - const auto& pdu_name = layer_2_signalling_pdu_description_.at(pdu_type); - - if (metrics_) { - metrics_->increment(pdu_name); + if (metrics_) { + metrics_->increment(pdu_name); + } } -} -auto operator<<(std::ostream& stream, const LogicalLinkControl& llc) -> std::ostream& { return stream; } + return packet_builder_.parse_c_plane_signalling(packet); +} \ No newline at end of file diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 5cc85b7..8db0ac9 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -142,7 +142,7 @@ auto UpperMac::processPackets(UpperMacPackets&& packets) -> void { } for (const auto& packet : c_plane_packets) { - auto data = BitVector(*packet.tm_sdu_); - logical_link_control_.process(packet.address_, data); + auto parsed_packet = logical_link_control_.process(packet); + /// TODO: send this packet to borzoi } } \ No newline at end of file diff --git a/src/l3/mobile_link_entity.cpp b/src/l3/mobile_link_entity.cpp index fbf1d0a..f22c3b1 100644 --- a/src/l3/mobile_link_entity.cpp +++ b/src/l3/mobile_link_entity.cpp @@ -8,61 +8,50 @@ */ #include "l3/mobile_link_entity.hpp" +#include "l2/logical_link_control_packet.hpp" +#include "utils/bit_vector.hpp" #include +#include -void MobileLinkEntity::service_user_pdu(const Address address, BitVector& vec) { - if (vec.bits_left() == 0) { - return; - } +auto MobileLinkEntity::process(const LogicalLinkControlPacket& packet) -> std::unique_ptr { + auto data = BitVector(packet.tl_sdu_); - auto pdu_type = vec.take<3>(); + auto pdu_type = data.take<3>(); const auto& pdu_name = protocol_discriminator_description_.at(pdu_type); - std::cout << "MLE " << pdu_name << " " << vec << std::endl; - if (metrics_ && pdu_type != kMleProtocol) { metrics_->increment(pdu_name); } + if (pdu_type == kMleProtocol) { + auto pdu_type = data.take<3>(); + const auto& pdu_name = mle_pdu_description_.at(pdu_type); + + if (metrics_ && pdu_type != kExtendedPdu) { + metrics_->increment(pdu_name); + } + + if (pdu_type == kExtendedPdu) { + auto pdu_type = data.take<4>(); + const auto& pdu_name = mle_pdu_extension_description_.at(pdu_type); + + if (metrics_) { + metrics_->increment(pdu_name); + } + } + } + switch (pdu_type) { case 0b001: - mm_.process(address, vec); + mm_.process(packet.address_, data); break; case 0b010: - cmce_.process(address, vec); - break; - case kMleProtocol: - service_data_pdu(address, vec); + cmce_.process(packet.address_, data); break; default: - break; + return std::make_unique(packet); } -} -void MobileLinkEntity::service_data_pdu(const Address address, BitVector& vec) { - if (vec.bits_left() == 0) { - return; - } - - auto pdu_type = vec.take<3>(); - const auto& pdu_name = mle_pdu_description_.at(pdu_type); - - if (metrics_ && pdu_type != kExtendedPdu) { - metrics_->increment(pdu_name); - } - - std::cout << " " << pdu_name << std::endl; - std::cout << " " << vec << std::endl; - - if (pdu_type == kExtendedPdu) { - auto pdu_type = vec.take<4>(); - const auto& pdu_name = mle_pdu_extension_description_.at(pdu_type); - - if (metrics_) { - metrics_->increment(pdu_name); - } - - std::cout << " " << pdu_name << std::endl; - std::cout << " " << vec << std::endl; - } + // TODO: return the specific parsed packet which is currently not handled yet + return std::make_unique(packet); } \ No newline at end of file From e739fa4bd98ff7ba75fc85dbd389caaee45eb327 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 1 Jul 2024 23:49:01 +0200 Subject: [PATCH 03/34] refactor layer 3 packet parsing. header and source files still need to be sperated. leyer 3 parser/metrics architecture has to be refactored to use a shared abstraction. --- include/l2/logical_link_control.hpp | 6 +- include/l2/logical_link_control_packet.hpp | 34 +- .../logical_link_control_packet_builder.hpp | 5 +- include/l2/upper_mac.hpp | 4 +- include/l2/upper_mac_packet.hpp | 15 + include/l3/circuit_mode_control_entity.hpp | 56 +-- .../l3/circuit_mode_control_entity_packet.hpp | 361 ++++++++++++++++++ ...uit_mode_control_entity_packet_builder.hpp | 34 ++ include/l3/mobile_link_entity.hpp | 70 ++-- include/l3/mobile_link_entity_packet.hpp | 68 ++++ .../l3/mobile_link_entity_packet_builder.hpp | 54 +++ include/l3/mobile_management.hpp | 78 ++-- include/l3/mobile_management_packet.hpp | 18 + include/l3/short_data_service.hpp | 17 +- include/l3/short_data_service_packet.hpp | 169 ++++++++ src/decoder.cpp | 4 +- src/l2/upper_mac.cpp | 33 +- src/l2/upper_mac_packet_builder.cpp | 24 +- src/l3/circuit_mode_control_entity.cpp | 105 +---- src/l3/mobile_link_entity.cpp | 24 +- src/l3/mobile_management.cpp | 21 +- src/l3/short_data_service.cpp | 165 +------- 22 files changed, 892 insertions(+), 473 deletions(-) create mode 100644 include/l3/circuit_mode_control_entity_packet.hpp create mode 100644 include/l3/circuit_mode_control_entity_packet_builder.hpp create mode 100644 include/l3/mobile_link_entity_packet.hpp create mode 100644 include/l3/mobile_link_entity_packet_builder.hpp create mode 100644 include/l3/mobile_management_packet.hpp create mode 100644 include/l3/short_data_service_packet.hpp diff --git a/include/l2/logical_link_control.hpp b/include/l2/logical_link_control.hpp index 828e333..7d41932 100644 --- a/include/l2/logical_link_control.hpp +++ b/include/l2/logical_link_control.hpp @@ -10,16 +10,14 @@ #include "l2/logical_link_control_packet_builder.hpp" #include "l2/upper_mac_packet.hpp" -#include "l3/mobile_link_entity.hpp" #include "utils/packet_counter_metrics.hpp" #include class LogicalLinkControl { public: LogicalLinkControl() = delete; - LogicalLinkControl(const std::shared_ptr& prometheus_exporter, Reporter&& reporter, - bool is_downlink) - : packet_builder_(prometheus_exporter, std::move(reporter), is_downlink) { + LogicalLinkControl(const std::shared_ptr& prometheus_exporter) + : packet_builder_(prometheus_exporter) { llc_pdu_description_ = {"BL-ADATA without FCS", "BL-DATA without FCS", "BL-UDATA without FCS", diff --git a/include/l2/logical_link_control_packet.hpp b/include/l2/logical_link_control_packet.hpp index 88205e9..0a55653 100644 --- a/include/l2/logical_link_control_packet.hpp +++ b/include/l2/logical_link_control_packet.hpp @@ -10,6 +10,7 @@ #include "l2/upper_mac_packet.hpp" #include "utils/bit_vector.hpp" +#include #include #include @@ -60,35 +61,27 @@ struct BasicLinkInformation { switch (pdu_type) { case 0b0000: - basic_link_type_ = BasicLinkType::kBlAdataWithoutFcs; n_r_ = data.take<1>(); n_s_ = data.take<1>(); break; case 0b0001: - basic_link_type_ = BasicLinkType::kBlDataWithoutFcs; n_s_ = data.take<1>(); break; case 0b0010: - basic_link_type_ = BasicLinkType::kBlUdataWithoutFcs; break; case 0b0011: - basic_link_type_ = BasicLinkType::kBlAckWithoutFcs; n_r_ = data.take<1>(); break; case 0b0100: - basic_link_type_ = BasicLinkType::kBlAdataWithFcs; n_r_ = data.take<1>(); n_s_ = data.take<1>(); break; case 0b0101: - basic_link_type_ = BasicLinkType::kBlDataWithFcs; n_s_ = data.take<1>(); break; case 0b0110: - basic_link_type_ = BasicLinkType::kBlUdataWithFcs; break; case 0b0111: - basic_link_type_ = BasicLinkType::kBlAckWithFcs; n_r_ = data.take<1>(); break; default: @@ -96,6 +89,8 @@ struct BasicLinkInformation { "Did not expect something other than basic link in LogicalLinkControlPacket parser!"); } + basic_link_type_ = BasicLinkType(pdu_type); + if (pdu_type >= 0b0100) { auto fcs = data.take_last<32>(); auto computed_fcs = data.compute_fcs(); @@ -104,7 +99,20 @@ struct BasicLinkInformation { } }; -auto operator<<(std::ostream& stream, const BasicLinkInformation& llc) -> std::ostream&; +inline auto operator<<(std::ostream& stream, const BasicLinkInformation& bli) -> std::ostream& { + stream << " Basic Link Information: " << to_string(bli.basic_link_type_); + if (bli.n_r_) { + stream << " N(R): " << std::bitset<1>(*bli.n_r_); + } + if (bli.n_s_) { + stream << " N(S): " << std::bitset<1>(*bli.n_s_); + } + if (bli.fcs_good_) { + stream << " FCS good: " << (*bli.fcs_good_ ? "true" : "false"); + } + stream << std::endl; + return stream; +} /// The packet that is parsed in the logical link control layer. Currently we only implement basic link. struct LogicalLinkControlPacket : public UpperMacCPlaneSignallingPacket { @@ -121,4 +129,10 @@ struct LogicalLinkControlPacket : public UpperMacCPlaneSignallingPacket { } }; -auto operator<<(std::ostream& stream, const LogicalLinkControlPacket& llc) -> std::ostream&; \ No newline at end of file +inline auto operator<<(std::ostream& stream, const LogicalLinkControlPacket& llc) -> std::ostream& { + if (llc.basic_link_information_) { + stream << *llc.basic_link_information_; + } + stream << " TL-SDU: " << llc.tl_sdu_ << std::endl; + return stream; +} \ No newline at end of file diff --git a/include/l2/logical_link_control_packet_builder.hpp b/include/l2/logical_link_control_packet_builder.hpp index b5cf56d..f30818d 100644 --- a/include/l2/logical_link_control_packet_builder.hpp +++ b/include/l2/logical_link_control_packet_builder.hpp @@ -22,9 +22,8 @@ class LogicalLinkControlPacketBuilder { public: LogicalLinkControlPacketBuilder() = delete; - explicit LogicalLinkControlPacketBuilder(const std::shared_ptr& prometheus_exporter, - Reporter&& reporter, bool is_downlink) - : mle_(prometheus_exporter, std::move(reporter), is_downlink){}; + explicit LogicalLinkControlPacketBuilder(const std::shared_ptr& prometheus_exporter) + : mle_(prometheus_exporter){}; [[nodiscard]] auto parse_c_plane_signalling(const UpperMacCPlaneSignallingPacket& packet) -> std::unique_ptr { diff --git a/include/l2/upper_mac.hpp b/include/l2/upper_mac.hpp index 271c905..1a8e4a6 100644 --- a/include/l2/upper_mac.hpp +++ b/include/l2/upper_mac.hpp @@ -31,10 +31,8 @@ class UpperMac { /// finished /// \param prometheus_exporter the reference to the prometheus exporter that is used for the metrics in the upper /// mac - /// \param is_downlink true if this channel is on the downlink UpperMac(const std::shared_ptr>& input_queue, - std::atomic_bool& termination_flag, const std::shared_ptr& prometheus_exporter, - Reporter&& reporter, bool is_downlink); + std::atomic_bool& termination_flag, const std::shared_ptr& prometheus_exporter); ~UpperMac(); private: diff --git a/include/l2/upper_mac_packet.hpp b/include/l2/upper_mac_packet.hpp index a0f82ae..3fd9a92 100644 --- a/include/l2/upper_mac_packet.hpp +++ b/include/l2/upper_mac_packet.hpp @@ -8,6 +8,7 @@ #pragma once +#include "burst_type.hpp" #include "l2/logical_channel.hpp" #include "utils/address.hpp" #include "utils/bit_vector.hpp" @@ -357,6 +358,8 @@ struct ChannelAllocationElement { auto operator<<(std::ostream& stream, const ChannelAllocationElement& element) -> std::ostream&; struct UpperMacCPlaneSignallingPacket { + /// the burst type which was used to send this pavket + BurstType burst_type_; /// the type of the logical channel on which this packet is sent LogicalChannel logical_channel_; /// the type of the mac packet @@ -409,7 +412,19 @@ struct UpperMacCPlaneSignallingPacket { (type_ == MacPacketType::kMacFragmentUplink) || (type_ == MacPacketType::kMacEndUplink); }; + /// check if this packet is sent on downlink + [[nodiscard]] auto is_downlink() const -> bool { return is_downlink_burst(burst_type_); }; + + /// check if this packet is sent on uplink + [[nodiscard]] auto is_uplink() const -> bool { return is_uplink_burst(burst_type_); }; + UpperMacCPlaneSignallingPacket(const UpperMacCPlaneSignallingPacket&) = default; + UpperMacCPlaneSignallingPacket(BurstType burst_type, LogicalChannel logical_channel, MacPacketType type) + : burst_type_(burst_type) + , logical_channel_(logical_channel) + , type_(type){}; + + virtual ~UpperMacCPlaneSignallingPacket() = default; friend auto operator<<(std::ostream& stream, const UpperMacCPlaneSignallingPacket& packet) -> std::ostream&; }; diff --git a/include/l3/circuit_mode_control_entity.hpp b/include/l3/circuit_mode_control_entity.hpp index 588a288..448a81b 100644 --- a/include/l3/circuit_mode_control_entity.hpp +++ b/include/l3/circuit_mode_control_entity.hpp @@ -8,67 +8,25 @@ #pragma once -#include "l3/short_data_service.hpp" -#include "reporter.hpp" -#include "utils/address.hpp" -#include "utils/bit_vector.hpp" +#include "l3/circuit_mode_control_entity_packet.hpp" +#include "l3/circuit_mode_control_entity_packet_builder.hpp" +#include "l3/mobile_link_entity_packet.hpp" #include "utils/packet_counter_metrics.hpp" -#include -#include class CircuitModeControlEntity { public: CircuitModeControlEntity() = delete; - explicit CircuitModeControlEntity(const std::shared_ptr& prometheus_exporter, - Reporter&& reporter, bool is_downlink) - : sds_(ShortDataService(prometheus_exporter, std::move(reporter))) - , is_downlink_(is_downlink) { - if (is_downlink) { - cmce_pdu_description_ = {"D-ALERT", "D-CALL-PROCEEDING", - "D-CONNECT", "D-CONNECT ACKNOWLEDGE", - "D-DISCONNECT", "D-INFO", - "D-RELEASE", "D-SETUP", - "D-STATUS", "D-TX CEASED", - "D-TX CONTINUE", "D-TX GRANTED", - "D-TX WAIT", "D-TX INTERRUPT", - "D-CALL-RESTORE", "D-SDS-DATA", - "D-FACILITY", "D-Reserved17", - "D-Reserved18", "D-Reserved19", - "D-Reserved20", "D-Reserved21", - "D-Reserved22", "D-Reserved23", - "D-Reserved24", "D-Reserved25", - "D-Reserved26", "D-Reserved27", - "D-Reserved28", "D-Reserved29", - "D-Reserved30", "CMCE FUNCTION NOT SUPPORTED"}; - } else { - cmce_pdu_description_ = {"U-ALERT", "U-Reserved1", "U-CONNECT", "U-Reserved3", - "U-DISCONNECT", "U-INFO", "U-RELEASE", "U-SETUP", - "U-STATUS", "U-TX CEASED", "U-TX DEMAND", "U-Reserved11", - "U-Reserved12", "U-Reserved13", "U-CALL-RESTORE", "U-SDS-DATA", - "U-FACILITY", "U-Reserved17", "U-Reserved18", "U-Reserved19", - "U-Reserved20", "U-Reserved21", "U-Reserved22", "U-Reserved23", - "U-Reserved24", "U-Reserved25", "U-Reserved26", "U-Reserved27", - "U-Reserved28", "U-Reserved29", "U-Reserved30", "CMCE FUNCTION NOT SUPPORTED"}; - } - + explicit CircuitModeControlEntity(const std::shared_ptr& prometheus_exporter) + : packet_builder_(prometheus_exporter) { if (prometheus_exporter) { metrics_ = std::make_unique(prometheus_exporter, "circuit_mode_control_entity"); } }; ~CircuitModeControlEntity() noexcept = default; - void process(Address address, BitVector& vec); + auto process(const MobileLinkEntityPacket& packet) -> std::unique_ptr; private: - void process_d_sds_data(Address to_address, BitVector& vec); - void process_u_sds_data(Address from_address, BitVector& vec); - - static const auto kSdsData = 15; - - ShortDataService sds_; - bool is_downlink_; - - std::array cmce_pdu_description_; - + CircuitModeControlEntityPacketBuilder packet_builder_; std::unique_ptr metrics_; }; \ No newline at end of file diff --git a/include/l3/circuit_mode_control_entity_packet.hpp b/include/l3/circuit_mode_control_entity_packet.hpp new file mode 100644 index 0000000..9df3429 --- /dev/null +++ b/include/l3/circuit_mode_control_entity_packet.hpp @@ -0,0 +1,361 @@ +/* + * Copyright (C) 2022 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l3/mobile_link_entity_packet.hpp" +#include "utils/address.hpp" +#include "utils/bit_vector.hpp" +#include +#include + +enum class CircuitModeControlEntityDownlinkPacketType { + kDAlert, + kDCallProceeding, + kDConnect, + kDConnectAcknowledge, + kDDisconnect, + kDInfo, + kDRelease, + kDSetup, + kDStatus, + kDTxCeased, + kDTxContinue, + kDTxGranted, + kDTxWait, + kDTxInterrupt, + kDCallRestore, + kDSdsData, + kDFacility, + kDReservered17, + kDReservered18, + kDReservered19, + kDReservered20, + kDReservered21, + kDReservered22, + kDReservered23, + kDReservered24, + kDReservered25, + kDReservered26, + kDReservered27, + kDReservered28, + kDReservered29, + kDReservered30, + kCmceFunctionNotSupported, +}; + +constexpr auto to_string(CircuitModeControlEntityDownlinkPacketType type) -> const char* { + switch (type) { + case CircuitModeControlEntityDownlinkPacketType::kDAlert: + return "D-ALERT"; + case CircuitModeControlEntityDownlinkPacketType::kDCallProceeding: + return "D-CALL-PROCEEDING"; + case CircuitModeControlEntityDownlinkPacketType::kDConnect: + return "D-CONNECT"; + case CircuitModeControlEntityDownlinkPacketType::kDConnectAcknowledge: + return "D-CONNECT ACKNOWLEDGE"; + case CircuitModeControlEntityDownlinkPacketType::kDDisconnect: + return "D-DISCONNECT"; + case CircuitModeControlEntityDownlinkPacketType::kDInfo: + return "D-INFO"; + case CircuitModeControlEntityDownlinkPacketType::kDRelease: + return "D-RELEASE"; + case CircuitModeControlEntityDownlinkPacketType::kDSetup: + return "D-SETUP"; + case CircuitModeControlEntityDownlinkPacketType::kDStatus: + return "D-STATUS"; + case CircuitModeControlEntityDownlinkPacketType::kDTxCeased: + return "D-TX CEASED"; + case CircuitModeControlEntityDownlinkPacketType::kDTxContinue: + return "D-TX CONTINUE"; + case CircuitModeControlEntityDownlinkPacketType::kDTxGranted: + return "D-TX GRANTED"; + case CircuitModeControlEntityDownlinkPacketType::kDTxWait: + return "D-TX WAIT"; + case CircuitModeControlEntityDownlinkPacketType::kDTxInterrupt: + return "D-TX INTERRUPT"; + case CircuitModeControlEntityDownlinkPacketType::kDCallRestore: + return "D-CALL-RESTORE"; + case CircuitModeControlEntityDownlinkPacketType::kDSdsData: + return "D-SDS-DATA"; + case CircuitModeControlEntityDownlinkPacketType::kDFacility: + return "D-FACILITY"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered17: + return "D-Reserved17"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered18: + return "D-Reserved18"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered19: + return "D-Reserved19"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered20: + return "D-Reserved20"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered21: + return "D-Reserved21"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered22: + return "D-Reserved22"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered23: + return "D-Reserved23"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered24: + return "D-Reserved24"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered25: + return "D-Reserved25"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered26: + return "D-Reserved26"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered27: + return "D-Reserved27"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered28: + return "D-Reserved28"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered29: + return "D-Reserved29"; + case CircuitModeControlEntityDownlinkPacketType::kDReservered30: + return "D-Reserved30"; + case CircuitModeControlEntityDownlinkPacketType::kCmceFunctionNotSupported: + return "CMCE FUNCTION NOT SUPPORTED"; + } +}; + +enum class CircuitModeControlEntityUplinkPacketType { + kUAlert, + kUReserved1, + kUConnect, + kUReserved3, + kUDisconnect, + kUInfo, + kURelease, + kUSetup, + kUStatus, + kUTxCeased, + kUTxDemand, + kUReserved11, + kUReserved12, + kUReserved13, + kUCallRestore, + kUSdsData, + kUFacility, + kUReserved17, + kUReserved18, + kUReserved19, + kUReserved20, + kUReserved21, + kUReserved22, + kUReserved23, + kUReserved24, + kUReserved25, + kUReserved26, + kUReserved27, + kUReserved28, + kUReserved29, + kUReserved30, + kCmceFunctionNotSupported, +}; + +constexpr auto to_string(CircuitModeControlEntityUplinkPacketType type) -> const char* { + switch (type) { + case CircuitModeControlEntityUplinkPacketType::kUAlert: + return "U-ALERT"; + case CircuitModeControlEntityUplinkPacketType::kUReserved1: + return "U-Reserved1"; + case CircuitModeControlEntityUplinkPacketType::kUConnect: + return "U-CONNECT"; + case CircuitModeControlEntityUplinkPacketType::kUReserved3: + return "U-Reserved3"; + case CircuitModeControlEntityUplinkPacketType::kUDisconnect: + return "U-DISCONNECT"; + case CircuitModeControlEntityUplinkPacketType::kUInfo: + return "U-INFO"; + case CircuitModeControlEntityUplinkPacketType::kURelease: + return "U-RELEASE"; + case CircuitModeControlEntityUplinkPacketType::kUSetup: + return "U-SETUP"; + case CircuitModeControlEntityUplinkPacketType::kUStatus: + return "U-STATUS"; + case CircuitModeControlEntityUplinkPacketType::kUTxCeased: + return "U-TX CEASED"; + case CircuitModeControlEntityUplinkPacketType::kUTxDemand: + return "U-TX DEMAND"; + case CircuitModeControlEntityUplinkPacketType::kUReserved11: + return "U-Reserved11"; + case CircuitModeControlEntityUplinkPacketType::kUReserved12: + return "U-Reserved12"; + case CircuitModeControlEntityUplinkPacketType::kUReserved13: + return "U-Reserved13"; + case CircuitModeControlEntityUplinkPacketType::kUCallRestore: + return "U-CALL-RESTORE"; + case CircuitModeControlEntityUplinkPacketType::kUSdsData: + return "U-SDS-DATA"; + case CircuitModeControlEntityUplinkPacketType::kUFacility: + return "U-FACILITY"; + case CircuitModeControlEntityUplinkPacketType::kUReserved17: + return "U-Reserved17"; + case CircuitModeControlEntityUplinkPacketType::kUReserved18: + return "U-Reserved18"; + case CircuitModeControlEntityUplinkPacketType::kUReserved19: + return "U-Reserved19"; + case CircuitModeControlEntityUplinkPacketType::kUReserved20: + return "U-Reserved20"; + case CircuitModeControlEntityUplinkPacketType::kUReserved21: + return "U-Reserved21"; + case CircuitModeControlEntityUplinkPacketType::kUReserved22: + return "U-Reserved22"; + case CircuitModeControlEntityUplinkPacketType::kUReserved23: + return "U-Reserved23"; + case CircuitModeControlEntityUplinkPacketType::kUReserved24: + return "U-Reserved24"; + case CircuitModeControlEntityUplinkPacketType::kUReserved25: + return "U-Reserved25"; + case CircuitModeControlEntityUplinkPacketType::kUReserved26: + return "U-Reserved26"; + case CircuitModeControlEntityUplinkPacketType::kUReserved27: + return "U-Reserved27"; + case CircuitModeControlEntityUplinkPacketType::kUReserved28: + return "U-Reserved28"; + case CircuitModeControlEntityUplinkPacketType::kUReserved29: + return "U-Reserved29"; + case CircuitModeControlEntityUplinkPacketType::kUReserved30: + return "U-Reserved30"; + case CircuitModeControlEntityUplinkPacketType::kCmceFunctionNotSupported: + return "CMCE FUNCTION NOT SUPPORTED"; + } +} + +using CircuitModeControlEntityPacketType = + std::variant; + +constexpr auto to_string(CircuitModeControlEntityPacketType type) -> const char* { + return std::visit([](auto&& arg) { return to_string(arg); }, type); +} + +struct SdsData { + /// the area selection that is present in the uplink sds + std::optional area_selection_; + /// the from or to address of the sds cmce packet + Address address_; + /// the sds data that is included int he cmce packet + BitVector data_; + /// TODO: The "External subscriber number" and "DM-MS address" is not parsed! + BitVector unparsed_; + + public: + static auto from_d_sds_data(BitVector& data) -> SdsData { + SdsData sds; + auto calling_party_type_identifier = data.take<2>(); + + if (calling_party_type_identifier == 1 || calling_party_type_identifier == 2) { + sds.address_.set_ssi(data.take<24>()); + } + if (calling_party_type_identifier == 2) { + sds.address_.set_country_code(data.take<10>()); + sds.address_.set_network_code(data.take<14>()); + } + + auto short_data_type_identifier = data.take<2>(); + + unsigned length_identifier = 0; + switch (short_data_type_identifier) { + case 0b00: + length_identifier = 16; + break; + case 0b01: + length_identifier = 32; + break; + case 0b10: + length_identifier = 64; + break; + case 0b11: + length_identifier = data.take<11>(); + break; + } + sds.data_ = data.take_vector(length_identifier); + sds.unparsed_ = data.take_vector(data.bits_left()); + + return sds; + }; + + static auto from_u_sds_data(BitVector& data) -> SdsData { + SdsData sds; + sds.area_selection_ = data.take<4>(); + auto calling_party_type_identifier = data.take<2>(); + + if (calling_party_type_identifier == 0) { + sds.address_.set_sna(data.take<8>()); + } + if (calling_party_type_identifier == 1 || calling_party_type_identifier == 2) { + sds.address_.set_ssi(data.take<24>()); + } + if (calling_party_type_identifier == 2) { + sds.address_.set_country_code(data.take<10>()); + sds.address_.set_network_code(data.take<14>()); + } + + auto short_data_type_identifier = data.take<2>(); + unsigned length_identifier = 0; + switch (short_data_type_identifier) { + case 0b00: + length_identifier = 16; + break; + case 0b01: + length_identifier = 32; + break; + case 0b10: + length_identifier = 64; + break; + case 0b11: + length_identifier = data.take<11>(); + break; + } + sds.data_ = data.take_vector(length_identifier); + sds.unparsed_ = data.take_vector(data.bits_left()); + + return sds; + }; +}; + +inline auto operator<<(std::ostream& stream, const SdsData& sds) -> std::ostream& { + if (sds.area_selection_) { + stream << " Area Selction: " << std::bitset<4>(*sds.area_selection_) << std::endl; + } + stream << " SDS Address: " << sds.address_ << std::endl; + stream << " Data: " << sds.data_ << std::endl; + stream << " Unparsed: " << sds.unparsed_ << std::endl; + return stream; +}; + +struct CircuitModeControlEntityPacket : public MobileLinkEntityPacket { + CircuitModeControlEntityPacketType packet_type_; + + std::optional sds_data_; + + CircuitModeControlEntityPacket() = delete; + + explicit CircuitModeControlEntityPacket(const MobileLinkEntityPacket& packet) + : MobileLinkEntityPacket(packet) { + auto data = BitVector(sdu_); + + auto pdu_type = data.take<5>(); + if (is_downlink()) { + packet_type_ = CircuitModeControlEntityDownlinkPacketType(pdu_type); + } else { + packet_type_ = CircuitModeControlEntityUplinkPacketType(pdu_type); + } + + if (packet_type_ == CircuitModeControlEntityPacketType(CircuitModeControlEntityDownlinkPacketType::kDSdsData)) { + sds_data_ = SdsData::from_d_sds_data(data); + } + + if (packet_type_ == CircuitModeControlEntityPacketType(CircuitModeControlEntityUplinkPacketType::kUSdsData)) { + sds_data_ = SdsData::from_u_sds_data(data); + } + } +}; + +inline auto operator<<(std::ostream& stream, const CircuitModeControlEntityPacket& cmce) -> std::ostream& { + stream << " CMCE: " << to_string(cmce.packet_type_) << std::endl; + if (cmce.sds_data_) { + stream << *cmce.sds_data_; + } + return stream; +}; \ No newline at end of file diff --git a/include/l3/circuit_mode_control_entity_packet_builder.hpp b/include/l3/circuit_mode_control_entity_packet_builder.hpp new file mode 100644 index 0000000..7470151 --- /dev/null +++ b/include/l3/circuit_mode_control_entity_packet_builder.hpp @@ -0,0 +1,34 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l3/circuit_mode_control_entity_packet.hpp" +#include "l3/short_data_service.hpp" +#include + +class CircuitModeControlEntityPacketBuilder { + private: + ShortDataService sds_; + + public: + CircuitModeControlEntityPacketBuilder() = delete; + + explicit CircuitModeControlEntityPacketBuilder(const std::shared_ptr& prometheus_exporter) + : sds_(prometheus_exporter){}; + + [[nodiscard]] auto parse_mobile_link_entity(const MobileLinkEntityPacket& packet) + -> std::unique_ptr { + auto cmce_packet = CircuitModeControlEntityPacket(packet); + if (cmce_packet.sds_data_) { + return sds_.process(cmce_packet); + } + + return std::make_unique(cmce_packet); + }; +}; \ No newline at end of file diff --git a/include/l3/mobile_link_entity.hpp b/include/l3/mobile_link_entity.hpp index ad36026..07cc902 100644 --- a/include/l3/mobile_link_entity.hpp +++ b/include/l3/mobile_link_entity.hpp @@ -10,50 +10,34 @@ #pragma once #include "l2/logical_link_control_packet.hpp" -#include "l3/circuit_mode_control_entity.hpp" -#include "l3/mobile_management.hpp" +#include "l3/mobile_link_entity_packet_builder.hpp" #include "prometheus.h" -#include "reporter.hpp" -#include "utils/bit_vector.hpp" #include "utils/packet_counter_metrics.hpp" #include class MobileLinkEntity { public: MobileLinkEntity() = delete; - MobileLinkEntity(const std::shared_ptr& prometheus_exporter, Reporter&& reporter, - bool is_downlink) - : cmce_(CircuitModeControlEntity(prometheus_exporter, std::move(reporter), is_downlink)) - , mm_(MobileManagement(prometheus_exporter, is_downlink)) { - protocol_discriminator_description_ = {"Reserved0", - "MM protocol", - "CMCE protocol", - "Reserved3", - "SNDCP protocol", - "MLE protocol", - "TETRA management entity protocol", - "Reserved for testing"}; - - if (is_downlink) { - mle_pdu_description_ = { - "D-NEW-CELL", "D-PREPARE-FAIL", "D-NWRK-BROADCAST", "D-NWRK-BROADCAST EXTENSION", - "D-RESTORE-ACK", "D-RESTORE-FAIL", "D-CHANNEL RESPONSE", "Extended PDU"}; - mle_pdu_extension_description_ = {"D-NWRK-BROADCAST-DA", "D-NWRK-BROADCAST REMOVE", - "D-Reserved2", "D-Reserved3", - "D-Reserved4", "D-Reserved5", - "D-Reserved6", "D-Reserved7", - "D-Reserved8", "D-Reserved9", - "D-Reserved10", "D-Reserved11", - "D-Reserved12", "D-Reserved13", - "D-Reserved14", "D-Reserved15"}; - } else { - mle_pdu_description_ = {"U-PREPARE", "U-PREPARE-DA", "U-IRREGULAR CHANNEL ADVICE", "U-CHANNEL CLASS ADVICE", - "U-RESTORE", "Reserved", "U-CHANNEL REQUEST", "Extended PDU"}; - mle_pdu_extension_description_ = {"U-Reserved0", "U-Reserved1", "U-Reserved2", "U-Reserved3", - "U-Reserved4", "U-Reserved5", "U-Reserved6", "U-Reserved7", - "U-Reserved8", "U-Reserved9", "U-Reserved10", "U-Reserved11", - "U-Reserved12", "U-Reserved13", "U-Reserved14", "U-Reserved15"}; - } + MobileLinkEntity(const std::shared_ptr& prometheus_exporter) + : packet_builder_(prometheus_exporter) { + downlink_mle_pdu_description_ = { + "D-NEW-CELL", "D-PREPARE-FAIL", "D-NWRK-BROADCAST", "D-NWRK-BROADCAST EXTENSION", + "D-RESTORE-ACK", "D-RESTORE-FAIL", "D-CHANNEL RESPONSE", "Extended PDU"}; + downlink_mle_pdu_extension_description_ = {"D-NWRK-BROADCAST-DA", "D-NWRK-BROADCAST REMOVE", + "D-Reserved2", "D-Reserved3", + "D-Reserved4", "D-Reserved5", + "D-Reserved6", "D-Reserved7", + "D-Reserved8", "D-Reserved9", + "D-Reserved10", "D-Reserved11", + "D-Reserved12", "D-Reserved13", + "D-Reserved14", "D-Reserved15"}; + uplink_mle_pdu_description_ = { + "U-PREPARE", "U-PREPARE-DA", "U-IRREGULAR CHANNEL ADVICE", "U-CHANNEL CLASS ADVICE", + "U-RESTORE", "Reserved", "U-CHANNEL REQUEST", "Extended PDU"}; + uplink_mle_pdu_extension_description_ = {"U-Reserved0", "U-Reserved1", "U-Reserved2", "U-Reserved3", + "U-Reserved4", "U-Reserved5", "U-Reserved6", "U-Reserved7", + "U-Reserved8", "U-Reserved9", "U-Reserved10", "U-Reserved11", + "U-Reserved12", "U-Reserved13", "U-Reserved14", "U-Reserved15"}; if (prometheus_exporter) { metrics_ = std::make_unique(prometheus_exporter, "mobile_link_entity"); @@ -64,15 +48,15 @@ class MobileLinkEntity { auto process(const LogicalLinkControlPacket& packet) -> std::unique_ptr; private: - CircuitModeControlEntity cmce_; - MobileManagement mm_; - static const auto kMleProtocol = 5; static const auto kExtendedPdu = 7; - std::array protocol_discriminator_description_; - std::array mle_pdu_description_; - std::array mle_pdu_extension_description_; + std::array downlink_mle_pdu_description_; + std::array downlink_mle_pdu_extension_description_; + + std::array uplink_mle_pdu_description_; + std::array uplink_mle_pdu_extension_description_; + MobileLinkEntityPacketBuilder packet_builder_; std::unique_ptr metrics_; }; \ No newline at end of file diff --git a/include/l3/mobile_link_entity_packet.hpp b/include/l3/mobile_link_entity_packet.hpp new file mode 100644 index 0000000..04df3f8 --- /dev/null +++ b/include/l3/mobile_link_entity_packet.hpp @@ -0,0 +1,68 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l2/logical_link_control_packet.hpp" + +/// The protocol that is included in this MLE packet. +enum class MobileLinkEntityProtocolDiscriminator { + kReserved0, + kMmProtocol, + kCmceProtocol, + kReserved3, + kSndcpProtocol, + kMleProtocol, + kTetraManagementEntityProtocol, + kReservedForTesting, +}; + +constexpr auto to_string(MobileLinkEntityProtocolDiscriminator type) noexcept -> const char* { + switch (type) { + case MobileLinkEntityProtocolDiscriminator::kReserved0: + return "Reserved0"; + case MobileLinkEntityProtocolDiscriminator::kMmProtocol: + return "MM protocol"; + case MobileLinkEntityProtocolDiscriminator::kCmceProtocol: + return "CMCE protocol"; + case MobileLinkEntityProtocolDiscriminator::kReserved3: + return "Reserved3"; + case MobileLinkEntityProtocolDiscriminator::kSndcpProtocol: + return "SNDCP protocol"; + case MobileLinkEntityProtocolDiscriminator::kMleProtocol: + return "MLE protocol"; + case MobileLinkEntityProtocolDiscriminator::kTetraManagementEntityProtocol: + return "TETRA management entity protocol"; + case MobileLinkEntityProtocolDiscriminator::kReservedForTesting: + return "Reserved for testing"; + } +}; + +/// The packet that is parsed in the logical link control layer. Currently we only implement basic link. +struct MobileLinkEntityPacket : public LogicalLinkControlPacket { + MobileLinkEntityProtocolDiscriminator mle_protocol_; + BitVector sdu_; + + MobileLinkEntityPacket() = delete; + + explicit MobileLinkEntityPacket(const LogicalLinkControlPacket& packet) + : LogicalLinkControlPacket(packet) { + sdu_ = BitVector(tl_sdu_); + + auto discriminator = sdu_.take<3>(); + mle_protocol_ = MobileLinkEntityProtocolDiscriminator(discriminator); + + // TODO: add the special handling for the MLE protocol + }; +}; + +inline auto operator<<(std::ostream& stream, const MobileLinkEntityPacket& mle) -> std::ostream& { + stream << " MLE: " << to_string(mle.mle_protocol_) << std::endl; + stream << " SDU: " << mle.sdu_ << std::endl; + return stream; +} \ No newline at end of file diff --git a/include/l3/mobile_link_entity_packet_builder.hpp b/include/l3/mobile_link_entity_packet_builder.hpp new file mode 100644 index 0000000..24bdc9e --- /dev/null +++ b/include/l3/mobile_link_entity_packet_builder.hpp @@ -0,0 +1,54 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l2/logical_link_control_packet.hpp" +#include "l3/circuit_mode_control_entity.hpp" +#include "l3/mobile_link_entity_packet.hpp" +#include "l3/mobile_management.hpp" +#include +#include + +/// The packet that is parsed in the logical link control layer. Currently we only implement basic link. +class MobileLinkEntityPacketBuilder { + private: + CircuitModeControlEntity cmce_; + MobileManagement mm_; + + public: + MobileLinkEntityPacketBuilder() = delete; + + explicit MobileLinkEntityPacketBuilder(const std::shared_ptr& prometheus_exporter) + : cmce_(prometheus_exporter) + , mm_(prometheus_exporter){}; + + [[nodiscard]] auto parse_logical_link_control(const LogicalLinkControlPacket& packet) + -> std::unique_ptr { + auto mle_packet = MobileLinkEntityPacket(packet); + + auto pdu_type = packet.tm_sdu_->look<4>(0); + + // TODO: currently we only handle CMCE and MM + switch (mle_packet.mle_protocol_) { + case MobileLinkEntityProtocolDiscriminator::kMmProtocol: + return mm_.process(mle_packet); + case MobileLinkEntityProtocolDiscriminator::kCmceProtocol: + return cmce_.process(mle_packet); + + // Fall through for all other unimplemented packet types + case MobileLinkEntityProtocolDiscriminator::kReserved0: + case MobileLinkEntityProtocolDiscriminator::kReserved3: + case MobileLinkEntityProtocolDiscriminator::kSndcpProtocol: + case MobileLinkEntityProtocolDiscriminator::kMleProtocol: + case MobileLinkEntityProtocolDiscriminator::kTetraManagementEntityProtocol: + case MobileLinkEntityProtocolDiscriminator::kReservedForTesting: + return std::make_unique(mle_packet); + } + }; +}; \ No newline at end of file diff --git a/include/l3/mobile_management.hpp b/include/l3/mobile_management.hpp index 11b314d..ccfd66b 100644 --- a/include/l3/mobile_management.hpp +++ b/include/l3/mobile_management.hpp @@ -8,8 +8,8 @@ #pragma once -#include "utils/address.hpp" -#include "utils/bit_vector.hpp" +#include "l3/mobile_link_entity_packet.hpp" +#include "l3/mobile_management_packet.hpp" #include "utils/packet_counter_metrics.hpp" #include #include @@ -17,52 +17,50 @@ class MobileManagement { public: MobileManagement() = delete; - explicit MobileManagement(const std::shared_ptr& prometheus_exporter, bool is_downlink) { - if (is_downlink) { - mm_pdu_description_ = {"D-OTAR", - "D-AUTHENTICATION", - "D-CK CHANGE DEMAND", - "D-DISABLE", - "D-ENABLE", - "D-LOCATION UPDATE ACCEPT", - "D-LOCATION UPDATE COMMAND", - "D-LOCATION UPDATE REJECT", - "D-Reserved8", - "D-LOCATION UPDATE PROCEEDING", - "D-LOCATION UPDATE PROCEEDING", - "D-ATTACH/DETACH GROUP IDENTITY ACK", - "D-MM STATUS", - "D-Reserved13", - "D-Reserved14", - "D-MM PDU/FUNCTION NOT SUPPORTED"}; - } else { - mm_pdu_description_ = {"U-AUTHENTICATION", - "U-ITSI DETACH", - "U-LOCATION UPDATE DEMAND", - "U-MM STATUS", - "U-CK CHANGE RESULT", - "U-OTAR", - "U-INFORMATION PROVIDE", - "U-ATTACH/DETACH GROUP IDENTITY", - "U-ATTACH/DETACH GROUP IDENTITY ACK", - "U-TEI PROVIDE", - "U-Reserved10", - "U-DISABLE STATUS", - "U-Reserved12", - "U-Reserved13", - "U-Reserved14", - "U-MM PDU/FUNCTION NOT SUPPORTED"}; - } + explicit MobileManagement(const std::shared_ptr& prometheus_exporter) { + downlink_mm_pdu_description_ = {"D-OTAR", + "D-AUTHENTICATION", + "D-CK CHANGE DEMAND", + "D-DISABLE", + "D-ENABLE", + "D-LOCATION UPDATE ACCEPT", + "D-LOCATION UPDATE COMMAND", + "D-LOCATION UPDATE REJECT", + "D-Reserved8", + "D-LOCATION UPDATE PROCEEDING", + "D-LOCATION UPDATE PROCEEDING", + "D-ATTACH/DETACH GROUP IDENTITY ACK", + "D-MM STATUS", + "D-Reserved13", + "D-Reserved14", + "D-MM PDU/FUNCTION NOT SUPPORTED"}; + uplink_mm_pdu_description_ = {"U-AUTHENTICATION", + "U-ITSI DETACH", + "U-LOCATION UPDATE DEMAND", + "U-MM STATUS", + "U-CK CHANGE RESULT", + "U-OTAR", + "U-INFORMATION PROVIDE", + "U-ATTACH/DETACH GROUP IDENTITY", + "U-ATTACH/DETACH GROUP IDENTITY ACK", + "U-TEI PROVIDE", + "U-Reserved10", + "U-DISABLE STATUS", + "U-Reserved12", + "U-Reserved13", + "U-Reserved14", + "U-MM PDU/FUNCTION NOT SUPPORTED"}; if (prometheus_exporter) { metrics_ = std::make_unique(prometheus_exporter, "mobile_management"); } } ~MobileManagement() noexcept = default; - void process(Address address, BitVector& vec); + auto process(const MobileLinkEntityPacket& packet) -> std::unique_ptr; private: - std::array mm_pdu_description_; + std::array downlink_mm_pdu_description_; + std::array uplink_mm_pdu_description_; std::unique_ptr metrics_; }; diff --git a/include/l3/mobile_management_packet.hpp b/include/l3/mobile_management_packet.hpp new file mode 100644 index 0000000..d67f59e --- /dev/null +++ b/include/l3/mobile_management_packet.hpp @@ -0,0 +1,18 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l3/mobile_link_entity_packet.hpp" + +struct MobileManagementPacket : public MobileLinkEntityPacket { + MobileManagementPacket() = delete; + + explicit MobileManagementPacket(const MobileLinkEntityPacket& packet) + : MobileLinkEntityPacket(packet){}; +}; \ No newline at end of file diff --git a/include/l3/short_data_service.hpp b/include/l3/short_data_service.hpp index eb9aa89..992f9ea 100644 --- a/include/l3/short_data_service.hpp +++ b/include/l3/short_data_service.hpp @@ -8,9 +8,8 @@ #pragma once -#include "reporter.hpp" -#include "utils/address.hpp" -#include "utils/bit_vector.hpp" +#include "l3/circuit_mode_control_entity_packet.hpp" +#include "l3/short_data_service_packet.hpp" #include "utils/packet_counter_metrics.hpp" #include #include @@ -18,8 +17,7 @@ class ShortDataService { public: ShortDataService() = delete; - ShortDataService(const std::shared_ptr& prometheus_exporter, Reporter&& reporter) - : reporter_(reporter) { + ShortDataService(const std::shared_ptr& prometheus_exporter) { sds_pdu_description_[0] = "Reserved 0"; sds_pdu_description_[1] = "OTAK (Over The Air re-Keying for end to end encryption)"; sds_pdu_description_[2] = "Simple Text Messaging"; @@ -70,16 +68,9 @@ class ShortDataService { }; ~ShortDataService() noexcept = default; - void process(Address to_address, Address from_address, BitVector& vec); + auto process(const CircuitModeControlEntityPacket& packet) -> std::unique_ptr; private: - void process_simple_text_messaging(Address to_address, Address from_address, BitVector& vec); - void process_location_information_protocol(Address to_address, Address from_address, BitVector& vec); - void process_default(Address to_address, Address from_address, BitVector& vec); - - nlohmann::json message_; - Reporter reporter_; - std::array sds_pdu_description_; std::unique_ptr metrics_; diff --git a/include/l3/short_data_service_packet.hpp b/include/l3/short_data_service_packet.hpp new file mode 100644 index 0000000..12dbc71 --- /dev/null +++ b/include/l3/short_data_service_packet.hpp @@ -0,0 +1,169 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l3/circuit_mode_control_entity_packet.hpp" +#include "utils/bit_vector.hpp" +#include +#include + +static auto integer_to_double(uint32_t data, std::size_t bits, double multiplier) -> double { + if (data & (1 << (bits - 1))) { + data = ~data; + data += 1; + data &= (0xFFFFFFFF >> (32 - bits)); + + return -1 * multiplier * data / static_cast(1 << (bits - 1)); + } else { + return multiplier * data / static_cast(1 << (bits - 1)); + } +} + +static auto decode_longitude(uint64_t v) -> double { + assert(v < std::pow(2, 25)); + + return integer_to_double(static_cast(v), 25, 180.0); +} + +static auto decode_latitude(uint64_t v) -> double { + assert(v < std::pow(2, 24)); + + return integer_to_double(static_cast(v), 24, 90.0); +} + +static auto decode_position_error(uint64_t v) -> std::string { + assert(v < 8); + + const std::string position_error[] = {"< 2 m", "< 20 m", "< 200 m", "< 2 km", + "< 20 km", "<= 200 km", "> 200 km", "unknown"}; + + return position_error[v]; +} + +static auto decode_horizontal_velocity(uint64_t v) -> double { + assert(v < std::pow(2, 7)); + + if (v == 127) { + return -1.0; + } else { + const double C = 16.0; + const double x = 0.038; + const double A = 13.0; + const double B = 0.0; + + return C * std::pow((1 + x), (v - A)) + B; + } +} + +static auto decode_direction_of_travel(uint64_t v) -> std::string { + assert(v < std::pow(2, 4)); + + const std::string direction_of_travel[] = {"0 N", "22.5 NNE", "45 NE", "67.5 ENE", "90 E", "112.5 ESE", + "135 SE", "157.5 SSE", "180 S", "202.5 SSW", "225 SW", "247.5 WSW", + "270 W", "292.5 WNW", "315 NW", "337.5 NNW"}; + + return direction_of_travel[v]; +} + +struct ShortLocationReport { + unsigned _BitInt(2) time_elapsed_; + double longitude_; + double latitude_; + std::string position_error_; + double horizontal_velocity_; + std::string direction_of_travel_; + unsigned _BitInt(1) type_of_addition_data_; + unsigned _BitInt(8) additional_data_; + + ShortLocationReport() = delete; + explicit ShortLocationReport(BitVector& data) + : time_elapsed_(data.take<2>()) + , longitude_(decode_longitude(data.take<25>())) + , latitude_(decode_latitude(data.take<24>())) + , position_error_(decode_position_error(data.take<3>())) + , horizontal_velocity_(decode_horizontal_velocity(data.take<7>())) + , direction_of_travel_(decode_direction_of_travel(data.take<4>())) + , type_of_addition_data_(data.take<1>()) + , additional_data_(data.take<8>()){}; +}; + +inline auto operator<<(std::ostream& stream, const ShortLocationReport& slr) -> std::ostream& { + stream << " Short Location Report" << std::endl; + stream << " Time elapsed: " << std::bitset<2>(slr.time_elapsed_) << " Lon: " << slr.longitude_ + << " Lat: " << slr.latitude_ << " Position error: " << slr.position_error_ + << " horizontal_velocity: " << slr.horizontal_velocity_ + << " direction_of_travel: " << slr.direction_of_travel_ + << " type_of_addition_data: " << std::bitset<1>(slr.additional_data_) + << " additional_data: " << std::bitset<8>(slr.additional_data_) << std::endl; + return stream; +} + +struct LocationInformationProtocol { + unsigned _BitInt(2) pdu_type_; + std::optional short_location_report_; + + LocationInformationProtocol() = delete; + + explicit LocationInformationProtocol(BitVector& data) + : pdu_type_(data.take<2>()) { + if (pdu_type_ == 0b00) { + short_location_report_ = ShortLocationReport(data); + } + }; +}; + +inline auto operator<<(std::ostream& stream, const LocationInformationProtocol& lip) -> std::ostream& { + if (lip.short_location_report_) { + stream << *lip.short_location_report_ << std::endl; + } else if (lip.pdu_type_ == 0b01) { + stream << "Location protocol PDU with extension" << std::endl; + } else { + stream << "Reserved LIP" << std::endl; + } + return stream; +}; + +struct ShortDataServicePacket : public CircuitModeControlEntityPacket { + unsigned _BitInt(8) protocol_identifier_; + std::optional location_information_protocol_; + + static constexpr const std::size_t kLocationInformationProtocolIdentifier = 0b00001010; + + ShortDataServicePacket() = delete; + + explicit ShortDataServicePacket(const CircuitModeControlEntityPacket& packet) + : CircuitModeControlEntityPacket(packet) { + assert(sds_data_.has_value()); + auto data = BitVector(sds_data_->data_); + + protocol_identifier_ = data.take<8>(); + + if (protocol_identifier_ == kLocationInformationProtocolIdentifier) { + location_information_protocol_ = LocationInformationProtocol(data); + } + }; +}; + +inline auto operator<<(std::ostream& stream, const ShortDataServicePacket& sds) -> std::ostream& { + stream << "SDS " << std::bitset<8>(sds.protocol_identifier_) << std::endl; + stream << " L2 Address: " << sds.address_ << " SDS Address: " << sds.sds_data_->address_ << std::endl; + if (sds.location_information_protocol_) { + stream << *sds.location_information_protocol_; + } + stream << " Data: " << sds.sds_data_->data_ << std::endl; + stream << " decoded: "; + const auto len = sds.sds_data_->data_.bits_left(); + for (auto i = 8; i + 8 <= len; i += 8) { + auto bits = sds.sds_data_->data_.look<8>(i); + stream << " " << std::hex << std::setw(2) << std::setfill('0') << static_cast(bits); + } + stream << std::endl; + + return stream; +} \ No newline at end of file diff --git a/src/decoder.cpp b/src/decoder.cpp index b84093a..e9d8ce6 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -34,9 +34,7 @@ Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::op , iq_or_bit_stream_(iq_or_bit_stream) { auto is_uplink = uplink_scrambling_code_.has_value(); auto lower_mac = std::make_shared(prometheus_exporter, uplink_scrambling_code); - upper_mac_ = std::make_unique(lower_mac_work_queue_, upper_mac_termination_flag_, prometheus_exporter, - Reporter(send_port), - /*is_downlink=*/!is_uplink); + upper_mac_ = std::make_unique(lower_mac_work_queue_, upper_mac_termination_flag_, prometheus_exporter); bit_stream_decoder_ = std::make_shared(lower_mac_work_queue_, lower_mac, uplink_scrambling_code_.has_value()); iq_stream_decoder_ = diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 8db0ac9..8e58c6a 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -8,14 +8,17 @@ #include "l2/upper_mac.hpp" #include "l2/logical_link_control.hpp" +#include "l2/logical_link_control_packet.hpp" #include "l2/lower_mac.hpp" #include "l2/upper_mac_fragments.hpp" #include "l2/upper_mac_packet.hpp" +#include "l3/circuit_mode_control_entity_packet.hpp" +#include "l3/mobile_link_entity_packet.hpp" +#include "l3/short_data_service_packet.hpp" #include "streaming_ordered_output_thread_pool_executor.hpp" #include #include #include -#include #include #if defined(__linux__) @@ -23,11 +26,10 @@ #endif UpperMac::UpperMac(const std::shared_ptr>& input_queue, - std::atomic_bool& termination_flag, const std::shared_ptr& prometheus_exporter, - Reporter&& reporter, bool is_downlink) + std::atomic_bool& termination_flag, const std::shared_ptr& prometheus_exporter) : input_queue_(input_queue) , termination_flag_(termination_flag) - , logical_link_control_(prometheus_exporter, std::move(reporter), is_downlink) { + , logical_link_control_(prometheus_exporter) { if (prometheus_exporter) { metrics_ = std::make_unique(prometheus_exporter); fragmentation_metrics_continous_ = @@ -143,6 +145,29 @@ auto UpperMac::processPackets(UpperMacPackets&& packets) -> void { for (const auto& packet : c_plane_packets) { auto parsed_packet = logical_link_control_.process(packet); + + if (auto* llc = dynamic_cast(parsed_packet.get())) { + if (llc->basic_link_information_ && + (llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithoutFcs || + llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithFcs)) { + continue; + } + std::cout << *llc; + if (auto* mle = dynamic_cast(llc)) { + std::cout << *mle; + if (auto* cmce = dynamic_cast(llc)) { + std::cout << *cmce; + if (auto* sds = dynamic_cast(llc)) { + std::cout << *sds; + } + } + std::cout << std::endl; + } + } + + // if (!parsed_packet->is_null_pdu()) { + // std::cout << *parsed_packet << std::endl; + // } /// TODO: send this packet to borzoi } } \ No newline at end of file diff --git a/src/l2/upper_mac_packet_builder.cpp b/src/l2/upper_mac_packet_builder.cpp index bc647d7..58aba0d 100644 --- a/src/l2/upper_mac_packet_builder.cpp +++ b/src/l2/upper_mac_packet_builder.cpp @@ -160,7 +160,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type if (pdu_type == 0b0) { // process MAC-ACCESS - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacAccess}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacAccess); packet.encrypted_ = (data.take<1>() == 1U); packet.address_ = Address::from_mac_access(data); @@ -192,7 +192,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type { // process MAC-END-HU - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacEndHu}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacEndHu); auto length_indictaion_or_capacity_request = data.take<1>(); std::optional length_indication; @@ -218,7 +218,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type if (pdu_type == 0b00) { // MAC-DATA (uplink) // TMA-SAP - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacData}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacData); auto fill_bit_indication = data.take<1>(); @@ -265,8 +265,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type throw std::runtime_error("MAC-FRAG may not be sent on stealing channel."); } - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, - .type_ = MacPacketType::kMacFragmentUplink}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacFragmentUplink); auto fill_bit_indication = data.take<1>(); packet.tm_sdu_ = @@ -276,8 +275,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type } { - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, - .type_ = MacPacketType::kMacEndUplink}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacEndUplink); auto fill_bit_indication = data.take<1>(); @@ -315,7 +313,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type throw std::runtime_error("MAC-U-BLCK may only be sent on SCH/F."); } - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacUBlck}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacUBlck); auto fill_bit_indication = data.take<1>(); packet.encrypted_ = (data.take<1>() == 1U); @@ -335,7 +333,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type if (pdu_type == 0b00) { // MAC-RESOURCE (downlink) // TMA-SAP - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacResource}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacResource); auto fill_bit_indication = data.take<1>(); @@ -406,8 +404,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type throw std::runtime_error("MAC-FRAG may not be sent on stealing channel."); } - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, - .type_ = MacPacketType::kMacFragmentDownlink}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacFragmentDownlink); auto fill_bit_indication = data.take<1>(); @@ -418,8 +415,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type } { - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, - .type_ = MacPacketType::kMacEndDownlink}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacEndDownlink); auto fill_bit_indication = data.take<1>(); @@ -469,7 +465,7 @@ auto UpperMacPacketBuilder::parse_c_plane_signalling_packet(BurstType burst_type throw std::runtime_error("MAC-D-BLCK may only be sent on SCH/F."); } - UpperMacCPlaneSignallingPacket packet{.logical_channel_ = channel, .type_ = MacPacketType::kMacDBlck}; + UpperMacCPlaneSignallingPacket packet(burst_type, channel, MacPacketType::kMacDBlck); auto fill_bit_indication = data.take<1>(); auto encryption_mode = data.take<2>(); diff --git a/src/l3/circuit_mode_control_entity.cpp b/src/l3/circuit_mode_control_entity.cpp index 55c29e7..aed0404 100644 --- a/src/l3/circuit_mode_control_entity.cpp +++ b/src/l3/circuit_mode_control_entity.cpp @@ -1,107 +1,14 @@ #include -#include - #include +#include -void CircuitModeControlEntity::process(const Address address, BitVector& vec) { - auto pdu_type = vec.take<5>(); - - const auto& pdu_name = cmce_pdu_description_.at(pdu_type); - std::cout << "CMCE " << pdu_name << std::endl; +auto CircuitModeControlEntity::process(const MobileLinkEntityPacket& packet) + -> std::unique_ptr { + auto cmce_packet = packet_builder_.parse_mobile_link_entity(packet); if (metrics_) { - metrics_->increment(pdu_name); - } - - switch (pdu_type) { - case kSdsData: - if (is_downlink_) { - process_d_sds_data(address, vec); - } else { - process_u_sds_data(address, vec); - } - break; - default: - break; - } -} - -void CircuitModeControlEntity::process_d_sds_data(const Address to_address, BitVector& vec) { - auto calling_party_type_identifier = vec.take<2>(); - Address from_address; - - if (calling_party_type_identifier == 1 || calling_party_type_identifier == 2) { - from_address.set_ssi(vec.take<24>()); - } - if (calling_party_type_identifier == 2) { - from_address.set_country_code(vec.take<10>()); - from_address.set_network_code(vec.take<14>()); - } - - auto short_data_type_identifier = vec.take<2>(); - - std::cout << " Address: " << from_address << std::endl; - std::cout << " short_data_type_identifier: " << static_cast(short_data_type_identifier) << std::endl; - - unsigned length_identifier = 0; - switch (short_data_type_identifier) { - case 0b00: - length_identifier = 16; - break; - case 0b01: - length_identifier = 32; - break; - case 0b10: - length_identifier = 64; - break; - case 0b11: - length_identifier = vec.take<11>(); - break; - } - std::cout << " length_identifier: " << static_cast(length_identifier) << std::endl; - auto sds_data = vec.take_vector(length_identifier); - sds_.process(to_address, from_address, sds_data); - std::cout << " leftover: " << vec << std::endl; -} - -void CircuitModeControlEntity::process_u_sds_data(const Address from_address, BitVector& vec) { - auto area_selection = vec.take<4>(); - auto calling_party_type_identifier = vec.take<2>(); - Address to_address; - - if (calling_party_type_identifier == 0) { - to_address.set_sna(vec.take<8>()); + metrics_->increment(to_string(cmce_packet->packet_type_)); } - if (calling_party_type_identifier == 1 || calling_party_type_identifier == 2) { - to_address.set_ssi(vec.take<24>()); - } - if (calling_party_type_identifier == 2) { - to_address.set_country_code(vec.take<10>()); - to_address.set_network_code(vec.take<14>()); - } - - auto short_data_type_identifier = vec.take<2>(); - std::cout << " Address: " << to_address << std::endl; - std::cout << " short_data_type_identifier: " << static_cast(short_data_type_identifier) << std::endl; - - unsigned length_identifier = 0; - switch (short_data_type_identifier) { - case 0b00: - length_identifier = 16; - break; - case 0b01: - length_identifier = 32; - break; - case 0b10: - length_identifier = 64; - break; - case 0b11: - length_identifier = vec.take<11>(); - break; - } - std::cout << " length_identifier: " << static_cast(length_identifier) << std::endl; - auto sds_data = vec.take_vector(length_identifier); - sds_.process(to_address, from_address, sds_data); - std::cout << " leftover: " << vec << std::endl; + return cmce_packet; } \ No newline at end of file diff --git a/src/l3/mobile_link_entity.cpp b/src/l3/mobile_link_entity.cpp index f22c3b1..866ae1d 100644 --- a/src/l3/mobile_link_entity.cpp +++ b/src/l3/mobile_link_entity.cpp @@ -9,6 +9,7 @@ #include "l3/mobile_link_entity.hpp" #include "l2/logical_link_control_packet.hpp" +#include "l3/mobile_link_entity_packet.hpp" #include "utils/bit_vector.hpp" #include #include @@ -17,7 +18,7 @@ auto MobileLinkEntity::process(const LogicalLinkControlPacket& packet) -> std::u auto data = BitVector(packet.tl_sdu_); auto pdu_type = data.take<3>(); - const auto& pdu_name = protocol_discriminator_description_.at(pdu_type); + const auto* pdu_name = to_string(MobileLinkEntityProtocolDiscriminator(pdu_type)); if (metrics_ && pdu_type != kMleProtocol) { metrics_->increment(pdu_name); @@ -25,7 +26,8 @@ auto MobileLinkEntity::process(const LogicalLinkControlPacket& packet) -> std::u if (pdu_type == kMleProtocol) { auto pdu_type = data.take<3>(); - const auto& pdu_name = mle_pdu_description_.at(pdu_type); + const auto& pdu_name = + (packet.is_downlink() ? downlink_mle_pdu_description_ : uplink_mle_pdu_description_).at(pdu_type); if (metrics_ && pdu_type != kExtendedPdu) { metrics_->increment(pdu_name); @@ -33,7 +35,9 @@ auto MobileLinkEntity::process(const LogicalLinkControlPacket& packet) -> std::u if (pdu_type == kExtendedPdu) { auto pdu_type = data.take<4>(); - const auto& pdu_name = mle_pdu_extension_description_.at(pdu_type); + const auto& pdu_name = + (packet.is_downlink() ? downlink_mle_pdu_extension_description_ : uplink_mle_pdu_extension_description_) + .at(pdu_type); if (metrics_) { metrics_->increment(pdu_name); @@ -41,17 +45,5 @@ auto MobileLinkEntity::process(const LogicalLinkControlPacket& packet) -> std::u } } - switch (pdu_type) { - case 0b001: - mm_.process(packet.address_, data); - break; - case 0b010: - cmce_.process(packet.address_, data); - break; - default: - return std::make_unique(packet); - } - - // TODO: return the specific parsed packet which is currently not handled yet - return std::make_unique(packet); + return packet_builder_.parse_logical_link_control(packet); } \ No newline at end of file diff --git a/src/l3/mobile_management.cpp b/src/l3/mobile_management.cpp index 7131119..ac24174 100644 --- a/src/l3/mobile_management.cpp +++ b/src/l3/mobile_management.cpp @@ -7,21 +7,16 @@ */ #include "l3/mobile_management.hpp" -#include -#include - -void MobileManagement::process(const Address /*address*/, BitVector& vec) { - - auto pdu_type = vec.take<4>(); - const auto& pdu_name = mm_pdu_description_.at(pdu_type); +#include "l3/mobile_management_packet.hpp" +#include +auto MobileManagement::process(const MobileLinkEntityPacket& packet) -> std::unique_ptr { if (metrics_) { + auto pdu_type = packet.sdu_.look<4>(0); + const auto& pdu_name = + (packet.is_downlink() ? downlink_mm_pdu_description_ : uplink_mm_pdu_description_).at(pdu_type); metrics_->increment(pdu_name); } - std::cout << "MM " << pdu_name << std::endl; - switch (pdu_type) { - default: - break; - } -} + return std::make_unique(packet); +} \ No newline at end of file diff --git a/src/l3/short_data_service.cpp b/src/l3/short_data_service.cpp index 76f82d2..842dfbe 100644 --- a/src/l3/short_data_service.cpp +++ b/src/l3/short_data_service.cpp @@ -7,171 +7,18 @@ */ #include "l3/short_data_service.hpp" -#include +#include "l3/short_data_service_packet.hpp" #include #include -#include -#include -void ShortDataService::process(const Address to_address, const Address from_address, BitVector& vec) { - auto protocol_identifier = vec.take<8>(); +auto ShortDataService::process(const CircuitModeControlEntityPacket& packet) + -> std::unique_ptr { + auto protocol_identifier = packet.sds_data_->data_.look<8>(0); const auto& protocol_identifier_name = sds_pdu_description_.at(protocol_identifier); if (metrics_) { metrics_->increment(protocol_identifier_name); } - message_ = {}; - - message_["type"] = "SDS"; - message_["to"] = to_address; - message_["from"] = from_address; - - message_["protocol_identifier"] = static_cast(protocol_identifier); - - std::cout << "SDS " << std::bitset<8>(protocol_identifier) << std::endl; - std::cout << " From: " << from_address << "To: " << to_address << std::endl; - - { - auto vec_copy = BitVector(vec); - - message_["data"] = nlohmann::json::array(); - - for (uint64_t bits; vec_copy.bits_left() >= 8;) { - bits = vec_copy.take<8>(); - message_["data"].push_back(bits); - } - - message_["bits_in_last_byte"] = vec_copy.bits_left(); - message_["data"].push_back(vec_copy.take_all()); - } - - switch (protocol_identifier) { - case 0b00000010: - process_simple_text_messaging(to_address, from_address, vec); - break; - case 0b00001010: - process_location_information_protocol(to_address, from_address, vec); - break; - default: - process_default(to_address, from_address, vec); - break; - } - - reporter_.emit_report(message_); -} - -void ShortDataService::process_default(const Address to_address, const Address from_address, BitVector& vec) { - - std::stringstream stream; - for (uint64_t bits; vec.bits_left() >= 8;) { - bits = vec.take<8>(); - stream << " " << std::hex << std::setw(2) << std::setfill('0') << bits; - } - - std::cout << " decoded: " << stream.str() << std::endl; - std::cout << " " << vec << std::endl; -} - -void ShortDataService::process_simple_text_messaging(const Address to_address, const Address from_address, - BitVector& vec) { - auto reserved = vec.take<1>(); - auto text_coding_scheme = vec.take<7>(); - - std::stringstream stream; - for (uint64_t bits; vec.bits_left() >= 8;) { - bits = vec.take<8>(); - stream << " " << std::hex << std::setw(2) << std::setfill('0') << bits; - } - - std::cout << " text_coding_scheme: 0b" << std::bitset<7>(text_coding_scheme) << std::endl; - std::cout << " decoded: " << stream.str() << std::endl; - std::cout << " " << vec << std::endl; -} - -static auto integer_to_double(uint32_t data, std::size_t bits, double multiplier) -> double { - if (data & (1 << (bits - 1))) { - data = ~data; - data += 1; - data &= (0xFFFFFFFF >> (32 - bits)); - - return -1 * multiplier * data / static_cast(1 << (bits - 1)); - } else { - return multiplier * data / static_cast(1 << (bits - 1)); - } -} - -static auto decode_longitude(uint64_t v) -> double { - assert(v < std::pow(2, 25)); - - return integer_to_double(static_cast(v), 25, 180.0); -} - -static auto decode_latitude(uint64_t v) -> double { - assert(v < std::pow(2, 24)); - - return integer_to_double(static_cast(v), 24, 90.0); -} - -static auto decode_position_error(uint64_t v) -> std::string { - assert(v < 8); - - const std::string position_error[] = {"< 2 m", "< 20 m", "< 200 m", "< 2 km", - "< 20 km", "<= 200 km", "> 200 km", "unknown"}; - - return position_error[v]; -} - -static auto decode_horizontal_velocity(uint64_t v) -> double { - assert(v < std::pow(2, 7)); - - if (v == 127) { - return -1.0; - } else { - const double C = 16.0; - const double x = 0.038; - const double A = 13.0; - const double B = 0.0; - - return C * std::pow((1 + x), (v - A)) + B; - } -} - -static auto decode_direction_of_travel(uint64_t v) -> std::string { - assert(v < std::pow(2, 4)); - - const std::string direction_of_travel[] = {"0 N", "22.5 NNE", "45 NE", "67.5 ENE", "90 E", "112.5 ESE", - "135 SE", "157.5 SSE", "180 S", "202.5 SSW", "225 SW", "247.5 WSW", - "270 W", "292.5 WNW", "315 NW", "337.5 NNW"}; - - return direction_of_travel[v]; -} - -void ShortDataService::process_location_information_protocol(const Address to_address, const Address from_address, - BitVector& vec) { - auto pdu_type = vec.take<2>(); - - if (pdu_type == 0b00) { - // Short location report - auto time_elapsed = vec.take<2>(); - auto longitude = decode_longitude(vec.take<25>()); - auto latitude = decode_latitude(vec.take<24>()); - auto position_error = decode_position_error(vec.take<3>()); - auto horizontal_velocity = decode_horizontal_velocity(vec.take<7>()); - auto direction_of_travel = decode_direction_of_travel(vec.take<4>()); - auto type_of_addition_data = vec.take<1>(); - auto additional_data = vec.take<8>(); - std::cout << " Short Location Report" << std::endl; - std::cout << " Time elapsed: " << std::bitset<2>(time_elapsed) << " Lon: " << longitude << " Lat: " << latitude - << " Position error: " << position_error << " horizontal_velocity: " << horizontal_velocity - << " direction_of_travel: " << direction_of_travel - << " type_of_addition_data: " << std::bitset<1>(additional_data) - << " additional_data: " << std::bitset<8>(additional_data) << std::endl; - } else if (pdu_type == 0b01) { - // Location protocol PDU with extension - auto pdu_type_extension = vec.take<4>(); - std::cout << " Location Information Protocol extension: 0b" << std::bitset<4>(pdu_type_extension) << std::endl; - } else { - // reserved - } -} + return std::make_unique(packet); +} \ No newline at end of file From 65bb61b73d0a41ba5271a310bcbdb8545998ea4d Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 1 Jul 2024 23:50:49 +0200 Subject: [PATCH 04/34] remove reporter --- CMakeLists.txt | 1 - include/l2/upper_mac.hpp | 1 - include/reporter.hpp | 30 ----------------------- src/reporter.cpp | 53 ---------------------------------------- 4 files changed, 85 deletions(-) delete mode 100644 include/reporter.hpp delete mode 100644 src/reporter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 63cdda5..1b87975 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,7 +7,6 @@ option(NIX_BUILD "Is CMake called by a nix build?" OFF) add_executable(tetra-decoder src/main.cpp - src/reporter.cpp src/decoder.cpp src/bit_stream_decoder.cpp src/iq_stream_decoder.cpp diff --git a/include/l2/upper_mac.hpp b/include/l2/upper_mac.hpp index 1a8e4a6..4d36eeb 100644 --- a/include/l2/upper_mac.hpp +++ b/include/l2/upper_mac.hpp @@ -16,7 +16,6 @@ #include "l2/upper_mac_metrics.hpp" #include "l2/upper_mac_packet_builder.hpp" #include "prometheus.h" -#include "reporter.hpp" #include "streaming_ordered_output_thread_pool_executor.hpp" #include #include diff --git a/include/reporter.hpp b/include/reporter.hpp deleted file mode 100644 index 863b1a8..0000000 --- a/include/reporter.hpp +++ /dev/null @@ -1,30 +0,0 @@ -/* - * Copyright (C) 2022-2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - * Tassilo Tanneberger - */ - -#pragma once - -#include -#include -#include -#include -#include -#include -#include - -class Reporter { - public: - explicit Reporter(unsigned send_port); - ~Reporter(); - - void emit_report(nlohmann::json& message); - - private: - int output_socket_fd_ = 0; - struct sockaddr_in destination_ {}; -}; diff --git a/src/reporter.cpp b/src/reporter.cpp deleted file mode 100644 index 1738025..0000000 --- a/src/reporter.cpp +++ /dev/null @@ -1,53 +0,0 @@ -/* - * Copyright (C) 2022-2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - * Tassilo Tanneberger - */ - -#include "reporter.hpp" -#include -#include -#include -#include - -Reporter::Reporter(unsigned send_port) { - // output file descriptor for the udp socket - // there goes our nice json - std::memset(&destination_, 0, sizeof(struct sockaddr_in)); - destination_.sin_family = AF_INET; - destination_.sin_port = htons(send_port); - destination_.sin_addr.s_addr = inet_addr("127.0.0.1"); - - output_socket_fd_ = socket(AF_INET, SOCK_DGRAM, 0); - - if (output_socket_fd_ < 0) { - throw std::runtime_error("Couldn't create output socket"); - } -} - -Reporter::~Reporter() { close(output_socket_fd_); } - -inline static auto get_time() -> std::string { - auto t = std::time(nullptr); - auto tm = *std::localtime(&t); - std::stringstream ss; - ss << std::put_time(&tm, "%F_%T%z"); - return ss.str(); -} - -void Reporter::emit_report(nlohmann::json& message) { - message["time"] = get_time(); - - std::string message_str = message.dump() + "\n"; - - int n_bytes = sendto(output_socket_fd_, message_str.c_str(), message_str.length(), 0, - reinterpret_cast(&(this->destination_)), sizeof(destination_)); - - if (n_bytes < 0) { - fmt::print(fg(fmt::color::crimson) | fmt::emphasis::bold, "[ERROR] could not send data over UDP socket\n"); - // TODO: handle error - } -} From 805cbb1958b87f8a61062f1b9a6c0dc281e14e7b Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 01:16:59 +0200 Subject: [PATCH 05/34] refactor packet parsing and implement it for llc --- CMakeLists.txt | 1 - include/l2/logical_link_control.hpp | 47 ++++++++++------ include/l2/logical_link_control_packet.hpp | 9 ++- .../logical_link_control_packet_builder.hpp | 46 --------------- include/l2/upper_mac.hpp | 2 +- .../l3/mobile_link_entity_packet_builder.hpp | 3 - include/utils/packet_parser.hpp | 56 +++++++++++++++++++ src/l2/logical_link_control.cpp | 44 --------------- src/l2/upper_mac.cpp | 2 +- 9 files changed, 96 insertions(+), 114 deletions(-) delete mode 100644 include/l2/logical_link_control_packet_builder.hpp create mode 100644 include/utils/packet_parser.hpp delete mode 100644 src/l2/logical_link_control.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1b87975..27008b4 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -13,7 +13,6 @@ add_executable(tetra-decoder src/prometheus.cpp src/l2/access_assignment_channel.cpp 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 diff --git a/include/l2/logical_link_control.hpp b/include/l2/logical_link_control.hpp index 7d41932..5b55871 100644 --- a/include/l2/logical_link_control.hpp +++ b/include/l2/logical_link_control.hpp @@ -8,16 +8,15 @@ #pragma once -#include "l2/logical_link_control_packet_builder.hpp" -#include "l2/upper_mac_packet.hpp" -#include "utils/packet_counter_metrics.hpp" -#include +#include "l3/mobile_link_entity.hpp" +#include "utils/packet_parser.hpp" -class LogicalLinkControl { +class LogicalLinkControlParser : public PacketParser { public: - LogicalLinkControl() = delete; - LogicalLinkControl(const std::shared_ptr& prometheus_exporter) - : packet_builder_(prometheus_exporter) { + LogicalLinkControlParser() = delete; + explicit LogicalLinkControlParser(const std::shared_ptr& prometheus_exporter) + : PacketParser(prometheus_exporter, "logical_link_control") + , mle_(prometheus_exporter) { llc_pdu_description_ = {"BL-ADATA without FCS", "BL-DATA without FCS", "BL-UDATA without FCS", @@ -53,22 +52,38 @@ class LogicalLinkControl { "ReservedLayer2SignallingPdu13", "ReservedLayer2SignallingPdu14", "ReservedLayer2SignallingPdu15"}; - if (prometheus_exporter) { - metrics_ = std::make_unique(prometheus_exporter, "logical_link_control"); + }; + + private: + auto packet_name(const LogicalLinkControlPacket& packet) -> std::string override { + auto pdu_type = packet.tm_sdu_->look<4>(0); + + if (pdu_type == kSupplementaryLlcPdu) { + auto pdu_type = packet.tm_sdu_->look<2>(4); + return supplementary_llc_pdu_description_.at(pdu_type); + } + + if (pdu_type == kLayer2SignallingPdu) { + auto pdu_type = packet.tm_sdu_->look<4>(4); + return layer_2_signalling_pdu_description_.at(pdu_type); } + + return llc_pdu_description_.at(pdu_type); + }; + + auto forward(const LogicalLinkControlPacket& packet) -> std::unique_ptr override { + if (packet.basic_link_information_ && packet.tl_sdu_.bits_left() > 0) { + return mle_.process(packet); + } + return std::make_unique(packet); }; - ~LogicalLinkControl() noexcept = default; - auto process(const UpperMacCPlaneSignallingPacket& packet) -> std::unique_ptr; + MobileLinkEntity mle_; - private: static const auto kSupplementaryLlcPdu = 13; static const auto kLayer2SignallingPdu = 14; std::array llc_pdu_description_; std::array supplementary_llc_pdu_description_; std::array layer_2_signalling_pdu_description_; - - LogicalLinkControlPacketBuilder packet_builder_; - std::unique_ptr metrics_; }; \ No newline at end of file diff --git a/include/l2/logical_link_control_packet.hpp b/include/l2/logical_link_control_packet.hpp index 0a55653..fc7e288 100644 --- a/include/l2/logical_link_control_packet.hpp +++ b/include/l2/logical_link_control_packet.hpp @@ -124,8 +124,13 @@ struct LogicalLinkControlPacket : public UpperMacCPlaneSignallingPacket { explicit LogicalLinkControlPacket(const UpperMacCPlaneSignallingPacket& packet) : UpperMacCPlaneSignallingPacket(packet) { auto data = BitVector(*tm_sdu_); - basic_link_information_ = BasicLinkInformation(data); - tl_sdu_ = data; + auto pdu_type = data.look<4>(0); + + /// We only implemented packet parsing for Basic Link PDUs at this point in time + if (pdu_type <= 0b1000) { + basic_link_information_ = BasicLinkInformation(data); + tl_sdu_ = data; + } } }; diff --git a/include/l2/logical_link_control_packet_builder.hpp b/include/l2/logical_link_control_packet_builder.hpp deleted file mode 100644 index f30818d..0000000 --- a/include/l2/logical_link_control_packet_builder.hpp +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - */ - -#pragma once - -#include "l2/logical_link_control_packet.hpp" -#include "l2/upper_mac_packet.hpp" -#include "l3/mobile_link_entity.hpp" -#include -#include - -/// The packet that is parsed in the logical link control layer. Currently we only implement basic link. -class LogicalLinkControlPacketBuilder { - private: - MobileLinkEntity mle_; - - public: - LogicalLinkControlPacketBuilder() = delete; - - explicit LogicalLinkControlPacketBuilder(const std::shared_ptr& prometheus_exporter) - : mle_(prometheus_exporter){}; - - [[nodiscard]] auto parse_c_plane_signalling(const UpperMacCPlaneSignallingPacket& packet) - -> std::unique_ptr { - auto pdu_type = packet.tm_sdu_->look<4>(0); - - /// We only implemented packet parsing for Basic Link PDUs at this point in time - if (pdu_type <= 0b1000) { - auto llc_packet = LogicalLinkControlPacket(packet); - - /// Contains optional TL-SDU pass to MLE for further parsing. - if (llc_packet.tl_sdu_.bits_left() > 0) { - return mle_.process(llc_packet); - } - - return std::make_unique(llc_packet); - } - - return std::make_unique(packet); - }; -}; \ No newline at end of file diff --git a/include/l2/upper_mac.hpp b/include/l2/upper_mac.hpp index 4d36eeb..f6a9d7a 100644 --- a/include/l2/upper_mac.hpp +++ b/include/l2/upper_mac.hpp @@ -59,7 +59,7 @@ class UpperMac { std::shared_ptr fragmentation_metrics_continous_; std::shared_ptr fragmentation_metrics_stealing_channel_; - LogicalLinkControl logical_link_control_; + LogicalLinkControlParser logical_link_control_; std::unique_ptr fragmentation_; diff --git a/include/l3/mobile_link_entity_packet_builder.hpp b/include/l3/mobile_link_entity_packet_builder.hpp index 24bdc9e..22d3a80 100644 --- a/include/l3/mobile_link_entity_packet_builder.hpp +++ b/include/l3/mobile_link_entity_packet_builder.hpp @@ -13,7 +13,6 @@ #include "l3/mobile_link_entity_packet.hpp" #include "l3/mobile_management.hpp" #include -#include /// The packet that is parsed in the logical link control layer. Currently we only implement basic link. class MobileLinkEntityPacketBuilder { @@ -32,8 +31,6 @@ class MobileLinkEntityPacketBuilder { -> std::unique_ptr { auto mle_packet = MobileLinkEntityPacket(packet); - auto pdu_type = packet.tm_sdu_->look<4>(0); - // TODO: currently we only handle CMCE and MM switch (mle_packet.mle_protocol_) { case MobileLinkEntityProtocolDiscriminator::kMmProtocol: diff --git a/include/utils/packet_parser.hpp b/include/utils/packet_parser.hpp new file mode 100644 index 0000000..923940a --- /dev/null +++ b/include/utils/packet_parser.hpp @@ -0,0 +1,56 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "prometheus.h" +#include "utils/packet_counter_metrics.hpp" +#include +#include + +/// This is the template that is used to keep an consistent interface in the protocol parsing. +/// 1. The input of type Input is passed into the constructor of type Output. +/// 2. The metrics are incremented in the increment_metrics function. +/// 3. The packet is forwared to the next parsing stage or returned if this is not necessary. +template class PacketParser { + private: + /// The metrics for this parser + std::unique_ptr metrics_; + + public: + PacketParser() = delete; + virtual ~PacketParser() = default; + + explicit PacketParser(const std::shared_ptr& prometheus_exporter, + const std::string& packet_parser_name) { + if (prometheus_exporter) { + metrics_ = std::make_unique(prometheus_exporter, packet_parser_name); + } + }; + + /// Parse the input packet, increment the metrics and return the parsed packet passed through all the layers defined + /// in the forward function. + virtual auto parse(const Input& input) -> std::unique_ptr { + /// Parse this layer + auto packet = Output(input); + /// Increment the metrics + if (metrics_) { + metrics_->increment(packet_name(packet)); + } + /// Forward it to further parsing steps + return forward(packet); + }; + + protected: + /// This function needs to be implemented for each parsing layer. It should return the correct packet name. + virtual auto packet_name(const Output&) -> std::string = 0; + + /// This function take the currently parsed packet and should forward it to the next parsing stage or return a + /// unique pointer to it. + virtual auto forward(const Output&) -> std::unique_ptr = 0; +}; \ No newline at end of file diff --git a/src/l2/logical_link_control.cpp b/src/l2/logical_link_control.cpp deleted file mode 100644 index 15b19c0..0000000 --- a/src/l2/logical_link_control.cpp +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright (C) 2022-2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - */ - -#include "l2/logical_link_control.hpp" -#include "l2/logical_link_control_packet_builder.hpp" -#include "l2/upper_mac_packet.hpp" -#include -#include - -auto LogicalLinkControl::process(const UpperMacCPlaneSignallingPacket& packet) - -> std::unique_ptr { - auto pdu_type = packet.tm_sdu_->look<4>(0); - const auto& pdu_name = llc_pdu_description_.at(pdu_type); - - // Skip incrementing the metrics for Supplementary LLC PDU and Layer 2 signalling PDU - if (metrics_ && (pdu_type != kSupplementaryLlcPdu) && (pdu_type != kLayer2SignallingPdu)) { - metrics_->increment(pdu_name); - } - - if (pdu_type == kSupplementaryLlcPdu) { - auto pdu_type = packet.tm_sdu_->look<2>(4); - const auto& pdu_name = supplementary_llc_pdu_description_.at(pdu_type); - - if (metrics_) { - metrics_->increment(pdu_name); - } - } - - if (pdu_type == kLayer2SignallingPdu) { - auto pdu_type = packet.tm_sdu_->look<4>(4); - const auto& pdu_name = layer_2_signalling_pdu_description_.at(pdu_type); - - if (metrics_) { - metrics_->increment(pdu_name); - } - } - - return packet_builder_.parse_c_plane_signalling(packet); -} \ No newline at end of file diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 8e58c6a..526e3d0 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -144,7 +144,7 @@ auto UpperMac::processPackets(UpperMacPackets&& packets) -> void { } for (const auto& packet : c_plane_packets) { - auto parsed_packet = logical_link_control_.process(packet); + auto parsed_packet = logical_link_control_.parse(packet); if (auto* llc = dynamic_cast(parsed_packet.get())) { if (llc->basic_link_information_ && From 828902ed28e6e9a3698a687b0fbefbac7d35a666 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 01:32:37 +0200 Subject: [PATCH 06/34] refactor mle parsing --- CMakeLists.txt | 1 - include/l2/logical_link_control.hpp | 4 +- include/l3/mobile_link_entity.hpp | 64 ++++++++++++++----- .../l3/mobile_link_entity_packet_builder.hpp | 51 --------------- src/l3/mobile_link_entity.cpp | 49 -------------- 5 files changed, 49 insertions(+), 120 deletions(-) delete mode 100644 include/l3/mobile_link_entity_packet_builder.hpp delete mode 100644 src/l3/mobile_link_entity.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 27008b4..53df63a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,6 @@ add_executable(tetra-decoder src/l2/upper_mac_packet.cpp src/l2/upper_mac_packet_builder.cpp src/l2/upper_mac_packet_formatter.cpp - src/l3/mobile_link_entity.cpp src/l3/mobile_management.cpp src/l3/circuit_mode_control_entity.cpp src/l3/short_data_service.cpp diff --git a/include/l2/logical_link_control.hpp b/include/l2/logical_link_control.hpp index 5b55871..fe29481 100644 --- a/include/l2/logical_link_control.hpp +++ b/include/l2/logical_link_control.hpp @@ -73,12 +73,12 @@ class LogicalLinkControlParser : public PacketParser std::unique_ptr override { if (packet.basic_link_information_ && packet.tl_sdu_.bits_left() > 0) { - return mle_.process(packet); + return mle_.parse(packet); } return std::make_unique(packet); }; - MobileLinkEntity mle_; + MobileLinkEntityParser mle_; static const auto kSupplementaryLlcPdu = 13; static const auto kLayer2SignallingPdu = 14; diff --git a/include/l3/mobile_link_entity.hpp b/include/l3/mobile_link_entity.hpp index 07cc902..7ea62ac 100644 --- a/include/l3/mobile_link_entity.hpp +++ b/include/l3/mobile_link_entity.hpp @@ -10,16 +10,18 @@ #pragma once #include "l2/logical_link_control_packet.hpp" -#include "l3/mobile_link_entity_packet_builder.hpp" -#include "prometheus.h" -#include "utils/packet_counter_metrics.hpp" -#include +#include "l3/circuit_mode_control_entity.hpp" +#include "l3/mobile_link_entity_packet.hpp" +#include "l3/mobile_management.hpp" +#include "utils/packet_parser.hpp" -class MobileLinkEntity { +class MobileLinkEntityParser : public PacketParser { public: - MobileLinkEntity() = delete; - MobileLinkEntity(const std::shared_ptr& prometheus_exporter) - : packet_builder_(prometheus_exporter) { + MobileLinkEntityParser() = delete; + explicit MobileLinkEntityParser(const std::shared_ptr& prometheus_exporter) + : PacketParser(prometheus_exporter, "mobile_link_entity") + , cmce_(prometheus_exporter) + , mm_(prometheus_exporter) { downlink_mle_pdu_description_ = { "D-NEW-CELL", "D-PREPARE-FAIL", "D-NWRK-BROADCAST", "D-NWRK-BROADCAST EXTENSION", "D-RESTORE-ACK", "D-RESTORE-FAIL", "D-CHANNEL RESPONSE", "Extended PDU"}; @@ -38,17 +40,48 @@ class MobileLinkEntity { "U-Reserved4", "U-Reserved5", "U-Reserved6", "U-Reserved7", "U-Reserved8", "U-Reserved9", "U-Reserved10", "U-Reserved11", "U-Reserved12", "U-Reserved13", "U-Reserved14", "U-Reserved15"}; + }; + + private: + auto packet_name(const MobileLinkEntityPacket& packet) -> std::string override { + if (packet.mle_protocol_ == MobileLinkEntityProtocolDiscriminator::kMleProtocol) { + auto pdu_type = packet.sdu_.look<3>(0); - if (prometheus_exporter) { - metrics_ = std::make_unique(prometheus_exporter, "mobile_link_entity"); + if (pdu_type == kExtendedPdu) { + auto pdu_type = packet.sdu_.look<4>(3); + return (packet.is_downlink() ? downlink_mle_pdu_extension_description_ + : uplink_mle_pdu_extension_description_) + .at(pdu_type); + } + + return (packet.is_downlink() ? downlink_mle_pdu_description_ : uplink_mle_pdu_description_).at(pdu_type); } + + return to_string(packet.mle_protocol_); }; - ~MobileLinkEntity() noexcept = default; - auto process(const LogicalLinkControlPacket& packet) -> std::unique_ptr; + auto forward(const MobileLinkEntityPacket& packet) -> std::unique_ptr override { + // TODO: currently we only handle CMCE and MM + switch (packet.mle_protocol_) { + case MobileLinkEntityProtocolDiscriminator::kMmProtocol: + return mm_.process(packet); + case MobileLinkEntityProtocolDiscriminator::kCmceProtocol: + return cmce_.process(packet); + + // Fall through for all other unimplemented packet types + case MobileLinkEntityProtocolDiscriminator::kReserved0: + case MobileLinkEntityProtocolDiscriminator::kReserved3: + case MobileLinkEntityProtocolDiscriminator::kSndcpProtocol: + case MobileLinkEntityProtocolDiscriminator::kMleProtocol: + case MobileLinkEntityProtocolDiscriminator::kTetraManagementEntityProtocol: + case MobileLinkEntityProtocolDiscriminator::kReservedForTesting: + return std::make_unique(packet); + } + }; + + CircuitModeControlEntity cmce_; + MobileManagement mm_; - private: - static const auto kMleProtocol = 5; static const auto kExtendedPdu = 7; std::array downlink_mle_pdu_description_; @@ -56,7 +89,4 @@ class MobileLinkEntity { std::array uplink_mle_pdu_description_; std::array uplink_mle_pdu_extension_description_; - - MobileLinkEntityPacketBuilder packet_builder_; - std::unique_ptr metrics_; }; \ No newline at end of file diff --git a/include/l3/mobile_link_entity_packet_builder.hpp b/include/l3/mobile_link_entity_packet_builder.hpp deleted file mode 100644 index 22d3a80..0000000 --- a/include/l3/mobile_link_entity_packet_builder.hpp +++ /dev/null @@ -1,51 +0,0 @@ -/* - * Copyright (C) 2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - */ - -#pragma once - -#include "l2/logical_link_control_packet.hpp" -#include "l3/circuit_mode_control_entity.hpp" -#include "l3/mobile_link_entity_packet.hpp" -#include "l3/mobile_management.hpp" -#include - -/// The packet that is parsed in the logical link control layer. Currently we only implement basic link. -class MobileLinkEntityPacketBuilder { - private: - CircuitModeControlEntity cmce_; - MobileManagement mm_; - - public: - MobileLinkEntityPacketBuilder() = delete; - - explicit MobileLinkEntityPacketBuilder(const std::shared_ptr& prometheus_exporter) - : cmce_(prometheus_exporter) - , mm_(prometheus_exporter){}; - - [[nodiscard]] auto parse_logical_link_control(const LogicalLinkControlPacket& packet) - -> std::unique_ptr { - auto mle_packet = MobileLinkEntityPacket(packet); - - // TODO: currently we only handle CMCE and MM - switch (mle_packet.mle_protocol_) { - case MobileLinkEntityProtocolDiscriminator::kMmProtocol: - return mm_.process(mle_packet); - case MobileLinkEntityProtocolDiscriminator::kCmceProtocol: - return cmce_.process(mle_packet); - - // Fall through for all other unimplemented packet types - case MobileLinkEntityProtocolDiscriminator::kReserved0: - case MobileLinkEntityProtocolDiscriminator::kReserved3: - case MobileLinkEntityProtocolDiscriminator::kSndcpProtocol: - case MobileLinkEntityProtocolDiscriminator::kMleProtocol: - case MobileLinkEntityProtocolDiscriminator::kTetraManagementEntityProtocol: - case MobileLinkEntityProtocolDiscriminator::kReservedForTesting: - return std::make_unique(mle_packet); - } - }; -}; \ No newline at end of file diff --git a/src/l3/mobile_link_entity.cpp b/src/l3/mobile_link_entity.cpp deleted file mode 100644 index 866ae1d..0000000 --- a/src/l3/mobile_link_entity.cpp +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright (C) 2022-2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - * Tassilo Tanneberger - */ - -#include "l3/mobile_link_entity.hpp" -#include "l2/logical_link_control_packet.hpp" -#include "l3/mobile_link_entity_packet.hpp" -#include "utils/bit_vector.hpp" -#include -#include - -auto MobileLinkEntity::process(const LogicalLinkControlPacket& packet) -> std::unique_ptr { - auto data = BitVector(packet.tl_sdu_); - - auto pdu_type = data.take<3>(); - const auto* pdu_name = to_string(MobileLinkEntityProtocolDiscriminator(pdu_type)); - - if (metrics_ && pdu_type != kMleProtocol) { - metrics_->increment(pdu_name); - } - - if (pdu_type == kMleProtocol) { - auto pdu_type = data.take<3>(); - const auto& pdu_name = - (packet.is_downlink() ? downlink_mle_pdu_description_ : uplink_mle_pdu_description_).at(pdu_type); - - if (metrics_ && pdu_type != kExtendedPdu) { - metrics_->increment(pdu_name); - } - - if (pdu_type == kExtendedPdu) { - auto pdu_type = data.take<4>(); - const auto& pdu_name = - (packet.is_downlink() ? downlink_mle_pdu_extension_description_ : uplink_mle_pdu_extension_description_) - .at(pdu_type); - - if (metrics_) { - metrics_->increment(pdu_name); - } - } - } - - return packet_builder_.parse_logical_link_control(packet); -} \ No newline at end of file From 0569f65796b43f1bf6a83cc2ddfb337b7da06508 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 01:39:47 +0200 Subject: [PATCH 07/34] refactor mm parsing --- CMakeLists.txt | 1 - include/l3/mobile_link_entity.hpp | 5 ++--- include/l3/mobile_management.hpp | 31 +++++++++++++++---------------- src/l3/mobile_management.cpp | 22 ---------------------- 4 files changed, 17 insertions(+), 42 deletions(-) delete mode 100644 src/l3/mobile_management.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 53df63a..1ab174f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,6 @@ add_executable(tetra-decoder src/l2/upper_mac_packet.cpp src/l2/upper_mac_packet_builder.cpp src/l2/upper_mac_packet_formatter.cpp - src/l3/mobile_management.cpp src/l3/circuit_mode_control_entity.cpp src/l3/short_data_service.cpp src/utils/address.cpp diff --git a/include/l3/mobile_link_entity.hpp b/include/l3/mobile_link_entity.hpp index 7ea62ac..200b56a 100644 --- a/include/l3/mobile_link_entity.hpp +++ b/include/l3/mobile_link_entity.hpp @@ -9,7 +9,6 @@ #pragma once -#include "l2/logical_link_control_packet.hpp" #include "l3/circuit_mode_control_entity.hpp" #include "l3/mobile_link_entity_packet.hpp" #include "l3/mobile_management.hpp" @@ -64,7 +63,7 @@ class MobileLinkEntityParser : public PacketParser -#include +#include "utils/packet_parser.hpp" -class MobileManagement { +class MobileManagementParser : public PacketParser { public: - MobileManagement() = delete; - explicit MobileManagement(const std::shared_ptr& prometheus_exporter) { + MobileManagementParser() = delete; + explicit MobileManagementParser(const std::shared_ptr& prometheus_exporter) + : PacketParser(prometheus_exporter, "mobile_management") { downlink_mm_pdu_description_ = {"D-OTAR", "D-AUTHENTICATION", "D-CK CHANGE DEMAND", @@ -50,17 +48,18 @@ class MobileManagement { "U-Reserved13", "U-Reserved14", "U-MM PDU/FUNCTION NOT SUPPORTED"}; - if (prometheus_exporter) { - metrics_ = std::make_unique(prometheus_exporter, "mobile_management"); - } + }; + + private: + auto packet_name(const MobileManagementPacket& packet) -> std::string override { + auto pdu_type = packet.sdu_.look<4>(0); + return (packet.is_downlink() ? downlink_mm_pdu_description_ : uplink_mm_pdu_description_).at(pdu_type); } - ~MobileManagement() noexcept = default; - auto process(const MobileLinkEntityPacket& packet) -> std::unique_ptr; + auto forward(const MobileManagementPacket& packet) -> std::unique_ptr override { + return std::make_unique(packet); + }; - private: std::array downlink_mm_pdu_description_; std::array uplink_mm_pdu_description_; - - std::unique_ptr metrics_; -}; +}; \ No newline at end of file diff --git a/src/l3/mobile_management.cpp b/src/l3/mobile_management.cpp deleted file mode 100644 index ac24174..0000000 --- a/src/l3/mobile_management.cpp +++ /dev/null @@ -1,22 +0,0 @@ -/* - * Copyright (C) 2022-2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - */ - -#include "l3/mobile_management.hpp" -#include "l3/mobile_management_packet.hpp" -#include - -auto MobileManagement::process(const MobileLinkEntityPacket& packet) -> std::unique_ptr { - if (metrics_) { - auto pdu_type = packet.sdu_.look<4>(0); - const auto& pdu_name = - (packet.is_downlink() ? downlink_mm_pdu_description_ : uplink_mm_pdu_description_).at(pdu_type); - metrics_->increment(pdu_name); - } - - return std::make_unique(packet); -} \ No newline at end of file From e28228f744f502f3a1376d98210b93f41f61d2a5 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 01:46:53 +0200 Subject: [PATCH 08/34] refactor cmce parsing --- CMakeLists.txt | 1 - include/l3/circuit_mode_control_entity.hpp | 36 +++++++++++-------- ...uit_mode_control_entity_packet_builder.hpp | 34 ------------------ include/l3/mobile_link_entity.hpp | 4 +-- src/l3/circuit_mode_control_entity.cpp | 14 -------- 5 files changed, 23 insertions(+), 66 deletions(-) delete mode 100644 include/l3/circuit_mode_control_entity_packet_builder.hpp delete mode 100644 src/l3/circuit_mode_control_entity.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 1ab174f..fc51854 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,6 @@ add_executable(tetra-decoder src/l2/upper_mac_packet.cpp src/l2/upper_mac_packet_builder.cpp src/l2/upper_mac_packet_formatter.cpp - src/l3/circuit_mode_control_entity.cpp src/l3/short_data_service.cpp src/utils/address.cpp src/utils/bit_vector.cpp diff --git a/include/l3/circuit_mode_control_entity.hpp b/include/l3/circuit_mode_control_entity.hpp index 448a81b..20d8082 100644 --- a/include/l3/circuit_mode_control_entity.hpp +++ b/include/l3/circuit_mode_control_entity.hpp @@ -1,5 +1,5 @@ /* - * Copyright (C) 2022 Transit Live Mapping Solutions + * Copyright (C) 2022-2024 Transit Live Mapping Solutions * All rights reserved. * * Authors: @@ -9,24 +9,30 @@ #pragma once #include "l3/circuit_mode_control_entity_packet.hpp" -#include "l3/circuit_mode_control_entity_packet_builder.hpp" #include "l3/mobile_link_entity_packet.hpp" -#include "utils/packet_counter_metrics.hpp" +#include "l3/short_data_service.hpp" +#include "utils/packet_parser.hpp" -class CircuitModeControlEntity { +class CircuitModeControlEntityParser : public PacketParser { public: - CircuitModeControlEntity() = delete; - explicit CircuitModeControlEntity(const std::shared_ptr& prometheus_exporter) - : packet_builder_(prometheus_exporter) { - if (prometheus_exporter) { - metrics_ = std::make_unique(prometheus_exporter, "circuit_mode_control_entity"); - } + CircuitModeControlEntityParser() = delete; + explicit CircuitModeControlEntityParser(const std::shared_ptr& prometheus_exporter) + : PacketParser(prometheus_exporter, "circuit_mode_control_entity") + , sds_(prometheus_exporter){}; + + private: + auto packet_name(const CircuitModeControlEntityPacket& packet) -> std::string override { + return to_string(packet.packet_type_); }; - ~CircuitModeControlEntity() noexcept = default; - auto process(const MobileLinkEntityPacket& packet) -> std::unique_ptr; + auto forward(const CircuitModeControlEntityPacket& packet) + -> std::unique_ptr override { + if (packet.sds_data_) { + return sds_.process(packet); + } - private: - CircuitModeControlEntityPacketBuilder packet_builder_; - std::unique_ptr metrics_; + return std::make_unique(packet); + }; + + ShortDataService sds_; }; \ No newline at end of file diff --git a/include/l3/circuit_mode_control_entity_packet_builder.hpp b/include/l3/circuit_mode_control_entity_packet_builder.hpp deleted file mode 100644 index 7470151..0000000 --- a/include/l3/circuit_mode_control_entity_packet_builder.hpp +++ /dev/null @@ -1,34 +0,0 @@ -/* - * Copyright (C) 2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - */ - -#pragma once - -#include "l3/circuit_mode_control_entity_packet.hpp" -#include "l3/short_data_service.hpp" -#include - -class CircuitModeControlEntityPacketBuilder { - private: - ShortDataService sds_; - - public: - CircuitModeControlEntityPacketBuilder() = delete; - - explicit CircuitModeControlEntityPacketBuilder(const std::shared_ptr& prometheus_exporter) - : sds_(prometheus_exporter){}; - - [[nodiscard]] auto parse_mobile_link_entity(const MobileLinkEntityPacket& packet) - -> std::unique_ptr { - auto cmce_packet = CircuitModeControlEntityPacket(packet); - if (cmce_packet.sds_data_) { - return sds_.process(cmce_packet); - } - - return std::make_unique(cmce_packet); - }; -}; \ No newline at end of file diff --git a/include/l3/mobile_link_entity.hpp b/include/l3/mobile_link_entity.hpp index 200b56a..ba8fdcb 100644 --- a/include/l3/mobile_link_entity.hpp +++ b/include/l3/mobile_link_entity.hpp @@ -65,7 +65,7 @@ class MobileLinkEntityParser : public PacketParser -#include -#include - -auto CircuitModeControlEntity::process(const MobileLinkEntityPacket& packet) - -> std::unique_ptr { - auto cmce_packet = packet_builder_.parse_mobile_link_entity(packet); - - if (metrics_) { - metrics_->increment(to_string(cmce_packet->packet_type_)); - } - - return cmce_packet; -} \ No newline at end of file From 0149af2d335c1cc41cd13b58644e28c3c5e808ae Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 01:51:35 +0200 Subject: [PATCH 09/34] refactor sds parsing --- CMakeLists.txt | 1 - include/l3/circuit_mode_control_entity.hpp | 4 ++-- include/l3/short_data_service.hpp | 28 ++++++++++------------ src/l3/short_data_service.cpp | 24 ------------------- 4 files changed, 15 insertions(+), 42 deletions(-) delete mode 100644 src/l3/short_data_service.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index fc51854..eb775e0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -20,7 +20,6 @@ add_executable(tetra-decoder src/l2/upper_mac_packet.cpp src/l2/upper_mac_packet_builder.cpp src/l2/upper_mac_packet_formatter.cpp - src/l3/short_data_service.cpp src/utils/address.cpp src/utils/bit_vector.cpp src/utils/viter_bi_codec.cpp) diff --git a/include/l3/circuit_mode_control_entity.hpp b/include/l3/circuit_mode_control_entity.hpp index 20d8082..ebd511a 100644 --- a/include/l3/circuit_mode_control_entity.hpp +++ b/include/l3/circuit_mode_control_entity.hpp @@ -28,11 +28,11 @@ class CircuitModeControlEntityParser : public PacketParser std::unique_ptr override { if (packet.sds_data_) { - return sds_.process(packet); + return sds_.parse(packet); } return std::make_unique(packet); }; - ShortDataService sds_; + ShortDataServiceParser sds_; }; \ No newline at end of file diff --git a/include/l3/short_data_service.hpp b/include/l3/short_data_service.hpp index 992f9ea..af6762c 100644 --- a/include/l3/short_data_service.hpp +++ b/include/l3/short_data_service.hpp @@ -10,14 +10,13 @@ #include "l3/circuit_mode_control_entity_packet.hpp" #include "l3/short_data_service_packet.hpp" -#include "utils/packet_counter_metrics.hpp" -#include -#include +#include "utils/packet_parser.hpp" -class ShortDataService { +class ShortDataServiceParser : public PacketParser { public: - ShortDataService() = delete; - ShortDataService(const std::shared_ptr& prometheus_exporter) { + ShortDataServiceParser() = delete; + explicit ShortDataServiceParser(const std::shared_ptr& prometheus_exporter) + : PacketParser(prometheus_exporter, "short_data_service") { sds_pdu_description_[0] = "Reserved 0"; sds_pdu_description_[1] = "OTAK (Over The Air re-Keying for end to end encryption)"; sds_pdu_description_[2] = "Simple Text Messaging"; @@ -61,17 +60,16 @@ class ShortDataService { sds_pdu_description_[i] = "Available for user application definition " + std::to_string(i); } sds_pdu_description_[255] = "Reserved for extension"; - - if (prometheus_exporter) { - metrics_ = std::make_unique(prometheus_exporter, "short_data_service"); - } }; - ~ShortDataService() noexcept = default; - - auto process(const CircuitModeControlEntityPacket& packet) -> std::unique_ptr; private: - std::array sds_pdu_description_; + auto packet_name(const ShortDataServicePacket& packet) -> std::string override { + return sds_pdu_description_.at(packet.protocol_identifier_); + } - std::unique_ptr metrics_; + auto forward(const ShortDataServicePacket& packet) -> std::unique_ptr override { + return std::make_unique(packet); + }; + + std::array sds_pdu_description_; }; \ No newline at end of file diff --git a/src/l3/short_data_service.cpp b/src/l3/short_data_service.cpp deleted file mode 100644 index 842dfbe..0000000 --- a/src/l3/short_data_service.cpp +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (C) 2022-2024 Transit Live Mapping Solutions - * All rights reserved. - * - * Authors: - * Marenz Schmidl - */ - -#include "l3/short_data_service.hpp" -#include "l3/short_data_service_packet.hpp" -#include -#include - -auto ShortDataService::process(const CircuitModeControlEntityPacket& packet) - -> std::unique_ptr { - auto protocol_identifier = packet.sds_data_->data_.look<8>(0); - const auto& protocol_identifier_name = sds_pdu_description_.at(protocol_identifier); - - if (metrics_) { - metrics_->increment(protocol_identifier_name); - } - - return std::make_unique(packet); -} \ No newline at end of file From 43d618aaa5ace5703050316ffff35969880aadd5 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 20:14:18 +0200 Subject: [PATCH 10/34] rename packet parser headers. remove unused includes. --- ...ink_control.hpp => logical_link_control_parser.hpp} | 3 +-- include/l2/upper_mac.hpp | 2 +- ...tity.hpp => circuit_mode_control_entity_parser.hpp} | 4 +--- ...e_link_entity.hpp => mobile_link_entity_parser.hpp} | 5 ++--- ...ile_management.hpp => mobile_management_parser.hpp} | 0 ..._data_service.hpp => short_data_service_parser.hpp} | 0 src/l2/upper_mac.cpp | 10 +--------- 7 files changed, 6 insertions(+), 18 deletions(-) rename include/l2/{logical_link_control.hpp => logical_link_control_parser.hpp} (98%) rename include/l3/{circuit_mode_control_entity.hpp => circuit_mode_control_entity_parser.hpp} (90%) rename include/l3/{mobile_link_entity.hpp => mobile_link_entity_parser.hpp} (97%) rename include/l3/{mobile_management.hpp => mobile_management_parser.hpp} (100%) rename include/l3/{short_data_service.hpp => short_data_service_parser.hpp} (100%) diff --git a/include/l2/logical_link_control.hpp b/include/l2/logical_link_control_parser.hpp similarity index 98% rename from include/l2/logical_link_control.hpp rename to include/l2/logical_link_control_parser.hpp index fe29481..3f45fdf 100644 --- a/include/l2/logical_link_control.hpp +++ b/include/l2/logical_link_control_parser.hpp @@ -8,8 +8,7 @@ #pragma once -#include "l3/mobile_link_entity.hpp" -#include "utils/packet_parser.hpp" +#include "l3/mobile_link_entity_parser.hpp" class LogicalLinkControlParser : public PacketParser { public: diff --git a/include/l2/upper_mac.hpp b/include/l2/upper_mac.hpp index f6a9d7a..bbc2cda 100644 --- a/include/l2/upper_mac.hpp +++ b/include/l2/upper_mac.hpp @@ -9,7 +9,7 @@ #pragma once -#include "l2/logical_link_control.hpp" +#include "l2/logical_link_control_parser.hpp" #include "l2/lower_mac.hpp" #include "l2/slot.hpp" #include "l2/upper_mac_fragments.hpp" diff --git a/include/l3/circuit_mode_control_entity.hpp b/include/l3/circuit_mode_control_entity_parser.hpp similarity index 90% rename from include/l3/circuit_mode_control_entity.hpp rename to include/l3/circuit_mode_control_entity_parser.hpp index ebd511a..c2fdfd7 100644 --- a/include/l3/circuit_mode_control_entity.hpp +++ b/include/l3/circuit_mode_control_entity_parser.hpp @@ -9,9 +9,7 @@ #pragma once #include "l3/circuit_mode_control_entity_packet.hpp" -#include "l3/mobile_link_entity_packet.hpp" -#include "l3/short_data_service.hpp" -#include "utils/packet_parser.hpp" +#include "l3/short_data_service_parser.hpp" class CircuitModeControlEntityParser : public PacketParser { public: diff --git a/include/l3/mobile_link_entity.hpp b/include/l3/mobile_link_entity_parser.hpp similarity index 97% rename from include/l3/mobile_link_entity.hpp rename to include/l3/mobile_link_entity_parser.hpp index ba8fdcb..9ef050b 100644 --- a/include/l3/mobile_link_entity.hpp +++ b/include/l3/mobile_link_entity_parser.hpp @@ -9,10 +9,9 @@ #pragma once -#include "l3/circuit_mode_control_entity.hpp" +#include "l3/circuit_mode_control_entity_parser.hpp" #include "l3/mobile_link_entity_packet.hpp" -#include "l3/mobile_management.hpp" -#include "utils/packet_parser.hpp" +#include "l3/mobile_management_parser.hpp" class MobileLinkEntityParser : public PacketParser { public: diff --git a/include/l3/mobile_management.hpp b/include/l3/mobile_management_parser.hpp similarity index 100% rename from include/l3/mobile_management.hpp rename to include/l3/mobile_management_parser.hpp diff --git a/include/l3/short_data_service.hpp b/include/l3/short_data_service_parser.hpp similarity index 100% rename from include/l3/short_data_service.hpp rename to include/l3/short_data_service_parser.hpp diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 526e3d0..9102183 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -7,19 +7,11 @@ */ #include "l2/upper_mac.hpp" -#include "l2/logical_link_control.hpp" -#include "l2/logical_link_control_packet.hpp" +#include "l2/logical_link_control_parser.hpp" #include "l2/lower_mac.hpp" #include "l2/upper_mac_fragments.hpp" -#include "l2/upper_mac_packet.hpp" -#include "l3/circuit_mode_control_entity_packet.hpp" -#include "l3/mobile_link_entity_packet.hpp" -#include "l3/short_data_service_packet.hpp" #include "streaming_ordered_output_thread_pool_executor.hpp" -#include -#include #include -#include #if defined(__linux__) #include From 3c2e38526554a6c00e0ee3ff6f856fd6cbfe43d2 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 20:57:20 +0200 Subject: [PATCH 11/34] move packet parser implementation from header to cpp files --- CMakeLists.txt | 40 +++--- include/l2/logical_link_control_packet.hpp | 79 +---------- .../l3/circuit_mode_control_entity_packet.hpp | 114 ++-------------- include/l3/mobile_link_entity_packet.hpp | 16 +-- include/l3/short_data_service_packet.hpp | 128 +----------------- src/l2/logical_link_control_formatter.cpp | 32 +++++ src/l2/logical_link_control_packet.cpp | 62 +++++++++ .../circuit_mode_control_entity_formatter.cpp | 27 ++++ src/l3/circuit_mode_control_entity_packet.cpp | 102 ++++++++++++++ src/l3/mobile_link_entity_formatter.cpp | 15 ++ src/l3/mobile_link_entity_packet.cpp | 19 +++ src/l3/short_data_service_formatter.cpp | 49 +++++++ src/l3/short_data_service_packet.cpp | 82 +++++++++++ 13 files changed, 432 insertions(+), 333 deletions(-) create mode 100644 src/l2/logical_link_control_formatter.cpp create mode 100644 src/l2/logical_link_control_packet.cpp create mode 100644 src/l3/circuit_mode_control_entity_formatter.cpp create mode 100644 src/l3/circuit_mode_control_entity_packet.cpp create mode 100644 src/l3/mobile_link_entity_formatter.cpp create mode 100644 src/l3/mobile_link_entity_packet.cpp create mode 100644 src/l3/short_data_service_formatter.cpp create mode 100644 src/l3/short_data_service_packet.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index eb775e0..819d403 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -7,22 +7,30 @@ option(NIX_BUILD "Is CMake called by a nix build?" OFF) add_executable(tetra-decoder src/main.cpp - src/decoder.cpp - src/bit_stream_decoder.cpp - src/iq_stream_decoder.cpp - src/prometheus.cpp - src/l2/access_assignment_channel.cpp - src/l2/broadcast_synchronization_channel.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_packet_formatter.cpp - src/utils/address.cpp - src/utils/bit_vector.cpp - src/utils/viter_bi_codec.cpp) + src/decoder.cpp + src/bit_stream_decoder.cpp + src/iq_stream_decoder.cpp + src/prometheus.cpp + src/l2/access_assignment_channel.cpp + src/l2/broadcast_synchronization_channel.cpp + src/l2/logical_link_control_formatter.cpp + src/l2/logical_link_control_packet.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_packet_formatter.cpp + src/l3/circuit_mode_control_entity_formatter.cpp + src/l3/circuit_mode_control_entity_packet.cpp + src/l3/mobile_link_entity_formatter.cpp + src/l3/mobile_link_entity_packet.cpp + src/l3/short_data_service_formatter.cpp + src/l3/short_data_service_packet.cpp + src/utils/address.cpp + src/utils/bit_vector.cpp + src/utils/viter_bi_codec.cpp) add_executable(tetra-puncturing src/examples/tetra_puncturing.cpp) diff --git a/include/l2/logical_link_control_packet.hpp b/include/l2/logical_link_control_packet.hpp index fc7e288..17833cb 100644 --- a/include/l2/logical_link_control_packet.hpp +++ b/include/l2/logical_link_control_packet.hpp @@ -10,9 +10,7 @@ #include "l2/upper_mac_packet.hpp" #include "utils/bit_vector.hpp" -#include #include -#include /// The type of the basic link packet. enum class BasicLinkType { @@ -56,63 +54,10 @@ struct BasicLinkInformation { BasicLinkInformation() = delete; /// construct a BasicLinkInformation from a BitVector - explicit BasicLinkInformation(BitVector& data) { - auto pdu_type = data.take<4>(); - - switch (pdu_type) { - case 0b0000: - n_r_ = data.take<1>(); - n_s_ = data.take<1>(); - break; - case 0b0001: - n_s_ = data.take<1>(); - break; - case 0b0010: - break; - case 0b0011: - n_r_ = data.take<1>(); - break; - case 0b0100: - n_r_ = data.take<1>(); - n_s_ = data.take<1>(); - break; - case 0b0101: - n_s_ = data.take<1>(); - break; - case 0b0110: - break; - case 0b0111: - n_r_ = data.take<1>(); - break; - default: - throw std::runtime_error( - "Did not expect something other than basic link in LogicalLinkControlPacket parser!"); - } - - basic_link_type_ = BasicLinkType(pdu_type); - - if (pdu_type >= 0b0100) { - auto fcs = data.take_last<32>(); - auto computed_fcs = data.compute_fcs(); - fcs_good_ = fcs == computed_fcs; - } - } + explicit BasicLinkInformation(BitVector& data); }; -inline auto operator<<(std::ostream& stream, const BasicLinkInformation& bli) -> std::ostream& { - stream << " Basic Link Information: " << to_string(bli.basic_link_type_); - if (bli.n_r_) { - stream << " N(R): " << std::bitset<1>(*bli.n_r_); - } - if (bli.n_s_) { - stream << " N(S): " << std::bitset<1>(*bli.n_s_); - } - if (bli.fcs_good_) { - stream << " FCS good: " << (*bli.fcs_good_ ? "true" : "false"); - } - stream << std::endl; - return stream; -} +auto operator<<(std::ostream& stream, const BasicLinkInformation& bli) -> std::ostream&; /// The packet that is parsed in the logical link control layer. Currently we only implement basic link. struct LogicalLinkControlPacket : public UpperMacCPlaneSignallingPacket { @@ -121,23 +66,7 @@ struct LogicalLinkControlPacket : public UpperMacCPlaneSignallingPacket { LogicalLinkControlPacket() = delete; - explicit LogicalLinkControlPacket(const UpperMacCPlaneSignallingPacket& packet) - : UpperMacCPlaneSignallingPacket(packet) { - auto data = BitVector(*tm_sdu_); - auto pdu_type = data.look<4>(0); - - /// We only implemented packet parsing for Basic Link PDUs at this point in time - if (pdu_type <= 0b1000) { - basic_link_information_ = BasicLinkInformation(data); - tl_sdu_ = data; - } - } + explicit LogicalLinkControlPacket(const UpperMacCPlaneSignallingPacket& packet); }; -inline auto operator<<(std::ostream& stream, const LogicalLinkControlPacket& llc) -> std::ostream& { - if (llc.basic_link_information_) { - stream << *llc.basic_link_information_; - } - stream << " TL-SDU: " << llc.tl_sdu_ << std::endl; - return stream; -} \ No newline at end of file +auto operator<<(std::ostream& stream, const LogicalLinkControlPacket& llc) -> std::ostream&; \ No newline at end of file diff --git a/include/l3/circuit_mode_control_entity_packet.hpp b/include/l3/circuit_mode_control_entity_packet.hpp index 9df3429..1d56b0a 100644 --- a/include/l3/circuit_mode_control_entity_packet.hpp +++ b/include/l3/circuit_mode_control_entity_packet.hpp @@ -239,90 +239,16 @@ struct SdsData { /// TODO: The "External subscriber number" and "DM-MS address" is not parsed! BitVector unparsed_; - public: - static auto from_d_sds_data(BitVector& data) -> SdsData { - SdsData sds; - auto calling_party_type_identifier = data.take<2>(); - - if (calling_party_type_identifier == 1 || calling_party_type_identifier == 2) { - sds.address_.set_ssi(data.take<24>()); - } - if (calling_party_type_identifier == 2) { - sds.address_.set_country_code(data.take<10>()); - sds.address_.set_network_code(data.take<14>()); - } - - auto short_data_type_identifier = data.take<2>(); - - unsigned length_identifier = 0; - switch (short_data_type_identifier) { - case 0b00: - length_identifier = 16; - break; - case 0b01: - length_identifier = 32; - break; - case 0b10: - length_identifier = 64; - break; - case 0b11: - length_identifier = data.take<11>(); - break; - } - sds.data_ = data.take_vector(length_identifier); - sds.unparsed_ = data.take_vector(data.bits_left()); + private: + SdsData() = default; - return sds; - }; - - static auto from_u_sds_data(BitVector& data) -> SdsData { - SdsData sds; - sds.area_selection_ = data.take<4>(); - auto calling_party_type_identifier = data.take<2>(); - - if (calling_party_type_identifier == 0) { - sds.address_.set_sna(data.take<8>()); - } - if (calling_party_type_identifier == 1 || calling_party_type_identifier == 2) { - sds.address_.set_ssi(data.take<24>()); - } - if (calling_party_type_identifier == 2) { - sds.address_.set_country_code(data.take<10>()); - sds.address_.set_network_code(data.take<14>()); - } - - auto short_data_type_identifier = data.take<2>(); - unsigned length_identifier = 0; - switch (short_data_type_identifier) { - case 0b00: - length_identifier = 16; - break; - case 0b01: - length_identifier = 32; - break; - case 0b10: - length_identifier = 64; - break; - case 0b11: - length_identifier = data.take<11>(); - break; - } - sds.data_ = data.take_vector(length_identifier); - sds.unparsed_ = data.take_vector(data.bits_left()); + public: + static auto from_d_sds_data(BitVector& data) -> SdsData; - return sds; - }; + static auto from_u_sds_data(BitVector& data) -> SdsData; }; -inline auto operator<<(std::ostream& stream, const SdsData& sds) -> std::ostream& { - if (sds.area_selection_) { - stream << " Area Selction: " << std::bitset<4>(*sds.area_selection_) << std::endl; - } - stream << " SDS Address: " << sds.address_ << std::endl; - stream << " Data: " << sds.data_ << std::endl; - stream << " Unparsed: " << sds.unparsed_ << std::endl; - return stream; -}; +auto operator<<(std::ostream& stream, const SdsData& sds) -> std::ostream&; struct CircuitModeControlEntityPacket : public MobileLinkEntityPacket { CircuitModeControlEntityPacketType packet_type_; @@ -331,31 +257,7 @@ struct CircuitModeControlEntityPacket : public MobileLinkEntityPacket { CircuitModeControlEntityPacket() = delete; - explicit CircuitModeControlEntityPacket(const MobileLinkEntityPacket& packet) - : MobileLinkEntityPacket(packet) { - auto data = BitVector(sdu_); - - auto pdu_type = data.take<5>(); - if (is_downlink()) { - packet_type_ = CircuitModeControlEntityDownlinkPacketType(pdu_type); - } else { - packet_type_ = CircuitModeControlEntityUplinkPacketType(pdu_type); - } - - if (packet_type_ == CircuitModeControlEntityPacketType(CircuitModeControlEntityDownlinkPacketType::kDSdsData)) { - sds_data_ = SdsData::from_d_sds_data(data); - } - - if (packet_type_ == CircuitModeControlEntityPacketType(CircuitModeControlEntityUplinkPacketType::kUSdsData)) { - sds_data_ = SdsData::from_u_sds_data(data); - } - } + explicit CircuitModeControlEntityPacket(const MobileLinkEntityPacket& packet); }; -inline auto operator<<(std::ostream& stream, const CircuitModeControlEntityPacket& cmce) -> std::ostream& { - stream << " CMCE: " << to_string(cmce.packet_type_) << std::endl; - if (cmce.sds_data_) { - stream << *cmce.sds_data_; - } - return stream; -}; \ No newline at end of file +auto operator<<(std::ostream& stream, const CircuitModeControlEntityPacket& cmce) -> std::ostream&; \ No newline at end of file diff --git a/include/l3/mobile_link_entity_packet.hpp b/include/l3/mobile_link_entity_packet.hpp index 04df3f8..e3dc3ba 100644 --- a/include/l3/mobile_link_entity_packet.hpp +++ b/include/l3/mobile_link_entity_packet.hpp @@ -50,19 +50,7 @@ struct MobileLinkEntityPacket : public LogicalLinkControlPacket { MobileLinkEntityPacket() = delete; - explicit MobileLinkEntityPacket(const LogicalLinkControlPacket& packet) - : LogicalLinkControlPacket(packet) { - sdu_ = BitVector(tl_sdu_); - - auto discriminator = sdu_.take<3>(); - mle_protocol_ = MobileLinkEntityProtocolDiscriminator(discriminator); - - // TODO: add the special handling for the MLE protocol - }; + explicit MobileLinkEntityPacket(const LogicalLinkControlPacket& packet); }; -inline auto operator<<(std::ostream& stream, const MobileLinkEntityPacket& mle) -> std::ostream& { - stream << " MLE: " << to_string(mle.mle_protocol_) << std::endl; - stream << " SDU: " << mle.sdu_ << std::endl; - return stream; -} \ No newline at end of file +auto operator<<(std::ostream& stream, const MobileLinkEntityPacket& mle) -> std::ostream&; \ No newline at end of file diff --git a/include/l3/short_data_service_packet.hpp b/include/l3/short_data_service_packet.hpp index 12dbc71..e69cb72 100644 --- a/include/l3/short_data_service_packet.hpp +++ b/include/l3/short_data_service_packet.hpp @@ -13,64 +13,6 @@ #include #include -static auto integer_to_double(uint32_t data, std::size_t bits, double multiplier) -> double { - if (data & (1 << (bits - 1))) { - data = ~data; - data += 1; - data &= (0xFFFFFFFF >> (32 - bits)); - - return -1 * multiplier * data / static_cast(1 << (bits - 1)); - } else { - return multiplier * data / static_cast(1 << (bits - 1)); - } -} - -static auto decode_longitude(uint64_t v) -> double { - assert(v < std::pow(2, 25)); - - return integer_to_double(static_cast(v), 25, 180.0); -} - -static auto decode_latitude(uint64_t v) -> double { - assert(v < std::pow(2, 24)); - - return integer_to_double(static_cast(v), 24, 90.0); -} - -static auto decode_position_error(uint64_t v) -> std::string { - assert(v < 8); - - const std::string position_error[] = {"< 2 m", "< 20 m", "< 200 m", "< 2 km", - "< 20 km", "<= 200 km", "> 200 km", "unknown"}; - - return position_error[v]; -} - -static auto decode_horizontal_velocity(uint64_t v) -> double { - assert(v < std::pow(2, 7)); - - if (v == 127) { - return -1.0; - } else { - const double C = 16.0; - const double x = 0.038; - const double A = 13.0; - const double B = 0.0; - - return C * std::pow((1 + x), (v - A)) + B; - } -} - -static auto decode_direction_of_travel(uint64_t v) -> std::string { - assert(v < std::pow(2, 4)); - - const std::string direction_of_travel[] = {"0 N", "22.5 NNE", "45 NE", "67.5 ENE", "90 E", "112.5 ESE", - "135 SE", "157.5 SSE", "180 S", "202.5 SSW", "225 SW", "247.5 WSW", - "270 W", "292.5 WNW", "315 NW", "337.5 NNW"}; - - return direction_of_travel[v]; -} - struct ShortLocationReport { unsigned _BitInt(2) time_elapsed_; double longitude_; @@ -82,27 +24,10 @@ struct ShortLocationReport { unsigned _BitInt(8) additional_data_; ShortLocationReport() = delete; - explicit ShortLocationReport(BitVector& data) - : time_elapsed_(data.take<2>()) - , longitude_(decode_longitude(data.take<25>())) - , latitude_(decode_latitude(data.take<24>())) - , position_error_(decode_position_error(data.take<3>())) - , horizontal_velocity_(decode_horizontal_velocity(data.take<7>())) - , direction_of_travel_(decode_direction_of_travel(data.take<4>())) - , type_of_addition_data_(data.take<1>()) - , additional_data_(data.take<8>()){}; + explicit ShortLocationReport(BitVector& data); }; -inline auto operator<<(std::ostream& stream, const ShortLocationReport& slr) -> std::ostream& { - stream << " Short Location Report" << std::endl; - stream << " Time elapsed: " << std::bitset<2>(slr.time_elapsed_) << " Lon: " << slr.longitude_ - << " Lat: " << slr.latitude_ << " Position error: " << slr.position_error_ - << " horizontal_velocity: " << slr.horizontal_velocity_ - << " direction_of_travel: " << slr.direction_of_travel_ - << " type_of_addition_data: " << std::bitset<1>(slr.additional_data_) - << " additional_data: " << std::bitset<8>(slr.additional_data_) << std::endl; - return stream; -} +auto operator<<(std::ostream& stream, const ShortLocationReport& slr) -> std::ostream&; struct LocationInformationProtocol { unsigned _BitInt(2) pdu_type_; @@ -110,25 +35,10 @@ struct LocationInformationProtocol { LocationInformationProtocol() = delete; - explicit LocationInformationProtocol(BitVector& data) - : pdu_type_(data.take<2>()) { - if (pdu_type_ == 0b00) { - short_location_report_ = ShortLocationReport(data); - } - }; -}; - -inline auto operator<<(std::ostream& stream, const LocationInformationProtocol& lip) -> std::ostream& { - if (lip.short_location_report_) { - stream << *lip.short_location_report_ << std::endl; - } else if (lip.pdu_type_ == 0b01) { - stream << "Location protocol PDU with extension" << std::endl; - } else { - stream << "Reserved LIP" << std::endl; - } - return stream; + explicit LocationInformationProtocol(BitVector& data); }; +auto operator<<(std::ostream& stream, const LocationInformationProtocol& lip) -> std::ostream&; struct ShortDataServicePacket : public CircuitModeControlEntityPacket { unsigned _BitInt(8) protocol_identifier_; std::optional location_information_protocol_; @@ -137,33 +47,7 @@ struct ShortDataServicePacket : public CircuitModeControlEntityPacket { ShortDataServicePacket() = delete; - explicit ShortDataServicePacket(const CircuitModeControlEntityPacket& packet) - : CircuitModeControlEntityPacket(packet) { - assert(sds_data_.has_value()); - auto data = BitVector(sds_data_->data_); - - protocol_identifier_ = data.take<8>(); - - if (protocol_identifier_ == kLocationInformationProtocolIdentifier) { - location_information_protocol_ = LocationInformationProtocol(data); - } - }; + explicit ShortDataServicePacket(const CircuitModeControlEntityPacket& packet); }; -inline auto operator<<(std::ostream& stream, const ShortDataServicePacket& sds) -> std::ostream& { - stream << "SDS " << std::bitset<8>(sds.protocol_identifier_) << std::endl; - stream << " L2 Address: " << sds.address_ << " SDS Address: " << sds.sds_data_->address_ << std::endl; - if (sds.location_information_protocol_) { - stream << *sds.location_information_protocol_; - } - stream << " Data: " << sds.sds_data_->data_ << std::endl; - stream << " decoded: "; - const auto len = sds.sds_data_->data_.bits_left(); - for (auto i = 8; i + 8 <= len; i += 8) { - auto bits = sds.sds_data_->data_.look<8>(i); - stream << " " << std::hex << std::setw(2) << std::setfill('0') << static_cast(bits); - } - stream << std::endl; - - return stream; -} \ No newline at end of file +auto operator<<(std::ostream& stream, const ShortDataServicePacket& sds) -> std::ostream&; \ No newline at end of file diff --git a/src/l2/logical_link_control_formatter.cpp b/src/l2/logical_link_control_formatter.cpp new file mode 100644 index 0000000..52af58d --- /dev/null +++ b/src/l2/logical_link_control_formatter.cpp @@ -0,0 +1,32 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l2/logical_link_control_packet.hpp" + +auto operator<<(std::ostream& stream, const BasicLinkInformation& bli) -> std::ostream& { + stream << " Basic Link Information: " << to_string(bli.basic_link_type_); + if (bli.n_r_) { + stream << " N(R): " << std::bitset<1>(*bli.n_r_); + } + if (bli.n_s_) { + stream << " N(S): " << std::bitset<1>(*bli.n_s_); + } + if (bli.fcs_good_) { + stream << " FCS good: " << (*bli.fcs_good_ ? "true" : "false"); + } + stream << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const LogicalLinkControlPacket& llc) -> std::ostream& { + if (llc.basic_link_information_) { + stream << *llc.basic_link_information_; + } + stream << " TL-SDU: " << llc.tl_sdu_ << std::endl; + return stream; +} \ No newline at end of file diff --git a/src/l2/logical_link_control_packet.cpp b/src/l2/logical_link_control_packet.cpp new file mode 100644 index 0000000..ded17ef --- /dev/null +++ b/src/l2/logical_link_control_packet.cpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l2/logical_link_control_packet.hpp" + +BasicLinkInformation::BasicLinkInformation(BitVector& data) { + auto pdu_type = data.take<4>(); + + switch (pdu_type) { + case 0b0000: + n_r_ = data.take<1>(); + n_s_ = data.take<1>(); + break; + case 0b0001: + n_s_ = data.take<1>(); + break; + case 0b0010: + break; + case 0b0011: + n_r_ = data.take<1>(); + break; + case 0b0100: + n_r_ = data.take<1>(); + n_s_ = data.take<1>(); + break; + case 0b0101: + n_s_ = data.take<1>(); + break; + case 0b0110: + break; + case 0b0111: + n_r_ = data.take<1>(); + break; + default: + throw std::runtime_error("Did not expect something other than basic link in LogicalLinkControlPacket parser!"); + } + + basic_link_type_ = BasicLinkType(pdu_type); + + if (pdu_type >= 0b0100) { + auto fcs = data.take_last<32>(); + auto computed_fcs = data.compute_fcs(); + fcs_good_ = fcs == computed_fcs; + } +} + +LogicalLinkControlPacket::LogicalLinkControlPacket(const UpperMacCPlaneSignallingPacket& packet) + : UpperMacCPlaneSignallingPacket(packet) { + auto data = BitVector(*tm_sdu_); + auto pdu_type = data.look<4>(0); + + /// We only implemented packet parsing for Basic Link PDUs at this point in time + if (pdu_type <= 0b1000) { + basic_link_information_ = BasicLinkInformation(data); + tl_sdu_ = data; + } +} \ No newline at end of file diff --git a/src/l3/circuit_mode_control_entity_formatter.cpp b/src/l3/circuit_mode_control_entity_formatter.cpp new file mode 100644 index 0000000..0a999b2 --- /dev/null +++ b/src/l3/circuit_mode_control_entity_formatter.cpp @@ -0,0 +1,27 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l3/circuit_mode_control_entity_packet.hpp" + +auto operator<<(std::ostream& stream, const SdsData& sds) -> std::ostream& { + if (sds.area_selection_) { + stream << " Area Selction: " << std::bitset<4>(*sds.area_selection_) << std::endl; + } + stream << " SDS Address: " << sds.address_ << std::endl; + stream << " Data: " << sds.data_ << std::endl; + stream << " Unparsed: " << sds.unparsed_ << std::endl; + return stream; +}; + +auto operator<<(std::ostream& stream, const CircuitModeControlEntityPacket& cmce) -> std::ostream& { + stream << " CMCE: " << to_string(cmce.packet_type_) << std::endl; + if (cmce.sds_data_) { + stream << *cmce.sds_data_; + } + return stream; +}; \ No newline at end of file diff --git a/src/l3/circuit_mode_control_entity_packet.cpp b/src/l3/circuit_mode_control_entity_packet.cpp new file mode 100644 index 0000000..9ea7b32 --- /dev/null +++ b/src/l3/circuit_mode_control_entity_packet.cpp @@ -0,0 +1,102 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l3/circuit_mode_control_entity_packet.hpp" + +auto SdsData::from_d_sds_data(BitVector& data) -> SdsData { + SdsData sds; + auto calling_party_type_identifier = data.take<2>(); + + if (calling_party_type_identifier == 1 || calling_party_type_identifier == 2) { + sds.address_.set_ssi(data.take<24>()); + } + if (calling_party_type_identifier == 2) { + sds.address_.set_country_code(data.take<10>()); + sds.address_.set_network_code(data.take<14>()); + } + + auto short_data_type_identifier = data.take<2>(); + + unsigned length_identifier = 0; + switch (short_data_type_identifier) { + case 0b00: + length_identifier = 16; + break; + case 0b01: + length_identifier = 32; + break; + case 0b10: + length_identifier = 64; + break; + case 0b11: + length_identifier = data.take<11>(); + break; + } + sds.data_ = data.take_vector(length_identifier); + sds.unparsed_ = data.take_vector(data.bits_left()); + + return sds; +} + +auto SdsData::from_u_sds_data(BitVector& data) -> SdsData { + SdsData sds; + sds.area_selection_ = data.take<4>(); + auto calling_party_type_identifier = data.take<2>(); + + if (calling_party_type_identifier == 0) { + sds.address_.set_sna(data.take<8>()); + } + if (calling_party_type_identifier == 1 || calling_party_type_identifier == 2) { + sds.address_.set_ssi(data.take<24>()); + } + if (calling_party_type_identifier == 2) { + sds.address_.set_country_code(data.take<10>()); + sds.address_.set_network_code(data.take<14>()); + } + + auto short_data_type_identifier = data.take<2>(); + unsigned length_identifier = 0; + switch (short_data_type_identifier) { + case 0b00: + length_identifier = 16; + break; + case 0b01: + length_identifier = 32; + break; + case 0b10: + length_identifier = 64; + break; + case 0b11: + length_identifier = data.take<11>(); + break; + } + sds.data_ = data.take_vector(length_identifier); + sds.unparsed_ = data.take_vector(data.bits_left()); + + return sds; +} + +CircuitModeControlEntityPacket::CircuitModeControlEntityPacket(const MobileLinkEntityPacket& packet) + : MobileLinkEntityPacket(packet) { + auto data = BitVector(sdu_); + + auto pdu_type = data.take<5>(); + if (is_downlink()) { + packet_type_ = CircuitModeControlEntityDownlinkPacketType(pdu_type); + } else { + packet_type_ = CircuitModeControlEntityUplinkPacketType(pdu_type); + } + + if (packet_type_ == CircuitModeControlEntityPacketType(CircuitModeControlEntityDownlinkPacketType::kDSdsData)) { + sds_data_ = SdsData::from_d_sds_data(data); + } + + if (packet_type_ == CircuitModeControlEntityPacketType(CircuitModeControlEntityUplinkPacketType::kUSdsData)) { + sds_data_ = SdsData::from_u_sds_data(data); + } +} \ No newline at end of file diff --git a/src/l3/mobile_link_entity_formatter.cpp b/src/l3/mobile_link_entity_formatter.cpp new file mode 100644 index 0000000..d0e39bf --- /dev/null +++ b/src/l3/mobile_link_entity_formatter.cpp @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l3/mobile_link_entity_packet.hpp" + +auto operator<<(std::ostream& stream, const MobileLinkEntityPacket& mle) -> std::ostream& { + stream << " MLE: " << to_string(mle.mle_protocol_) << std::endl; + stream << " SDU: " << mle.sdu_ << std::endl; + return stream; +} \ No newline at end of file diff --git a/src/l3/mobile_link_entity_packet.cpp b/src/l3/mobile_link_entity_packet.cpp new file mode 100644 index 0000000..51391f1 --- /dev/null +++ b/src/l3/mobile_link_entity_packet.cpp @@ -0,0 +1,19 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l3/mobile_link_entity_packet.hpp" + +MobileLinkEntityPacket::MobileLinkEntityPacket(const LogicalLinkControlPacket& packet) + : LogicalLinkControlPacket(packet) { + sdu_ = BitVector(tl_sdu_); + + auto discriminator = sdu_.take<3>(); + mle_protocol_ = MobileLinkEntityProtocolDiscriminator(discriminator); + + // TODO: add the special handling for the MLE protocol +}; \ No newline at end of file diff --git a/src/l3/short_data_service_formatter.cpp b/src/l3/short_data_service_formatter.cpp new file mode 100644 index 0000000..66ec319 --- /dev/null +++ b/src/l3/short_data_service_formatter.cpp @@ -0,0 +1,49 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l3/short_data_service_packet.hpp" + +auto operator<<(std::ostream& stream, const ShortLocationReport& slr) -> std::ostream& { + stream << " Short Location Report" << std::endl; + stream << " Time elapsed: " << std::bitset<2>(slr.time_elapsed_) << " Lon: " << slr.longitude_ + << " Lat: " << slr.latitude_ << " Position error: " << slr.position_error_ + << " horizontal_velocity: " << slr.horizontal_velocity_ + << " direction_of_travel: " << slr.direction_of_travel_ + << " type_of_addition_data: " << std::bitset<1>(slr.additional_data_) + << " additional_data: " << std::bitset<8>(slr.additional_data_) << std::endl; + return stream; +} + +auto operator<<(std::ostream& stream, const LocationInformationProtocol& lip) -> std::ostream& { + if (lip.short_location_report_) { + stream << *lip.short_location_report_ << std::endl; + } else if (lip.pdu_type_ == 0b01) { + stream << "Location protocol PDU with extension" << std::endl; + } else { + stream << "Reserved LIP" << std::endl; + } + return stream; +} + +auto operator<<(std::ostream& stream, const ShortDataServicePacket& sds) -> std::ostream& { + stream << "SDS " << std::bitset<8>(sds.protocol_identifier_) << std::endl; + stream << " L2 Address: " << sds.address_ << " SDS Address: " << sds.sds_data_->address_ << std::endl; + if (sds.location_information_protocol_) { + stream << *sds.location_information_protocol_; + } + stream << " Data: " << sds.sds_data_->data_ << std::endl; + stream << " decoded: "; + const auto len = sds.sds_data_->data_.bits_left(); + for (auto i = 8; i + 8 <= len; i += 8) { + auto bits = sds.sds_data_->data_.look<8>(i); + stream << " " << std::hex << std::setw(2) << std::setfill('0') << static_cast(bits); + } + stream << std::endl; + + return stream; +} \ No newline at end of file diff --git a/src/l3/short_data_service_packet.cpp b/src/l3/short_data_service_packet.cpp new file mode 100644 index 0000000..b12b0e1 --- /dev/null +++ b/src/l3/short_data_service_packet.cpp @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l3/short_data_service_packet.hpp" + +static auto integer_to_double(uint32_t data, std::size_t bits, double multiplier) -> double { + if (data & (1 << (bits - 1))) { + data = ~data; + data += 1; + data &= (0xFFFFFFFF >> (32 - bits)); + + return -1 * multiplier * data / static_cast(1 << (bits - 1)); + } + return multiplier * data / static_cast(1 << (bits - 1)); +} + +static auto decode_longitude(unsigned _BitInt(25) v) -> double { + return integer_to_double(static_cast(v), 25, 180.0); +} + +static auto decode_latitude(unsigned _BitInt(24) v) -> double { + return integer_to_double(static_cast(v), 24, 90.0); +} + +static auto decode_position_error(unsigned _BitInt(3) v) -> std::string { + static const std::array kPositionError = {"< 2 m", "< 20 m", "< 200 m", "< 2 km", + "< 20 km", "<= 200 km", "> 200 km", "unknown"}; + return kPositionError.at(v); +} + +static auto decode_horizontal_velocity(unsigned _BitInt(7) v) -> double { + if (v == 127) { + return -1.0; + } + const double C = 16.0; + const double x = 0.038; + const double A = 13.0; + const double B = 0.0; + + return C * std::pow((1 + x), (v - A)) + B; +} + +static auto decode_direction_of_travel(unsigned _BitInt(4) v) -> std::string { + static const std::array kDirectionOfTravel = { + "0 N", "22.5 NNE", "45 NE", "67.5 ENE", "90 E", "112.5 ESE", "135 SE", "157.5 SSE", + "180 S", "202.5 SSW", "225 SW", "247.5 WSW", "270 W", "292.5 WNW", "315 NW", "337.5 NNW"}; + return kDirectionOfTravel.at(v); +} + +ShortLocationReport::ShortLocationReport(BitVector& data) + : time_elapsed_(data.take<2>()) + , longitude_(decode_longitude(data.take<25>())) + , latitude_(decode_latitude(data.take<24>())) + , position_error_(decode_position_error(data.take<3>())) + , horizontal_velocity_(decode_horizontal_velocity(data.take<7>())) + , direction_of_travel_(decode_direction_of_travel(data.take<4>())) + , type_of_addition_data_(data.take<1>()) + , additional_data_(data.take<8>()) {} + +LocationInformationProtocol::LocationInformationProtocol(BitVector& data) + : pdu_type_(data.take<2>()) { + if (pdu_type_ == 0b00) { + short_location_report_ = ShortLocationReport(data); + } +} + +ShortDataServicePacket::ShortDataServicePacket(const CircuitModeControlEntityPacket& packet) + : CircuitModeControlEntityPacket(packet) { + assert(sds_data_.has_value()); + auto data = BitVector(sds_data_->data_); + + protocol_identifier_ = data.take<8>(); + + if (protocol_identifier_ == kLocationInformationProtocolIdentifier) { + location_information_protocol_ = LocationInformationProtocol(data); + } +} From 5863c982d2bc997d79200f3f23d0fc5a7590dbef Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 21:12:09 +0200 Subject: [PATCH 12/34] add libcpr --- CMakeLists.txt | 3 ++- derivation.nix | 2 ++ 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 819d403..9bc21be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,6 +52,7 @@ find_package(ZLIB REQUIRED) find_package(fmt REQUIRED) find_package(nlohmann_json REQUIRED) find_package(prometheus-cpp CONFIG REQUIRED) +find_package(cpr REQUIRED) include_directories(${CMAKE_SOURCE_DIR}/include) @@ -59,7 +60,7 @@ if (NOT NIX_BUILD) target_link_libraries(tetra-decoder cxxopts::cxxopts) endif() -target_link_libraries(tetra-decoder ZLIB::ZLIB fmt::fmt nlohmann_json::nlohmann_json viterbi prometheus-cpp::pull) +target_link_libraries(tetra-decoder ZLIB::ZLIB fmt::fmt nlohmann_json::nlohmann_json viterbi prometheus-cpp::pull cpr::cpr) target_link_libraries(tetra-viterbi viterbi) install(TARGETS tetra-decoder DESTINATION bin) diff --git a/derivation.nix b/derivation.nix index 2dff06b..e33acd3 100644 --- a/derivation.nix +++ b/derivation.nix @@ -7,6 +7,7 @@ , nlohmann_json , prometheus-cpp , curlFull +, libcpr }: clangStdenv.mkDerivation { name = "tetra-decoder"; @@ -21,6 +22,7 @@ clangStdenv.mkDerivation { nlohmann_json curlFull prometheus-cpp + libcpr ]; cmakeFlags = [ "-DNIX_BUILD=ON" ]; From a40daf3c343b43183c6f6ff0218453c153cf1229 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 22:14:32 +0200 Subject: [PATCH 13/34] implement borzoi sender thread --- CMakeLists.txt | 1 + include/borzoi_sender.hpp | 43 +++++++++++++++++++++ include/decoder.hpp | 10 +++++ include/l2/upper_mac.hpp | 14 ++++++- include/thread_safe_fifo.hpp | 62 ++++++++++++++++++++++++++++++ src/borzoi_sender.cpp | 73 ++++++++++++++++++++++++++++++++++++ src/decoder.cpp | 5 ++- src/l2/upper_mac.cpp | 39 +++++++------------ 8 files changed, 218 insertions(+), 29 deletions(-) create mode 100644 include/borzoi_sender.hpp create mode 100644 include/thread_safe_fifo.hpp create mode 100644 src/borzoi_sender.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 9bc21be..64d40d0 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,6 +9,7 @@ add_executable(tetra-decoder src/main.cpp src/decoder.cpp src/bit_stream_decoder.cpp + src/borzoi_sender.cpp src/iq_stream_decoder.cpp src/prometheus.cpp src/l2/access_assignment_channel.cpp diff --git a/include/borzoi_sender.hpp b/include/borzoi_sender.hpp new file mode 100644 index 0000000..5041b5c --- /dev/null +++ b/include/borzoi_sender.hpp @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2022-2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l2/logical_link_control_packet.hpp" +#include "l2/slot.hpp" +#include "thread_safe_fifo.hpp" +#include +#include +#include + +class BorzoiSender { + public: + BorzoiSender() = delete; + + /// This class sends the HTTPS Post requests to borzoi. https://github.com/tlm-solutions/borzoi + /// \param queue the queue holds either the parsed packets (std::unique_ptr) or Slots that + /// failed to decode + /// \param termination_flag this flag is set when the sender should terminate after finishing all work + BorzoiSender(ThreadSafeFifo, Slots>>& queue, + std::atomic_bool& termination_flag, unsigned borzoi_port); + + ~BorzoiSender(); + + private: + /// The thread function for continously process incomming parsed packets or failed slots. + auto worker() -> void; + + /// The input queue + ThreadSafeFifo, Slots>>& queue_; + + /// The flag that is set when terminating the program + std::atomic_bool& termination_flag_; + + /// The worker thread + std::thread worker_thread_; +}; diff --git a/include/decoder.hpp b/include/decoder.hpp index a728800..956f450 100644 --- a/include/decoder.hpp +++ b/include/decoder.hpp @@ -10,9 +10,11 @@ #pragma once #include "bit_stream_decoder.hpp" +#include "borzoi_sender.hpp" #include "iq_stream_decoder.hpp" #include "l2/lower_mac.hpp" #include "l2/upper_mac.hpp" +#include "thread_safe_fifo.hpp" #include #include #include @@ -54,12 +56,20 @@ class Decoder { /// This flag is passed for the StreamingOrderedOutputThreadPoolExecutor to the upper mac. std::atomic_bool upper_mac_termination_flag_ = false; + /// This flag is passed from the upper mac to the borzoi sender. + std::atomic_bool borzoi_sender_termination_flag_ = false; + /// This queue is used to pass data from the upper mac to the borzoi sender. + ThreadSafeFifo, Slots>> bozoi_queue_; + /// The worker queue for the lower mac std::shared_ptr> lower_mac_work_queue_; /// The reference to the upper mac thread class std::unique_ptr upper_mac_; + /// The reference to the borzoi sender thread class + std::unique_ptr borzoi_sender_; + std::shared_ptr bit_stream_decoder_; std::unique_ptr iq_stream_decoder_; diff --git a/include/l2/upper_mac.hpp b/include/l2/upper_mac.hpp index bbc2cda..a5b5064 100644 --- a/include/l2/upper_mac.hpp +++ b/include/l2/upper_mac.hpp @@ -17,6 +17,7 @@ #include "l2/upper_mac_packet_builder.hpp" #include "prometheus.h" #include "streaming_ordered_output_thread_pool_executor.hpp" +#include "thread_safe_fifo.hpp" #include #include #include @@ -25,13 +26,17 @@ class UpperMac { public: UpperMac() = delete; /// - /// \param queue the input queue from the lower mac + /// \param input_queue the input queue from the lower mac + /// \param output_queue the queue where successfully parsed packets or failed slots are inserted /// \param termination_flag the flag that indicates that the worker thread should stop execution after all work is /// finished + /// \param output_termination_flag this flag is set when all work is finished and pushed into the queue /// \param prometheus_exporter the reference to the prometheus exporter that is used for the metrics in the upper /// mac UpperMac(const std::shared_ptr>& input_queue, - std::atomic_bool& termination_flag, const std::shared_ptr& prometheus_exporter); + ThreadSafeFifo, Slots>>& output_queue, + std::atomic_bool& termination_flag, std::atomic_bool& output_termination_flag, + const std::shared_ptr& prometheus_exporter); ~UpperMac(); private: @@ -51,6 +56,11 @@ class UpperMac { std::shared_ptr> input_queue_; /// The termination flag std::atomic_bool& termination_flag_; + /// the termination flag on the input for the next stage + std::atomic_bool& output_termination_flag_; + + /// The output queue + ThreadSafeFifo, Slots>>& output_queue_; /// The prometheus metrics std::unique_ptr metrics_; diff --git a/include/thread_safe_fifo.hpp b/include/thread_safe_fifo.hpp new file mode 100644 index 0000000..0182d11 --- /dev/null +++ b/include/thread_safe_fifo.hpp @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include +#include +#include +#include + +template class ThreadSafeFifo { + public: + using OptionalT = std::optional; + + ThreadSafeFifo() = default; + ~ThreadSafeFifo() = default; + + ThreadSafeFifo(const ThreadSafeFifo&) = delete; + auto operator=(const ThreadSafeFifo&) -> ThreadSafeFifo& = delete; + + ThreadSafeFifo(ThreadSafeFifo&&) = delete; + auto operator=(ThreadSafeFifo&&) -> ThreadSafeFifo& = delete; + + // get a finished item of a nullopt + auto get_or_null() -> OptionalT { + using namespace std::chrono_literals; + + OptionalT result; + + { + std::lock_guard lk(mutex_); + if (!queue_.empty()) { + result = std::forward(queue_.front()); + queue_.pop_front(); + } + } + + return result; + }; + + auto empty() -> bool { + std::lock_guard lk(mutex_); + return queue_.empty(); + }; + + auto push_back(T&& element) -> void { + std::lock_guard lk(mutex_); + queue_.push_back(std::forward(element)); + }; + + private: + /// the mutex that is used to access the queue. + std::mutex mutex_; + + /// the wrapped queue + std::deque queue_; +}; diff --git a/src/borzoi_sender.cpp b/src/borzoi_sender.cpp new file mode 100644 index 0000000..2ceedc1 --- /dev/null +++ b/src/borzoi_sender.cpp @@ -0,0 +1,73 @@ +/* + * Copyright (C) 2022-2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "borzoi_sender.hpp" +#include "l3/circuit_mode_control_entity_packet.hpp" +#include "l3/mobile_link_entity_packet.hpp" +#include "l3/short_data_service_packet.hpp" + +#if defined(__linux__) +#include +#endif + +BorzoiSender::BorzoiSender(ThreadSafeFifo, Slots>>& queue, + std::atomic_bool& termination_flag, unsigned borzoi_port) + : queue_(queue) + , termination_flag_(termination_flag) { + worker_thread_ = std::thread(&BorzoiSender::worker, this); + +#if defined(__linux__) + auto handle = worker_thread_.native_handle(); + pthread_setname_np(handle, "BorzoiSender"); +#endif +} + +BorzoiSender::~BorzoiSender() { worker_thread_.join(); } + +void BorzoiSender::worker() { + for (;;) { + const auto return_value = queue_.get_or_null(); + + if (!return_value) { + if (termination_flag_.load() && queue_.empty()) { + break; + } + + continue; + } + + std::visit( + [](auto&& arg) { + using T = std::decay_t; + if constexpr (std::is_same_v>) { + /// process the parsed packet + if (auto* llc = dynamic_cast(arg.get())) { + if (llc->basic_link_information_ && + (llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithoutFcs || + llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithFcs)) { + return; + } + std::cout << *llc; + if (auto* mle = dynamic_cast(llc)) { + std::cout << *mle; + if (auto* cmce = dynamic_cast(llc)) { + std::cout << *cmce; + if (auto* sds = dynamic_cast(llc)) { + std::cout << *sds; + } + } + std::cout << std::endl; + } + } + } else if constexpr (std::is_same_v) { + /// send out the slots which had an error while parsing + } + }, + *return_value); + } +} \ No newline at end of file diff --git a/src/decoder.cpp b/src/decoder.cpp index e9d8ce6..fa1b53a 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -8,6 +8,7 @@ */ #include "decoder.hpp" +#include "borzoi_sender.hpp" #include "l2/upper_mac.hpp" #include #include @@ -34,7 +35,9 @@ Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::op , iq_or_bit_stream_(iq_or_bit_stream) { auto is_uplink = uplink_scrambling_code_.has_value(); auto lower_mac = std::make_shared(prometheus_exporter, uplink_scrambling_code); - upper_mac_ = std::make_unique(lower_mac_work_queue_, upper_mac_termination_flag_, prometheus_exporter); + upper_mac_ = std::make_unique(lower_mac_work_queue_, bozoi_queue_, upper_mac_termination_flag_, + borzoi_sender_termination_flag_, prometheus_exporter); + borzoi_sender_ = std::make_unique(bozoi_queue_, borzoi_sender_termination_flag_, send_port); bit_stream_decoder_ = std::make_shared(lower_mac_work_queue_, lower_mac, uplink_scrambling_code_.has_value()); iq_stream_decoder_ = diff --git a/src/l2/upper_mac.cpp b/src/l2/upper_mac.cpp index 9102183..0c590cf 100644 --- a/src/l2/upper_mac.cpp +++ b/src/l2/upper_mac.cpp @@ -18,9 +18,13 @@ #endif UpperMac::UpperMac(const std::shared_ptr>& input_queue, - std::atomic_bool& termination_flag, const std::shared_ptr& prometheus_exporter) + ThreadSafeFifo, Slots>>& output_queue, + std::atomic_bool& termination_flag, std::atomic_bool& output_termination_flag, + const std::shared_ptr& prometheus_exporter) : input_queue_(input_queue) , termination_flag_(termination_flag) + , output_termination_flag_(output_termination_flag) + , output_queue_(output_queue) , logical_link_control_(prometheus_exporter) { if (prometheus_exporter) { metrics_ = std::make_unique(prometheus_exporter); @@ -52,11 +56,14 @@ void UpperMac::worker() { continue; } - const auto& slots = *return_value; + auto slots = *return_value; if (slots) { this->process(*slots); } } + + // forward the termination to the next stage + output_termination_flag_.store(true); } auto UpperMac::process(const Slots& slots) -> void { @@ -92,6 +99,8 @@ auto UpperMac::process(const Slots& slots) -> void { for (const auto& slot : concreate_slots) { metrics_->increment_decode_error(slot); } + // send the broken slot to borzoi + output_queue_.push_back(slots); } } } @@ -136,30 +145,8 @@ auto UpperMac::processPackets(UpperMacPackets&& packets) -> void { } for (const auto& packet : c_plane_packets) { - auto parsed_packet = logical_link_control_.parse(packet); - - if (auto* llc = dynamic_cast(parsed_packet.get())) { - if (llc->basic_link_information_ && - (llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithoutFcs || - llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithFcs)) { - continue; - } - std::cout << *llc; - if (auto* mle = dynamic_cast(llc)) { - std::cout << *mle; - if (auto* cmce = dynamic_cast(llc)) { - std::cout << *cmce; - if (auto* sds = dynamic_cast(llc)) { - std::cout << *sds; - } - } - std::cout << std::endl; - } - } + auto llc = logical_link_control_.parse(packet); - // if (!parsed_packet->is_null_pdu()) { - // std::cout << *parsed_packet << std::endl; - // } - /// TODO: send this packet to borzoi + output_queue_.push_back(std::move(llc)); } } \ No newline at end of file From a091dbabfabe6199d5d9f38f668220ed37e5a0af Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 22:27:17 +0200 Subject: [PATCH 14/34] add config parsing for borzoi --- CMakeLists.txt | 2 +- include/{ => borzoi}/borzoi_sender.hpp | 7 ++++++- include/decoder.hpp | 4 ++-- src/{ => borzoi}/borzoi_sender.cpp | 8 +++++--- src/decoder.cpp | 6 +++--- src/main.cpp | 27 +++++++++++++------------- 6 files changed, 30 insertions(+), 24 deletions(-) rename include/{ => borzoi}/borzoi_sender.hpp (84%) rename src/{ => borzoi}/borzoi_sender.cpp (89%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 64d40d0..7b8d927 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -9,9 +9,9 @@ add_executable(tetra-decoder src/main.cpp src/decoder.cpp src/bit_stream_decoder.cpp - src/borzoi_sender.cpp src/iq_stream_decoder.cpp src/prometheus.cpp + src/borzoi/borzoi_sender.cpp src/l2/access_assignment_channel.cpp src/l2/broadcast_synchronization_channel.cpp src/l2/logical_link_control_formatter.cpp diff --git a/include/borzoi_sender.hpp b/include/borzoi/borzoi_sender.hpp similarity index 84% rename from include/borzoi_sender.hpp rename to include/borzoi/borzoi_sender.hpp index 5041b5c..cae6d04 100644 --- a/include/borzoi_sender.hpp +++ b/include/borzoi/borzoi_sender.hpp @@ -23,8 +23,9 @@ class BorzoiSender { /// \param queue the queue holds either the parsed packets (std::unique_ptr) or Slots that /// failed to decode /// \param termination_flag this flag is set when the sender should terminate after finishing all work + /// \param borzoi_url the URL of borzoi BorzoiSender(ThreadSafeFifo, Slots>>& queue, - std::atomic_bool& termination_flag, unsigned borzoi_port); + std::atomic_bool& termination_flag, const std::string& borzoi_url); ~BorzoiSender(); @@ -38,6 +39,10 @@ class BorzoiSender { /// The flag that is set when terminating the program std::atomic_bool& termination_flag_; + /// The urls of borzoi + std::string borzoi_url_sds_; + std::string borzoi_url_failed_slots_; + /// The worker thread std::thread worker_thread_; }; diff --git a/include/decoder.hpp b/include/decoder.hpp index 956f450..6d6452c 100644 --- a/include/decoder.hpp +++ b/include/decoder.hpp @@ -40,8 +40,8 @@ */ class Decoder { public: - Decoder(unsigned int receive_port, unsigned int send_port, bool packed, std::optional input_file, - std::optional output_file, bool iq_or_bit_stream, + Decoder(unsigned int receive_port, const std::string& borzoi_url, bool packed, + std::optional input_file, std::optional output_file, bool iq_or_bit_stream, std::optional uplink_scrambling_code, const std::shared_ptr& prometheus_exporter); ~Decoder(); diff --git a/src/borzoi_sender.cpp b/src/borzoi/borzoi_sender.cpp similarity index 89% rename from src/borzoi_sender.cpp rename to src/borzoi/borzoi_sender.cpp index 2ceedc1..3af44fa 100644 --- a/src/borzoi_sender.cpp +++ b/src/borzoi/borzoi_sender.cpp @@ -6,7 +6,7 @@ * Marenz Schmidl */ -#include "borzoi_sender.hpp" +#include "borzoi/borzoi_sender.hpp" #include "l3/circuit_mode_control_entity_packet.hpp" #include "l3/mobile_link_entity_packet.hpp" #include "l3/short_data_service_packet.hpp" @@ -16,9 +16,11 @@ #endif BorzoiSender::BorzoiSender(ThreadSafeFifo, Slots>>& queue, - std::atomic_bool& termination_flag, unsigned borzoi_port) + std::atomic_bool& termination_flag, const std::string& borzoi_url) : queue_(queue) - , termination_flag_(termination_flag) { + , termination_flag_(termination_flag) + , borzoi_url_sds_(borzoi_url + "/tetra") + , borzoi_url_failed_slots_(borzoi_url + "/tetra/failed_slots") { worker_thread_ = std::thread(&BorzoiSender::worker, this); #if defined(__linux__) diff --git a/src/decoder.cpp b/src/decoder.cpp index fa1b53a..7c67822 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -24,8 +24,8 @@ #include #include -Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::optional input_file, - std::optional output_file, bool iq_or_bit_stream, +Decoder::Decoder(unsigned receive_port, const std::string& borzoi_url, bool packed, + std::optional input_file, std::optional output_file, bool iq_or_bit_stream, std::optional uplink_scrambling_code, const std::shared_ptr& prometheus_exporter) : lower_mac_work_queue_(std::make_shared>( @@ -37,7 +37,7 @@ Decoder::Decoder(unsigned receive_port, unsigned send_port, bool packed, std::op auto lower_mac = std::make_shared(prometheus_exporter, uplink_scrambling_code); upper_mac_ = std::make_unique(lower_mac_work_queue_, bozoi_queue_, upper_mac_termination_flag_, borzoi_sender_termination_flag_, prometheus_exporter); - borzoi_sender_ = std::make_unique(bozoi_queue_, borzoi_sender_termination_flag_, send_port); + borzoi_sender_ = std::make_unique(bozoi_queue_, borzoi_sender_termination_flag_, borzoi_url); bit_stream_decoder_ = std::make_shared(lower_mac_work_queue_, lower_mac, uplink_scrambling_code_.has_value()); iq_stream_decoder_ = diff --git a/src/main.cpp b/src/main.cpp index 19432c2..914dbc4 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,10 +1,8 @@ +#include "decoder.hpp" #include #include #include #include -#include - -#include #include volatile bool stop = false; @@ -16,9 +14,12 @@ void sigint_handler(int s) { } auto main(int argc, char** argv) -> int { - unsigned int receive_port, send_port, debug_level; - bool packed, iq_or_bit_stream; - std::optional input_file, output_file; + unsigned int receive_port; + bool packed; + bool iq_or_bit_stream; + std::optional input_file; + std::optional output_file; + std::string borzoi_url; std::optional uplink_scrambling_code; std::optional prometheus_address; std::optional prometheus_name; @@ -35,10 +36,10 @@ auto main(int argc, char** argv) -> int { options.add_options() ("h,help", "Print usage") ("r,rx", " receiving from phy", cxxopts::value()->default_value("42000")) - ("t,tx", " sending Json data", cxxopts::value()->default_value("42100")) + ("t,tx", " sending Json data", cxxopts::value()->default_value("42100")) + ("borzoi-url", " the base url of which borzoi is running", cxxopts::value(borzoi_url)->default_value("http://localhost:3000")) ("i,infile", " replay data from binary file instead of UDP", cxxopts::value>(input_file)) ("o,outfile", " record data to binary file (can be replayed with -i option)", cxxopts::value>(output_file)) - ("d", " print debug information", cxxopts::value()->default_value("0")) ("P,packed", "pack rx data (1 byte = 8 bits)", cxxopts::value()->default_value("false")) ("iq", "Receive IQ instead of bitstream", cxxopts::value()->default_value("false")) ("uplink", " enable uplink parsing with predefined scrambilng code", cxxopts::value>(uplink_scrambling_code)) @@ -52,13 +53,11 @@ auto main(int argc, char** argv) -> int { if (result.count("help")) { std::cout << options.help() << std::endl; - exit(0); + return EXIT_SUCCESS; } receive_port = result["rx"].as(); - send_port = result["tx"].as(); - debug_level = result["d"].as(); packed = result["packed"].as(); iq_or_bit_stream = result["iq"].as(); @@ -71,15 +70,15 @@ auto main(int argc, char** argv) -> int { return EXIT_FAILURE; } - auto decoder = std::make_unique(receive_port, send_port, packed, input_file, output_file, iq_or_bit_stream, - uplink_scrambling_code, prometheus_exporter); + auto decoder = std::make_unique(receive_port, borzoi_url, packed, input_file, output_file, + iq_or_bit_stream, uplink_scrambling_code, prometheus_exporter); if (input_file.has_value()) { std::cout << "Reading from input file " << *input_file << std::endl; } else { std::cout << "Listening on UDP socket " << receive_port << std::endl; } - std::cout << "Sending on UDP socket " << send_port << std::endl; + std::cout << "Sending to Borzoi on: " << borzoi_url << std::endl; if (output_file.has_value()) { std::cout << "Writing to output file " << *output_file << std::endl; } From a8557c6bfaaa2065160d6b9b9f231a817bbfe6e2 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 23:39:30 +0200 Subject: [PATCH 15/34] integrate cpr for sds sending to borzoi --- CMakeLists.txt | 1 + include/borzoi/borzoi_converter.hpp | 15 +++++ include/borzoi/borzoi_sender.hpp | 15 +++-- include/decoder.hpp | 4 +- nixos-modules/default.nix | 93 +---------------------------- src/borzoi/borzoi_converter.cpp | 40 +++++++++++++ src/borzoi/borzoi_sender.cpp | 35 ++++++++++- src/decoder.cpp | 6 +- src/main.cpp | 4 +- 9 files changed, 110 insertions(+), 103 deletions(-) create mode 100644 include/borzoi/borzoi_converter.hpp create mode 100644 src/borzoi/borzoi_converter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 7b8d927..37ede83 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(tetra-decoder src/bit_stream_decoder.cpp src/iq_stream_decoder.cpp src/prometheus.cpp + src/borzoi/borzoi_converter.cpp src/borzoi/borzoi_sender.cpp src/l2/access_assignment_channel.cpp src/l2/broadcast_synchronization_channel.cpp diff --git a/include/borzoi/borzoi_converter.hpp b/include/borzoi/borzoi_converter.hpp new file mode 100644 index 0000000..350faf6 --- /dev/null +++ b/include/borzoi/borzoi_converter.hpp @@ -0,0 +1,15 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "l3/short_data_service_packet.hpp" + +struct BorzoiConverter { + static auto to_json(ShortDataServicePacket* packet) -> nlohmann::json; +}; diff --git a/include/borzoi/borzoi_sender.hpp b/include/borzoi/borzoi_sender.hpp index cae6d04..a2d5f96 100644 --- a/include/borzoi/borzoi_sender.hpp +++ b/include/borzoi/borzoi_sender.hpp @@ -12,6 +12,7 @@ #include "l2/slot.hpp" #include "thread_safe_fifo.hpp" #include +#include #include #include @@ -19,13 +20,14 @@ class BorzoiSender { public: BorzoiSender() = delete; - /// This class sends the HTTPS Post requests to borzoi. https://github.com/tlm-solutions/borzoi + /// This class sends the HTTP Post requests to borzoi. https://github.com/tlm-solutions/borzoi /// \param queue the queue holds either the parsed packets (std::unique_ptr) or Slots that /// failed to decode /// \param termination_flag this flag is set when the sender should terminate after finishing all work /// \param borzoi_url the URL of borzoi + /// \param borzoi_uuid the station UUID of this instance of tetra-decoder sending to borzoi BorzoiSender(ThreadSafeFifo, Slots>>& queue, - std::atomic_bool& termination_flag, const std::string& borzoi_url); + std::atomic_bool& termination_flag, const std::string& borzoi_url, std::string borzoi_uuid); ~BorzoiSender(); @@ -33,6 +35,8 @@ class BorzoiSender { /// The thread function for continously process incomming parsed packets or failed slots. auto worker() -> void; + void send_packet(const std::unique_ptr& packet); + /// The input queue ThreadSafeFifo, Slots>>& queue_; @@ -40,8 +44,11 @@ class BorzoiSender { std::atomic_bool& termination_flag_; /// The urls of borzoi - std::string borzoi_url_sds_; - std::string borzoi_url_failed_slots_; + cpr::Url borzoi_url_sds_; + cpr::Url borzoi_url_failed_slots_; + + /// The Station UUID of borzoi + std::string borzoi_uuid_; /// The worker thread std::thread worker_thread_; diff --git a/include/decoder.hpp b/include/decoder.hpp index 6d6452c..e168180 100644 --- a/include/decoder.hpp +++ b/include/decoder.hpp @@ -10,7 +10,7 @@ #pragma once #include "bit_stream_decoder.hpp" -#include "borzoi_sender.hpp" +#include "borzoi/borzoi_sender.hpp" #include "iq_stream_decoder.hpp" #include "l2/lower_mac.hpp" #include "l2/upper_mac.hpp" @@ -40,7 +40,7 @@ */ class Decoder { public: - Decoder(unsigned int receive_port, const std::string& borzoi_url, bool packed, + Decoder(unsigned int receive_port, const std::string& borzoi_url, const std::string& borzoi_uuid, bool packed, std::optional input_file, std::optional output_file, bool iq_or_bit_stream, std::optional uplink_scrambling_code, const std::shared_ptr& prometheus_exporter); diff --git a/nixos-modules/default.nix b/nixos-modules/default.nix index c67bc38..a4008f5 100644 --- a/nixos-modules/default.nix +++ b/nixos-modules/default.nix @@ -28,13 +28,6 @@ in { Port where the decoder receives its bits on. ''; }; - options.transmitPort = mkOption { - type = types.port; - default = 42100; - description = '' - Port where the decoder transmits its data on. - ''; - }; options.extraDecoderArgs = mkOption { type = types.str; default = ""; @@ -73,91 +66,11 @@ in { script = '' exec ${pkgs.tetra-decoder}/bin/tetra-decoder --rx ${ toString instanceConfig.receivePort - } --tx ${toString instanceConfig.transmitPort} ${instanceConfig.extraDecoderArgs} & - ''; - - serviceConfig = { - Type = "forking"; - User = cfg.user; - Restart = "on-failure"; - StartLimitBurst = "2"; - StartLimitIntervalSec = "150s"; - }; - }; - - "tetra-sender-${instanceName}" = { - enable = true; - wantedBy = [ "multi-user.target" ]; - - script = let - pythonScript = pkgs.writeScript "tetra-sender-${instanceName}" '' - #!${pkgs.python3.withPackages(ps: [ ps.requests ])}/bin/python3 - # -*- coding: utf-8 -*- - - import json - import socket - import requests - import sys - - UDP_IP = "127.0.0.1" - UDP_PORT = ${toString instanceConfig.transmitPort} - BORZOI_URL = "${toString instanceConfig.borzoiUrl}" - BORZOI_STATION_UUID = "${toString instanceConfig.borzoiStationUuid}" - - sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) - sock.bind((UDP_IP, UDP_PORT)) - - old_data = "" - while True: - data, addr = sock.recvfrom(4096) - - print(f"received message: {data}") - sys.stdout.flush() - - old_data += data.decode('utf-8') - - if len(old_data) > 0 and old_data[-1] != "\n": - continue - else: - try: - data = json.loads(old_data.strip('\n')) - old_data = "" - except: - print(f"Error parsing json: {old_data}") - old_data = "" - continue - - try: - data['source_ssi'] = data['from']['ssi'] - data['destination_ssi'] = data['to']['ssi'] - data['telegram_type'] = data['type'] - del data['from'] - del data['to'] - del data['type'] - data['arbitrary'] = {"bits_in_last_byte": data["bits_in_last_byte"]} - del data["bits_in_last_byte"] - data["station"] = BORZOI_STATION_UUID - data["time"] = data["time"].replace('_', 'T') - except: - print(f"Could not send data to borzoi: {data}") - continue - - try: - r = requests.post(BORZOI_URL, json=data) - except requests.exceptions.ConnectionError: - print(f"Borzoi refused connection") - continue - - if r.status_code != 200: - print(f"Send following data to borzoi: {data}") - print(f"Sending data to borzoi failed with HTTP code: {r.status_code}") - ''; - in '' - exec ${pythonScript} & + } --borzoi-url ${toString instanceConfig.borzoiUrl} --borzoi-uuid ${ + toString instanceConfig.borzoiStationUuid + } ${instanceConfig.extraDecoderArgs} & ''; - environment = { "PYTHONUNBUFFERED" = "1"; }; - serviceConfig = { Type = "forking"; User = cfg.user; diff --git a/src/borzoi/borzoi_converter.cpp b/src/borzoi/borzoi_converter.cpp new file mode 100644 index 0000000..b43f4c5 --- /dev/null +++ b/src/borzoi/borzoi_converter.cpp @@ -0,0 +1,40 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "borzoi/borzoi_converter.hpp" +#include "utils/bit_vector.hpp" +#include + +inline static auto get_time() -> std::string { + auto t = std::time(nullptr); + auto tm = *std::localtime(&t); + std::stringstream ss; + ss << std::put_time(&tm, "%FT%T%z"); + return ss.str(); +} + +auto BorzoiConverter::to_json(ShortDataServicePacket* packet) -> nlohmann::json { + auto message = nlohmann::json::object(); + /// TODO: this may throw + message["source_ssi"] = packet->sds_data_->address_.ssi()->to_ulong(); + message["destination_ssi"] = packet->address_.ssi()->to_ulong(); + message["protocol_identifier"] = static_cast(packet->protocol_identifier_); + message["telegram_type"] = "SDS"; + message["data"] = nlohmann::json::array(); + auto data = BitVector(packet->sds_data_->data_); + while (data.bits_left() >= 8) { + unsigned bits = data.take<8>(); + message["data"].push_back(bits); + } + message["arbitrary"] = nlohmann::json::object(); + message["arbitrary"]["bits_in_last_byte"] = data.bits_left(); + message["data"].push_back(data.take_all()); + message["time"] = get_time(); + + return message; +} \ No newline at end of file diff --git a/src/borzoi/borzoi_sender.cpp b/src/borzoi/borzoi_sender.cpp index 3af44fa..c01f4b1 100644 --- a/src/borzoi/borzoi_sender.cpp +++ b/src/borzoi/borzoi_sender.cpp @@ -7,20 +7,27 @@ */ #include "borzoi/borzoi_sender.hpp" +#include "borzoi/borzoi_converter.hpp" #include "l3/circuit_mode_control_entity_packet.hpp" #include "l3/mobile_link_entity_packet.hpp" #include "l3/short_data_service_packet.hpp" +#include +#include +#include +#include +#include #if defined(__linux__) #include #endif BorzoiSender::BorzoiSender(ThreadSafeFifo, Slots>>& queue, - std::atomic_bool& termination_flag, const std::string& borzoi_url) + std::atomic_bool& termination_flag, const std::string& borzoi_url, std::string borzoi_uuid) : queue_(queue) , termination_flag_(termination_flag) , borzoi_url_sds_(borzoi_url + "/tetra") - , borzoi_url_failed_slots_(borzoi_url + "/tetra/failed_slots") { + , borzoi_url_failed_slots_(borzoi_url + "/tetra/failed_slots") + , borzoi_uuid_(std::move(borzoi_uuid)) { worker_thread_ = std::thread(&BorzoiSender::worker, this); #if defined(__linux__) @@ -31,6 +38,27 @@ BorzoiSender::BorzoiSender(ThreadSafeFifo& packet) { + if (auto* sds = dynamic_cast(packet.get())) { + nlohmann::json json; + try { + json = BorzoiConverter::to_json(sds); + json["station"] = borzoi_uuid_; + /// TODO: add json to post request + } catch (std::exception& e) { + std::cout << "Failed to send packet to Borzoi. Error: " << e.what() << std::endl; + return; + } + cpr::Response resp = + cpr::Post(borzoi_url_sds_, cpr::Body{json}, cpr::Header{{"Content-Type", "application/json"}}); + + if (resp.status_code != 200) { + std::cout << "Failed to send packet to Borzoi. Error: " << resp.status_code << " " << resp.error.message + << std::endl; + } + } +} + void BorzoiSender::worker() { for (;;) { const auto return_value = queue_.get_or_null(); @@ -44,7 +72,7 @@ void BorzoiSender::worker() { } std::visit( - [](auto&& arg) { + [this](const auto& arg) { using T = std::decay_t; if constexpr (std::is_same_v>) { /// process the parsed packet @@ -66,6 +94,7 @@ void BorzoiSender::worker() { std::cout << std::endl; } } + send_packet(arg); } else if constexpr (std::is_same_v) { /// send out the slots which had an error while parsing } diff --git a/src/decoder.cpp b/src/decoder.cpp index 7c67822..1260aa9 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -8,7 +8,6 @@ */ #include "decoder.hpp" -#include "borzoi_sender.hpp" #include "l2/upper_mac.hpp" #include #include @@ -24,7 +23,7 @@ #include #include -Decoder::Decoder(unsigned receive_port, const std::string& borzoi_url, bool packed, +Decoder::Decoder(unsigned receive_port, const std::string& borzoi_url, const std::string& borzoi_uuid, bool packed, std::optional input_file, std::optional output_file, bool iq_or_bit_stream, std::optional uplink_scrambling_code, const std::shared_ptr& prometheus_exporter) @@ -37,7 +36,8 @@ Decoder::Decoder(unsigned receive_port, const std::string& borzoi_url, bool pack auto lower_mac = std::make_shared(prometheus_exporter, uplink_scrambling_code); upper_mac_ = std::make_unique(lower_mac_work_queue_, bozoi_queue_, upper_mac_termination_flag_, borzoi_sender_termination_flag_, prometheus_exporter); - borzoi_sender_ = std::make_unique(bozoi_queue_, borzoi_sender_termination_flag_, borzoi_url); + borzoi_sender_ = + std::make_unique(bozoi_queue_, borzoi_sender_termination_flag_, borzoi_url, borzoi_uuid); bit_stream_decoder_ = std::make_shared(lower_mac_work_queue_, lower_mac, uplink_scrambling_code_.has_value()); iq_stream_decoder_ = diff --git a/src/main.cpp b/src/main.cpp index 914dbc4..ec20bf9 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -20,6 +20,7 @@ auto main(int argc, char** argv) -> int { std::optional input_file; std::optional output_file; std::string borzoi_url; + std::string borzoi_uuid; std::optional uplink_scrambling_code; std::optional prometheus_address; std::optional prometheus_name; @@ -38,6 +39,7 @@ auto main(int argc, char** argv) -> int { ("r,rx", " receiving from phy", cxxopts::value()->default_value("42000")) ("t,tx", " sending Json data", cxxopts::value()->default_value("42100")) ("borzoi-url", " the base url of which borzoi is running", cxxopts::value(borzoi_url)->default_value("http://localhost:3000")) + ("borzoi-uuid", " the UUID of this tetra-decoder sending data to borzoi", cxxopts::value(borzoi_uuid)->default_value("00000000-0000-0000-0000-000000000000")) ("i,infile", " replay data from binary file instead of UDP", cxxopts::value>(input_file)) ("o,outfile", " record data to binary file (can be replayed with -i option)", cxxopts::value>(output_file)) ("P,packed", "pack rx data (1 byte = 8 bits)", cxxopts::value()->default_value("false")) @@ -70,7 +72,7 @@ auto main(int argc, char** argv) -> int { return EXIT_FAILURE; } - auto decoder = std::make_unique(receive_port, borzoi_url, packed, input_file, output_file, + auto decoder = std::make_unique(receive_port, borzoi_url, borzoi_uuid, packed, input_file, output_file, iq_or_bit_stream, uplink_scrambling_code, prometheus_exporter); if (input_file.has_value()) { From 2267175be9f50a67266b33f595d7e0dbdc78a85e Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Tue, 2 Jul 2024 23:47:55 +0200 Subject: [PATCH 16/34] send failed slots for borzoi --- include/borzoi/borzoi_converter.hpp | 2 ++ include/borzoi/borzoi_sender.hpp | 1 + src/borzoi/borzoi_converter.cpp | 29 +++++++++++++++++++++++++++++ src/borzoi/borzoi_sender.cpp | 20 ++++++++++++++++++++ 4 files changed, 52 insertions(+) diff --git a/include/borzoi/borzoi_converter.hpp b/include/borzoi/borzoi_converter.hpp index 350faf6..d124102 100644 --- a/include/borzoi/borzoi_converter.hpp +++ b/include/borzoi/borzoi_converter.hpp @@ -8,8 +8,10 @@ #pragma once +#include "l2/slot.hpp" #include "l3/short_data_service_packet.hpp" struct BorzoiConverter { static auto to_json(ShortDataServicePacket* packet) -> nlohmann::json; + static auto to_json(const Slots& slots) -> nlohmann::json; }; diff --git a/include/borzoi/borzoi_sender.hpp b/include/borzoi/borzoi_sender.hpp index a2d5f96..59f302e 100644 --- a/include/borzoi/borzoi_sender.hpp +++ b/include/borzoi/borzoi_sender.hpp @@ -36,6 +36,7 @@ class BorzoiSender { auto worker() -> void; void send_packet(const std::unique_ptr& packet); + void send_failed_slots(const Slots& slots); /// The input queue ThreadSafeFifo, Slots>>& queue_; diff --git a/src/borzoi/borzoi_converter.cpp b/src/borzoi/borzoi_converter.cpp index b43f4c5..a26ec99 100644 --- a/src/borzoi/borzoi_converter.cpp +++ b/src/borzoi/borzoi_converter.cpp @@ -36,5 +36,34 @@ auto BorzoiConverter::to_json(ShortDataServicePacket* packet) -> nlohmann::json message["data"].push_back(data.take_all()); message["time"] = get_time(); + return message; +} + +auto BorzoiConverter::to_json(const Slots& slots) -> nlohmann::json { + auto message = nlohmann::json::object(); + + message["time"] = get_time(); + message["burst_type"] = slots.get_burst_type(); + /// This may but should not throw. + auto first_slot = slots.get_first_slot().get_logical_channel_data_and_crc(); + message["first_slot_logical_channel"] = first_slot.channel; + message["first_slot_logical_data"] = nlohmann::json::array(); + while (first_slot.data.bits_left()) { + unsigned bit = first_slot.data.take<1>(); + message["first_slot_logical_data"].push_back(bit); + } + message["first_slot_crc_ok"] = first_slot.crc_ok; + auto second_slot_present = slots.has_second_slot(); + message["second_slot_present"] = second_slot_present; + if (second_slot_present) { + auto second_slot = slots.get_second_slot().get_logical_channel_data_and_crc(); + message["second_slot_logical_channel"] = first_slot.channel; + message["second_slot_data"] = nlohmann::json::array(); + while (first_slot.data.bits_left()) { + unsigned bit = first_slot.data.take<1>(); + message["second_slot_data"].push_back(bit); + } + message["second_slot_crc_ok"] = first_slot.crc_ok; + } return message; } \ No newline at end of file diff --git a/src/borzoi/borzoi_sender.cpp b/src/borzoi/borzoi_sender.cpp index c01f4b1..063b2a2 100644 --- a/src/borzoi/borzoi_sender.cpp +++ b/src/borzoi/borzoi_sender.cpp @@ -59,6 +59,25 @@ void BorzoiSender::send_packet(const std::unique_ptr& } } +void BorzoiSender::send_failed_slots(const Slots& slots) { + nlohmann::json json; + try { + json = BorzoiConverter::to_json(slots); + json["station"] = borzoi_uuid_; + /// TODO: add json to post request + } catch (std::exception& e) { + std::cout << "Failed to send packet to Borzoi. Error: " << e.what() << std::endl; + return; + } + cpr::Response resp = + cpr::Post(borzoi_url_failed_slots_, cpr::Body{json}, cpr::Header{{"Content-Type", "application/json"}}); + + if (resp.status_code != 200) { + std::cout << "Failed to send packet to Borzoi. Error: " << resp.status_code << " " << resp.error.message + << std::endl; + } +} + void BorzoiSender::worker() { for (;;) { const auto return_value = queue_.get_or_null(); @@ -97,6 +116,7 @@ void BorzoiSender::worker() { send_packet(arg); } else if constexpr (std::is_same_v) { /// send out the slots which had an error while parsing + send_failed_slots(arg); } }, *return_value); From ee0b1e060b8bed355c73adbbaf6a659c27e0a891 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 00:03:49 +0200 Subject: [PATCH 17/34] fix crashed in borzoi sender --- src/borzoi/borzoi_converter.cpp | 6 +++--- src/borzoi/borzoi_sender.cpp | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/borzoi/borzoi_converter.cpp b/src/borzoi/borzoi_converter.cpp index a26ec99..b067892 100644 --- a/src/borzoi/borzoi_converter.cpp +++ b/src/borzoi/borzoi_converter.cpp @@ -43,10 +43,10 @@ auto BorzoiConverter::to_json(const Slots& slots) -> nlohmann::json { auto message = nlohmann::json::object(); message["time"] = get_time(); - message["burst_type"] = slots.get_burst_type(); + message["burst_type"] = static_cast(slots.get_burst_type()); /// This may but should not throw. auto first_slot = slots.get_first_slot().get_logical_channel_data_and_crc(); - message["first_slot_logical_channel"] = first_slot.channel; + message["first_slot_logical_channel"] = static_cast(first_slot.channel); message["first_slot_logical_data"] = nlohmann::json::array(); while (first_slot.data.bits_left()) { unsigned bit = first_slot.data.take<1>(); @@ -57,7 +57,7 @@ auto BorzoiConverter::to_json(const Slots& slots) -> nlohmann::json { message["second_slot_present"] = second_slot_present; if (second_slot_present) { auto second_slot = slots.get_second_slot().get_logical_channel_data_and_crc(); - message["second_slot_logical_channel"] = first_slot.channel; + message["second_slot_logical_channel"] = static_cast(first_slot.channel); message["second_slot_data"] = nlohmann::json::array(); while (first_slot.data.bits_left()) { unsigned bit = first_slot.data.take<1>(); diff --git a/src/borzoi/borzoi_sender.cpp b/src/borzoi/borzoi_sender.cpp index 063b2a2..eaaeb31 100644 --- a/src/borzoi/borzoi_sender.cpp +++ b/src/borzoi/borzoi_sender.cpp @@ -50,7 +50,7 @@ void BorzoiSender::send_packet(const std::unique_ptr& return; } cpr::Response resp = - cpr::Post(borzoi_url_sds_, cpr::Body{json}, cpr::Header{{"Content-Type", "application/json"}}); + cpr::Post(borzoi_url_sds_, cpr::Body{json.dump()}, cpr::Header{{"Content-Type", "application/json"}}); if (resp.status_code != 200) { std::cout << "Failed to send packet to Borzoi. Error: " << resp.status_code << " " << resp.error.message @@ -70,7 +70,7 @@ void BorzoiSender::send_failed_slots(const Slots& slots) { return; } cpr::Response resp = - cpr::Post(borzoi_url_failed_slots_, cpr::Body{json}, cpr::Header{{"Content-Type", "application/json"}}); + cpr::Post(borzoi_url_failed_slots_, cpr::Body{json.dump()}, cpr::Header{{"Content-Type", "application/json"}}); if (resp.status_code != 200) { std::cout << "Failed to send packet to Borzoi. Error: " << resp.status_code << " " << resp.error.message From dd72bd3163524c7ade5bc63206b7bddcda8dc56a Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 00:24:18 +0200 Subject: [PATCH 18/34] make thread safe fifo take less cpu --- include/thread_safe_fifo.hpp | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/include/thread_safe_fifo.hpp b/include/thread_safe_fifo.hpp index 0182d11..41a47bc 100644 --- a/include/thread_safe_fifo.hpp +++ b/include/thread_safe_fifo.hpp @@ -8,6 +8,7 @@ #pragma once +#include #include #include #include @@ -37,7 +38,18 @@ template class ThreadSafeFifo { if (!queue_.empty()) { result = std::forward(queue_.front()); queue_.pop_front(); + + return result; } + cv_.wait_for(lk, 10ms, [&] { + if (!queue_.empty()) { + auto result = std::forward(queue_.front()); + queue_.pop_front(); + + return true; + } + return false; + }); } return result; @@ -51,12 +63,16 @@ template class ThreadSafeFifo { auto push_back(T&& element) -> void { std::lock_guard lk(mutex_); queue_.push_back(std::forward(element)); + cv_.notify_one(); }; private: /// the mutex that is used to access the queue. std::mutex mutex_; + /// the condition variable that is set when an item was inserted + std::condition_variable cv_; + /// the wrapped queue std::deque queue_; }; From dde49c79c30dfd76a414f3dabf4b4d4ee297c0d1 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 00:27:41 +0200 Subject: [PATCH 19/34] fix compile error --- include/thread_safe_fifo.hpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/thread_safe_fifo.hpp b/include/thread_safe_fifo.hpp index 41a47bc..285d0c8 100644 --- a/include/thread_safe_fifo.hpp +++ b/include/thread_safe_fifo.hpp @@ -41,6 +41,9 @@ template class ThreadSafeFifo { return result; } + } + { + std::unique_lock lk(mutex_); cv_.wait_for(lk, 10ms, [&] { if (!queue_.empty()) { auto result = std::forward(queue_.front()); From 6b3e57426db24a64fb2c6b4a529efa0e77d0320d Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 00:31:40 +0200 Subject: [PATCH 20/34] fix borzoi sender deadlock --- include/thread_safe_fifo.hpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/include/thread_safe_fifo.hpp b/include/thread_safe_fifo.hpp index 285d0c8..4b1b759 100644 --- a/include/thread_safe_fifo.hpp +++ b/include/thread_safe_fifo.hpp @@ -64,8 +64,10 @@ template class ThreadSafeFifo { }; auto push_back(T&& element) -> void { - std::lock_guard lk(mutex_); - queue_.push_back(std::forward(element)); + { + std::lock_guard lk(mutex_); + queue_.push_back(std::forward(element)); + } cv_.notify_one(); }; From 1812e02439377328a8936d710b0c0ab80c21a04f Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 00:39:57 +0200 Subject: [PATCH 21/34] fix bug in borzoi sender --- include/thread_safe_fifo.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/thread_safe_fifo.hpp b/include/thread_safe_fifo.hpp index 4b1b759..ea4653f 100644 --- a/include/thread_safe_fifo.hpp +++ b/include/thread_safe_fifo.hpp @@ -46,7 +46,7 @@ template class ThreadSafeFifo { std::unique_lock lk(mutex_); cv_.wait_for(lk, 10ms, [&] { if (!queue_.empty()) { - auto result = std::forward(queue_.front()); + result = std::forward(queue_.front()); queue_.pop_front(); return true; From ccf61fd3ef42d6bdea73011ab9ef99415d05dfc7 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 00:50:01 +0200 Subject: [PATCH 22/34] fix LLC parser bug --- src/l2/logical_link_control_packet.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/l2/logical_link_control_packet.cpp b/src/l2/logical_link_control_packet.cpp index ded17ef..ee871b8 100644 --- a/src/l2/logical_link_control_packet.cpp +++ b/src/l2/logical_link_control_packet.cpp @@ -55,7 +55,7 @@ LogicalLinkControlPacket::LogicalLinkControlPacket(const UpperMacCPlaneSignallin auto pdu_type = data.look<4>(0); /// We only implemented packet parsing for Basic Link PDUs at this point in time - if (pdu_type <= 0b1000) { + if (pdu_type < 0b1000) { basic_link_information_ = BasicLinkInformation(data); tl_sdu_ = data; } From 4ebea8680db75b2c6b84083c2017d7edceba08c7 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 12:05:35 +0200 Subject: [PATCH 23/34] add more debug prints --- src/borzoi/borzoi_sender.cpp | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/borzoi/borzoi_sender.cpp b/src/borzoi/borzoi_sender.cpp index eaaeb31..c6887ec 100644 --- a/src/borzoi/borzoi_sender.cpp +++ b/src/borzoi/borzoi_sender.cpp @@ -46,15 +46,15 @@ void BorzoiSender::send_packet(const std::unique_ptr& json["station"] = borzoi_uuid_; /// TODO: add json to post request } catch (std::exception& e) { - std::cout << "Failed to send packet to Borzoi. Error: " << e.what() << std::endl; + std::cout << "Failed to convert packet to json. Error: " << e.what() << std::endl; return; } cpr::Response resp = cpr::Post(borzoi_url_sds_, cpr::Body{json.dump()}, cpr::Header{{"Content-Type", "application/json"}}); if (resp.status_code != 200) { - std::cout << "Failed to send packet to Borzoi. Error: " << resp.status_code << " " << resp.error.message - << std::endl; + std::cout << "Failed to send packet to Borzoi: " << json.dump() << " Error: " << resp.status_code << " " + << resp.error.message << std::endl; } } } @@ -66,15 +66,15 @@ void BorzoiSender::send_failed_slots(const Slots& slots) { json["station"] = borzoi_uuid_; /// TODO: add json to post request } catch (std::exception& e) { - std::cout << "Failed to send packet to Borzoi. Error: " << e.what() << std::endl; + std::cout << "Failed to convert packet to json. Error: " << e.what() << std::endl; return; } cpr::Response resp = cpr::Post(borzoi_url_failed_slots_, cpr::Body{json.dump()}, cpr::Header{{"Content-Type", "application/json"}}); if (resp.status_code != 200) { - std::cout << "Failed to send packet to Borzoi. Error: " << resp.status_code << " " << resp.error.message - << std::endl; + std::cout << "Failed to send packet to Borzoi: " << json.dump() << " Error: " << resp.status_code << " " + << resp.error.message << std::endl; } } From 075475c980442a6be533ee6505462995fea87b9e Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 16:46:59 +0200 Subject: [PATCH 24/34] parse packet type for mobile management --- CMakeLists.txt | 1 + include/l3/mobile_management_packet.hpp | 124 +++++++++++++++++++++++- include/l3/mobile_management_parser.hpp | 42 +------- src/l3/mobile_management_packet.cpp | 20 ++++ 4 files changed, 146 insertions(+), 41 deletions(-) create mode 100644 src/l3/mobile_management_packet.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 37ede83..242f952 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(tetra-decoder src/l3/circuit_mode_control_entity_packet.cpp src/l3/mobile_link_entity_formatter.cpp src/l3/mobile_link_entity_packet.cpp + src/l3/mobile_management_packet.cpp src/l3/short_data_service_formatter.cpp src/l3/short_data_service_packet.cpp src/utils/address.cpp diff --git a/include/l3/mobile_management_packet.hpp b/include/l3/mobile_management_packet.hpp index d67f59e..08e8832 100644 --- a/include/l3/mobile_management_packet.hpp +++ b/include/l3/mobile_management_packet.hpp @@ -9,10 +9,130 @@ #pragma once #include "l3/mobile_link_entity_packet.hpp" +#include + +enum class MobileManagementDownlinkPacketType { + kDOtar, + kDAuthentication, + kDCkChangeDemand, + kDDisable, + kDEnable, + kDLocationUpdateAccept, + kDLocationUpdateCommand, + kDLocationUpdateReject, + kDReserved8, + kDLocationUpdateProceeding, + kDAttachDetachGroupIdentity, + kDAttachDetachGroupIdentityAck, + kDMmStatus, + kDReserved13, + kDReserved14, + kDMmPduFunctionNotSupported, +}; + +constexpr auto to_string(MobileManagementDownlinkPacketType type) -> const char* { + switch (type) { + case MobileManagementDownlinkPacketType::kDOtar: + return "D-OTAR"; + case MobileManagementDownlinkPacketType::kDAuthentication: + return "D-AUTHENTICATION"; + case MobileManagementDownlinkPacketType::kDCkChangeDemand: + return "D-CK CHANGE DEMAND"; + case MobileManagementDownlinkPacketType::kDDisable: + return "D-DISABLE"; + case MobileManagementDownlinkPacketType::kDEnable: + return "D-ENABLE"; + case MobileManagementDownlinkPacketType::kDLocationUpdateAccept: + return "D-LOCATION UPDATE ACCEPT"; + case MobileManagementDownlinkPacketType::kDLocationUpdateCommand: + return "D-LOCATION UPDATE COMMAND"; + case MobileManagementDownlinkPacketType::kDLocationUpdateReject: + return "D-LOCATION UPDATE REJECT"; + case MobileManagementDownlinkPacketType::kDReserved8: + return "D-Reserved8"; + case MobileManagementDownlinkPacketType::kDLocationUpdateProceeding: + return "D-LOCATION UPDATE PROCEEDING"; + case MobileManagementDownlinkPacketType::kDAttachDetachGroupIdentity: + return "D-ATTACH/DETACH GROUP IDENTITY"; + case MobileManagementDownlinkPacketType::kDAttachDetachGroupIdentityAck: + return "D-ATTACH/DETACH GROUP IDENTITY ACK"; + case MobileManagementDownlinkPacketType::kDMmStatus: + return "D-MM STATUS"; + case MobileManagementDownlinkPacketType::kDReserved13: + return "D-Reserved13"; + case MobileManagementDownlinkPacketType::kDReserved14: + return "D-Reserved14"; + case MobileManagementDownlinkPacketType::kDMmPduFunctionNotSupported: + return "D-MM PDU/FUNCTION NOT SUPPORTED"; + } +}; + +enum class MobileManagementUplinkPacketType { + kUAuthentication, + kUItsiDetach, + kULocationUpdateDemand, + kUMmStatus, + kUCkChangeResult, + kUOtar, + kUInformationProvide, + kUAttachDetachGroupIdentity, + kUAttachDetachGroupIdentityAck, + kUTeiProvide, + kUReserved10, + kUDisableStatus, + kUReserved12, + kUReserved13, + kUReserved14, + kUMmPduFunctionNotSupported, +}; + +constexpr auto to_string(MobileManagementUplinkPacketType type) -> const char* { + switch (type) { + case MobileManagementUplinkPacketType::kUAuthentication: + return "U-AUTHENTICATION"; + case MobileManagementUplinkPacketType::kUItsiDetach: + return "U-ITSI DETACH"; + case MobileManagementUplinkPacketType::kULocationUpdateDemand: + return "U-LOCATION UPDATE DEMAND"; + case MobileManagementUplinkPacketType::kUMmStatus: + return "U-MM STATUS"; + case MobileManagementUplinkPacketType::kUCkChangeResult: + return "U-CK CHANGE RESULT"; + case MobileManagementUplinkPacketType::kUOtar: + return "U-OTAR"; + case MobileManagementUplinkPacketType::kUInformationProvide: + return "U-INFORMATION PROVIDE"; + case MobileManagementUplinkPacketType::kUAttachDetachGroupIdentity: + return "U-ATTACH/DETACH GROUP IDENTITY"; + case MobileManagementUplinkPacketType::kUAttachDetachGroupIdentityAck: + return "U-ATTACH/DETACH GROUP IDENTITY ACK"; + case MobileManagementUplinkPacketType::kUTeiProvide: + return "U-TEI PROVIDE"; + case MobileManagementUplinkPacketType::kUReserved10: + return "U-Reserved10"; + case MobileManagementUplinkPacketType::kUDisableStatus: + return "U-DISABLE STATUS"; + case MobileManagementUplinkPacketType::kUReserved12: + return "U-Reserved12"; + case MobileManagementUplinkPacketType::kUReserved13: + return "U-Reserved13"; + case MobileManagementUplinkPacketType::kUReserved14: + return "U-Reserved14"; + case MobileManagementUplinkPacketType::kUMmPduFunctionNotSupported: + return "U-MM PDU/FUNCTION NOT SUPPORTED"; + } +}; + +using MobileManagementPacketType = std::variant; + +constexpr auto to_string(MobileManagementPacketType type) -> const char* { + return std::visit([](auto&& arg) { return to_string(arg); }, type); +} struct MobileManagementPacket : public MobileLinkEntityPacket { + MobileManagementPacketType packet_type_; + MobileManagementPacket() = delete; - explicit MobileManagementPacket(const MobileLinkEntityPacket& packet) - : MobileLinkEntityPacket(packet){}; + explicit MobileManagementPacket(const MobileLinkEntityPacket& packet); }; \ No newline at end of file diff --git a/include/l3/mobile_management_parser.hpp b/include/l3/mobile_management_parser.hpp index a83e028..a7d0b90 100644 --- a/include/l3/mobile_management_parser.hpp +++ b/include/l3/mobile_management_parser.hpp @@ -8,6 +8,7 @@ #pragma once +#include "l3/mobile_link_entity_packet.hpp" #include "l3/mobile_management_packet.hpp" #include "utils/packet_parser.hpp" @@ -15,51 +16,14 @@ class MobileManagementParser : public PacketParser& prometheus_exporter) - : PacketParser(prometheus_exporter, "mobile_management") { - downlink_mm_pdu_description_ = {"D-OTAR", - "D-AUTHENTICATION", - "D-CK CHANGE DEMAND", - "D-DISABLE", - "D-ENABLE", - "D-LOCATION UPDATE ACCEPT", - "D-LOCATION UPDATE COMMAND", - "D-LOCATION UPDATE REJECT", - "D-Reserved8", - "D-LOCATION UPDATE PROCEEDING", - "D-LOCATION UPDATE PROCEEDING", - "D-ATTACH/DETACH GROUP IDENTITY ACK", - "D-MM STATUS", - "D-Reserved13", - "D-Reserved14", - "D-MM PDU/FUNCTION NOT SUPPORTED"}; - uplink_mm_pdu_description_ = {"U-AUTHENTICATION", - "U-ITSI DETACH", - "U-LOCATION UPDATE DEMAND", - "U-MM STATUS", - "U-CK CHANGE RESULT", - "U-OTAR", - "U-INFORMATION PROVIDE", - "U-ATTACH/DETACH GROUP IDENTITY", - "U-ATTACH/DETACH GROUP IDENTITY ACK", - "U-TEI PROVIDE", - "U-Reserved10", - "U-DISABLE STATUS", - "U-Reserved12", - "U-Reserved13", - "U-Reserved14", - "U-MM PDU/FUNCTION NOT SUPPORTED"}; - }; + : PacketParser(prometheus_exporter, "mobile_management"){}; private: auto packet_name(const MobileManagementPacket& packet) -> std::string override { - auto pdu_type = packet.sdu_.look<4>(0); - return (packet.is_downlink() ? downlink_mm_pdu_description_ : uplink_mm_pdu_description_).at(pdu_type); + return to_string(packet.packet_type_); } auto forward(const MobileManagementPacket& packet) -> std::unique_ptr override { return std::make_unique(packet); }; - - std::array downlink_mm_pdu_description_; - std::array uplink_mm_pdu_description_; }; \ No newline at end of file diff --git a/src/l3/mobile_management_packet.cpp b/src/l3/mobile_management_packet.cpp new file mode 100644 index 0000000..4266ab9 --- /dev/null +++ b/src/l3/mobile_management_packet.cpp @@ -0,0 +1,20 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l3/mobile_management_packet.hpp" + +MobileManagementPacket::MobileManagementPacket(const MobileLinkEntityPacket& packet) + : MobileLinkEntityPacket(packet) { + auto data = BitVector(sdu_); + auto pdu_type = data.take<4>(); + if (is_downlink()) { + packet_type_ = MobileManagementDownlinkPacketType(pdu_type); + } else { + packet_type_ = MobileManagementUplinkPacketType(pdu_type); + } +}; \ No newline at end of file From aa5636497b4035ed5e7c995ca4d966c892f9d74d Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Wed, 3 Jul 2024 16:58:02 +0200 Subject: [PATCH 25/34] Use consistent style of includes. Closes #9 --- include/iq_stream_decoder.hpp | 10 ++++------ include/prometheus.h | 1 - .../streaming_ordered_output_thread_pool_executor.hpp | 2 -- include/utils/address.hpp | 3 +-- include/utils/packet_parser.hpp | 1 - src/bit_stream_decoder.cpp | 4 ++-- src/decoder.cpp | 2 +- src/examples/tetra_viterbi.cpp | 7 +++---- src/examples/viter_bi_codec.cpp | 2 +- src/iq_stream_decoder.cpp | 3 +-- src/l2/lower_mac.cpp | 5 ++--- src/main.cpp | 2 +- src/utils/address.cpp | 3 +-- src/utils/bit_vector.cpp | 4 +--- src/utils/viter_bi_codec.cpp | 2 +- 15 files changed, 19 insertions(+), 32 deletions(-) diff --git a/include/iq_stream_decoder.hpp b/include/iq_stream_decoder.hpp index b9b674d..3c03583 100644 --- a/include/iq_stream_decoder.hpp +++ b/include/iq_stream_decoder.hpp @@ -9,15 +9,13 @@ #pragma once +#include "bit_stream_decoder.hpp" +#include "fixed_queue.hpp" +#include "l2/lower_mac.hpp" +#include "streaming_ordered_output_thread_pool_executor.hpp" #include #include -#include -#include -#include -#include -#include - /** * Tetra downlink decoder for PI/4-DQPSK modulation * diff --git a/include/prometheus.h b/include/prometheus.h index c1dda02..ad0d176 100644 --- a/include/prometheus.h +++ b/include/prometheus.h @@ -2,7 +2,6 @@ #define PROMETHEUS_H #include - #include #include #include diff --git a/include/streaming_ordered_output_thread_pool_executor.hpp b/include/streaming_ordered_output_thread_pool_executor.hpp index 0dfaba0..b357a45 100644 --- a/include/streaming_ordered_output_thread_pool_executor.hpp +++ b/include/streaming_ordered_output_thread_pool_executor.hpp @@ -16,9 +16,7 @@ #include #include #include -#include #include -#include #include #if defined(__linux__) #include diff --git a/include/utils/address.hpp b/include/utils/address.hpp index 387bcae..9e97d3d 100644 --- a/include/utils/address.hpp +++ b/include/utils/address.hpp @@ -11,11 +11,10 @@ #include "utils/bit_vector.hpp" #include #include +#include #include #include -#include - class Address { public: Address() = default; diff --git a/include/utils/packet_parser.hpp b/include/utils/packet_parser.hpp index 923940a..1c54da5 100644 --- a/include/utils/packet_parser.hpp +++ b/include/utils/packet_parser.hpp @@ -10,7 +10,6 @@ #include "prometheus.h" #include "utils/packet_counter_metrics.hpp" -#include #include /// This is the template that is used to keep an consistent interface in the protocol parsing. diff --git a/src/bit_stream_decoder.cpp b/src/bit_stream_decoder.cpp index ce08b57..8c5efa8 100644 --- a/src/bit_stream_decoder.cpp +++ b/src/bit_stream_decoder.cpp @@ -7,9 +7,9 @@ * Tassilo Tanneberger */ +#include "bit_stream_decoder.hpp" +#include "burst_type.hpp" #include "l2/lower_mac.hpp" -#include -#include #include #include #include diff --git a/src/decoder.cpp b/src/decoder.cpp index 1260aa9..fe03c77 100644 --- a/src/decoder.cpp +++ b/src/decoder.cpp @@ -8,7 +8,7 @@ */ #include "decoder.hpp" -#include "l2/upper_mac.hpp" +#include "signal_handler.hpp" #include #include #include diff --git a/src/examples/tetra_viterbi.cpp b/src/examples/tetra_viterbi.cpp index 2ec0c6d..9ad684b 100644 --- a/src/examples/tetra_viterbi.cpp +++ b/src/examples/tetra_viterbi.cpp @@ -1,12 +1,11 @@ -#include +#include "viter_bi_codec.hpp" +#include "viterbi/viterbi_decoder_scalar.h" +#include #include #include #include #include -#include "viter_bi_codec.hpp" -#include "viterbi/viterbi_decoder_scalar.h" - auto viterbi = std::vector( {1, 0, 2, 2, 0, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 1, 2, 2, 2, 1, 0, 2, 2, 1, 2, 2, 2, 0, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, 0, 2, 2, 2, 0, 1, 2, 2, 0, 2, 2, 2, 1, 1, 2, 2, 1, 2, 2, 2, 0, 0, 2, 2, diff --git a/src/examples/viter_bi_codec.cpp b/src/examples/viter_bi_codec.cpp index 6cd02dc..858ae3c 100644 --- a/src/examples/viter_bi_codec.cpp +++ b/src/examples/viter_bi_codec.cpp @@ -19,8 +19,8 @@ * limitations under the License. * */ -#include "viter_bi_codec.hpp" +#include "viter_bi_codec.hpp" #include #include #include diff --git a/src/iq_stream_decoder.cpp b/src/iq_stream_decoder.cpp index 88d0f53..0e644ea 100644 --- a/src/iq_stream_decoder.cpp +++ b/src/iq_stream_decoder.cpp @@ -7,11 +7,10 @@ * Tassilo Tanneberger */ +#include "iq_stream_decoder.hpp" #include "l2/lower_mac.hpp" #include -#include - IQStreamDecoder::IQStreamDecoder( const std::shared_ptr>& lower_mac_worker_queue, const std::shared_ptr& lower_mac, const std::shared_ptr& bit_stream_decoder, diff --git a/src/l2/lower_mac.cpp b/src/l2/lower_mac.cpp index 95dfc77..658e40b 100644 --- a/src/l2/lower_mac.cpp +++ b/src/l2/lower_mac.cpp @@ -18,11 +18,10 @@ #include #include #include -#include -#include - #include #include +#include +#include LowerMac::LowerMac(const std::shared_ptr& prometheus_exporter, std::optional scrambling_code) { diff --git a/src/main.cpp b/src/main.cpp index ec20bf9..791a73a 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -1,9 +1,9 @@ #include "decoder.hpp" +#include "signal_handler.hpp" #include #include #include #include -#include volatile bool stop = false; diff --git a/src/utils/address.cpp b/src/utils/address.cpp index cae031c..b809ecf 100644 --- a/src/utils/address.cpp +++ b/src/utils/address.cpp @@ -1,7 +1,6 @@ +#include "utils/address.hpp" #include -#include - auto operator<<(std::ostream& stream, const Address& address_type) -> std::ostream& { if (address_type.country_code_) { stream << "Country Code: " << address_type.country_code_.value().to_ulong() << " "; diff --git a/src/utils/bit_vector.cpp b/src/utils/bit_vector.cpp index 4df2a1b..d9f56a1 100644 --- a/src/utils/bit_vector.cpp +++ b/src/utils/bit_vector.cpp @@ -7,16 +7,14 @@ * Tassilo Tanneberger */ +#include "utils/bit_vector.hpp" #include #include #include #include #include -#include #include -#include - auto BitVector::compute_fcs() -> uint32_t { uint32_t crc = 0xFFFFFFFF; if (len_ < 32) { diff --git a/src/utils/viter_bi_codec.cpp b/src/utils/viter_bi_codec.cpp index c6bd1b5..590e235 100644 --- a/src/utils/viter_bi_codec.cpp +++ b/src/utils/viter_bi_codec.cpp @@ -7,7 +7,7 @@ * Tassilo Tanneberger */ -#include +#include "utils/viter_bi_codec.hpp" auto ViterbiCodec::Decode(const std::vector& bits) const -> std::vector { auto vitdec = ViterbiDecoder_Core(branch_table, config); From b2e53b1a87b48df96af7dddfe089ae9369425d68 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Thu, 4 Jul 2024 16:32:22 +0200 Subject: [PATCH 26/34] add type 2/3/4 parser. add parsing for MobileManagementDownlinkLocationUpdateAccept --- include/l3/mobile_management_packet.hpp | 169 ++++++++++++++++++++++++ include/utils/address.hpp | 33 +++++ include/utils/type234_parser.hpp | 113 ++++++++++++++++ src/borzoi/borzoi_sender.cpp | 4 + src/l3/mobile_management_packet.cpp | 74 +++++++++++ 5 files changed, 393 insertions(+) create mode 100644 include/utils/type234_parser.hpp diff --git a/include/l3/mobile_management_packet.hpp b/include/l3/mobile_management_packet.hpp index 08e8832..3d837bc 100644 --- a/include/l3/mobile_management_packet.hpp +++ b/include/l3/mobile_management_packet.hpp @@ -9,6 +9,10 @@ #pragma once #include "l3/mobile_link_entity_packet.hpp" +#include "utils/bit_vector.hpp" +#include "utils/type234_parser.hpp" +#include +#include #include enum class MobileManagementDownlinkPacketType { @@ -129,10 +133,175 @@ constexpr auto to_string(MobileManagementPacketType type) -> const char* { return std::visit([](auto&& arg) { return to_string(arg); }, type); } +enum class LocationUpdateType { + kRoamingLocationUpdating, + kMigrationLocationUpdating, + kPeriodicLocationUpdating, + kItsiAttach, + kServiceRestorationRoamingLocationUpdating, + kServiceRestorationMigratingLocationUpdating, + kDemandLocationUpdating, + kDisableMsUpdating, +}; + +constexpr auto to_string(LocationUpdateType type) -> const char* { + switch (type) { + case LocationUpdateType::kRoamingLocationUpdating: + return "Roaming location updating"; + case LocationUpdateType::kMigrationLocationUpdating: + return "Migrating location updating"; + case LocationUpdateType::kPeriodicLocationUpdating: + return "Periodic location updating"; + case LocationUpdateType::kItsiAttach: + return "ITSI attach"; + case LocationUpdateType::kServiceRestorationRoamingLocationUpdating: + return "Service restoration roaming location updating"; + case LocationUpdateType::kServiceRestorationMigratingLocationUpdating: + return "Service restoration migrating location updating"; + case LocationUpdateType::kDemandLocationUpdating: + return "Demand location updating"; + case LocationUpdateType::kDisableMsUpdating: + return "Disabled MS updating"; + } +}; + +enum class LocationUpdateAcceptType { + kRoamingLocationUpdating, + kTemporaryRegistration, + kPeriodicLocationUpdating, + kItsiAttach, + kServiceRestorationRoamingLocationUpdating, + kMigratingOrServiceRestorationMigratingLocationUpdating, + kDemandLocationUpdating, + kDisableMsUpdating, +}; + +constexpr auto to_string(LocationUpdateAcceptType type) -> const char* { + switch (type) { + case LocationUpdateAcceptType::kRoamingLocationUpdating: + return "Roaming location updating"; + case LocationUpdateAcceptType::kTemporaryRegistration: + return "Temporary registration"; + case LocationUpdateAcceptType::kPeriodicLocationUpdating: + return "Periodic location updating"; + case LocationUpdateAcceptType::kItsiAttach: + return "ITSI attach"; + case LocationUpdateAcceptType::kServiceRestorationRoamingLocationUpdating: + return "Service restoration roaming location updating"; + case LocationUpdateAcceptType::kMigratingOrServiceRestorationMigratingLocationUpdating: + return "Migrating or service restoration migrating location updating"; + case LocationUpdateAcceptType::kDemandLocationUpdating: + return "Demand location updating"; + case LocationUpdateAcceptType::kDisableMsUpdating: + return "Disabled MS updating"; + } +}; + +enum class MobileManagementDownlinkType34ElementIdentifiers { + kReservedForFutureExtension, + kDefaultGroupAttachLifetime, + kNewRegisteredArea, + kSecurityDownlink, + kGroupReportResponse, + kGroupIdentityLocationAccept, + kDmMsAddress, + kGroupIdentityDownlink, + kReserved8, + kReserved9, + kAuthenticationDownlink, + kReserved11, + kGroupIdentitySecurityRelatedInformation, + kCellTypeControl, + kReserved14, + kProprietary, +}; + +constexpr auto to_string(MobileManagementDownlinkType34ElementIdentifiers type) -> const char* { + switch (type) { + case MobileManagementDownlinkType34ElementIdentifiers::kReservedForFutureExtension: + return "Reserved for future extension"; + case MobileManagementDownlinkType34ElementIdentifiers::kDefaultGroupAttachLifetime: + return "Default group attachment lifetime"; + case MobileManagementDownlinkType34ElementIdentifiers::kNewRegisteredArea: + return "New registered area"; + case MobileManagementDownlinkType34ElementIdentifiers::kSecurityDownlink: + return "Security downlink, see ETSI EN 300 392-7"; + case MobileManagementDownlinkType34ElementIdentifiers::kGroupReportResponse: + return "Group report response"; + case MobileManagementDownlinkType34ElementIdentifiers::kGroupIdentityLocationAccept: + return "Group identity location accept"; + case MobileManagementDownlinkType34ElementIdentifiers::kDmMsAddress: + return "DM-MS address, see ETSI EN 300 396-5"; + case MobileManagementDownlinkType34ElementIdentifiers::kGroupIdentityDownlink: + return "Group identity downlink"; + case MobileManagementDownlinkType34ElementIdentifiers::kReserved8: + return "Reserved 8 for any future specified Type 3/4 element"; + case MobileManagementDownlinkType34ElementIdentifiers::kReserved9: + return "Reserved 9 for any future specified Type 3/4 element"; + case MobileManagementDownlinkType34ElementIdentifiers::kAuthenticationDownlink: + return "Authentication downlink, see ETSI EN 300 392-7"; + case MobileManagementDownlinkType34ElementIdentifiers::kReserved11: + return "Reserved 11 for any future specified Type 3/4 element"; + case MobileManagementDownlinkType34ElementIdentifiers::kGroupIdentitySecurityRelatedInformation: + return "Group Identity Security Related Information, see ETSI EN 300 392-7"; + case MobileManagementDownlinkType34ElementIdentifiers::kCellTypeControl: + return "Cell type control"; + case MobileManagementDownlinkType34ElementIdentifiers::kReserved14: + return "Reserved 14 for any future specified Type 3/4 element"; + case MobileManagementDownlinkType34ElementIdentifiers::kProprietary: + return "Proprietary"; + } +}; + +struct MobileManagementDownlinkLocationUpdateAccept { + LocationUpdateAcceptType location_update_accept_type_; + Address address_; + std::optional subscriber_class_; + std::optional energy_saving_information_; + std::optional scch_information_; + std::optional distribution_on_18th_frame_; + + Type234Parser::Map optional_elements_; + + MobileManagementDownlinkLocationUpdateAccept() = delete; + + explicit MobileManagementDownlinkLocationUpdateAccept(BitVector&); +}; + +inline auto operator<<(std::ostream& stream, const MobileManagementDownlinkLocationUpdateAccept& packet) + -> std::ostream& { + stream << "D-LOCATION UPDATE ACCEPT " << to_string(packet.location_update_accept_type_) << std::endl; + if (packet.subscriber_class_) { + stream << " subscriber class: " << std::bitset<16>(*packet.subscriber_class_) << std::endl; + } + if (packet.energy_saving_information_) { + stream << " energy saving information: " << std::bitset<14>(*packet.energy_saving_information_) << std::endl; + } + if (packet.scch_information_) { + stream << " scch information: " << std::bitset<4>(*packet.scch_information_) << std::endl; + } + if (packet.distribution_on_18th_frame_) { + stream << " distribution on 18th frame: " << std::bitset<2>(*packet.distribution_on_18th_frame_) << std::endl; + } + for (const auto& [key, value] : packet.optional_elements_) { + stream << " " << to_string(key) << " " << value << std::endl; + } + return stream; +}; + struct MobileManagementPacket : public MobileLinkEntityPacket { MobileManagementPacketType packet_type_; + std::optional downlink_location_update_accept_; MobileManagementPacket() = delete; explicit MobileManagementPacket(const MobileLinkEntityPacket& packet); +}; + +inline auto operator<<(std::ostream& stream, const MobileManagementPacket& packet) -> std::ostream& { + stream << "MM " << to_string(packet.packet_type_) << std::endl; + if (packet.downlink_location_update_accept_) { + stream << *packet.downlink_location_update_accept_; + } + return stream; }; \ No newline at end of file diff --git a/include/utils/address.hpp b/include/utils/address.hpp index 9e97d3d..2bed57d 100644 --- a/include/utils/address.hpp +++ b/include/utils/address.hpp @@ -51,6 +51,39 @@ class Address { [[nodiscard]] auto smi() const noexcept { return smi_; } [[nodiscard]] auto usage_marker() const noexcept { return usage_marker_; } + auto merge(const Address& other) { + if (other.country_code_) { + country_code_ = other.country_code_; + } + if (other.network_code_) { + network_code_ = other.network_code_; + } + if (other.sna_) { + sna_ = other.sna_; + } + if (other.ssi_) { + ssi_ = other.ssi_; + } + if (other.event_label_) { + event_label_ = other.event_label_; + } + if (other.ussi_) { + ussi_ = other.ussi_; + } + if (other.smi_) { + smi_ = other.smi_; + } + if (other.usage_marker_) { + usage_marker_ = other.usage_marker_; + } + }; + + auto merge(const std::optional
& address) { + if (address) { + merge(*address); + } + } + friend auto operator<<(std::ostream& stream, const Address& address_type) -> std::ostream&; private: diff --git a/include/utils/type234_parser.hpp b/include/utils/type234_parser.hpp new file mode 100644 index 0000000..79d9d8f --- /dev/null +++ b/include/utils/type234_parser.hpp @@ -0,0 +1,113 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#pragma once + +#include "utils/bit_vector.hpp" +#include +#include +#include +#include +#include + +template struct BitVectorElement { + public: + BitVectorElement() = delete; + explicit BitVectorElement(BitVector& data) + : x_(data.take()){}; + + // NOLINTNEXTLINE(google-explicit-constructor) + operator unsigned _BitInt(N)() const noexcept { return x_; } + + private: + unsigned _BitInt(N) x_; +}; + +template struct Type34Element { + BitVector unparsed_bits; + unsigned repeated_elements = 1; +}; + +template +inline auto operator<<(std::ostream& stream, const Type34Element& element) -> std::ostream& { + stream << "#" << element.repeated_elements << ": " << element.unparsed_bits; + return stream; +}; + +template class Type234Parser { + public: + using Map = std::map>; + + Type234Parser() = delete; + + Type234Parser(BitVector& data, std::set allowed_type3_elements, + std::set allowed_type4_elements) + // Extract the O-bit + : present_(static_cast(data.take<1>())) + , allowed_type3_elements_(allowed_type3_elements) + , allowed_type4_elements_(allowed_type4_elements){}; + + template auto parse_type2(BitVector& data) -> std::optional { + if (!present_) { + return {}; + } + + std::optional type2_element; + // P-bit present? + if (data.take<1>()) { + type2_element = T(data); + } + return type2_element; + } + + auto parse_type34(BitVector& data) -> Map { + if (!present_) { + return {}; + } + + Map elements; + + while (data.bits_left()) { + // M-bit present? + if (data.take<1>() == 0) { + break; + } + auto element_identifier = ElementIdentifier(data.take<4>()); + auto length_indicator = data.take<11>(); + // Is this a type 3 element? + if (allowed_type3_elements_.count(element_identifier)) { + const auto element_data = data.take_vector(length_indicator); + if (elements.count(element_identifier)) { + throw std::runtime_error("This element identifier already occured."); + } + elements[element_identifier] = Type34Element{.unparsed_bits = element_data}; + continue; + } + // Is this a type 4 element? + if (allowed_type4_elements_.count(element_identifier)) { + const auto repeated_elements = data.take<6>(); + const auto element_data = data.take_vector(length_indicator); + if (elements.count(element_identifier)) { + throw std::runtime_error("This element identifier already occured."); + } + elements[element_identifier] = Type34Element{.unparsed_bits = element_data, + .repeated_elements = repeated_elements}; + continue; + } + // Is this element invalid? + throw std::runtime_error("This element identifier is not allowed in the current parser config."); + } + + return elements; + } + + private: + bool present_ = false; + std::set allowed_type3_elements_; + std::set allowed_type4_elements_; +}; \ No newline at end of file diff --git a/src/borzoi/borzoi_sender.cpp b/src/borzoi/borzoi_sender.cpp index c6887ec..5a93f26 100644 --- a/src/borzoi/borzoi_sender.cpp +++ b/src/borzoi/borzoi_sender.cpp @@ -10,6 +10,7 @@ #include "borzoi/borzoi_converter.hpp" #include "l3/circuit_mode_control_entity_packet.hpp" #include "l3/mobile_link_entity_packet.hpp" +#include "l3/mobile_management_packet.hpp" #include "l3/short_data_service_packet.hpp" #include #include @@ -110,6 +111,9 @@ void BorzoiSender::worker() { std::cout << *sds; } } + if (auto* mm = dynamic_cast(llc)) { + std::cout << *mm; + } std::cout << std::endl; } } diff --git a/src/l3/mobile_management_packet.cpp b/src/l3/mobile_management_packet.cpp index 4266ab9..eb36a9e 100644 --- a/src/l3/mobile_management_packet.cpp +++ b/src/l3/mobile_management_packet.cpp @@ -7,6 +7,76 @@ */ #include "l3/mobile_management_packet.hpp" +#include "utils/address.hpp" +#include "utils/bit_vector.hpp" + +struct ShortSubscriberIdentity { + public: + ShortSubscriberIdentity() = delete; + explicit ShortSubscriberIdentity(BitVector& data) { address_.set_ssi(data.take<24>()); }; + + // NOLINTNEXTLINE(google-explicit-constructor) + operator Address() const noexcept { return address_; }; + + private: + Address address_; +}; + +struct MobileNetworkInformation { + public: + MobileNetworkInformation() = delete; + explicit MobileNetworkInformation(BitVector& data) { + address_.set_country_code(data.take<10>()); + address_.set_network_code(data.take<14>()); + }; + + // NOLINTNEXTLINE(google-explicit-constructor) + operator Address() const noexcept { return address_; }; + + private: + Address address_; +}; + +struct ScchInformationAndDistributionOn18ThFrame { + public: + ScchInformationAndDistributionOn18ThFrame() = delete; + explicit ScchInformationAndDistributionOn18ThFrame(BitVector& data) + : scch_information_(data.take<4>()) + , distribution_on_18th_frame_(data.take<2>()){}; + + auto to(MobileManagementDownlinkLocationUpdateAccept* to_ref) -> void { + to_ref->scch_information_ = scch_information_; + to_ref->distribution_on_18th_frame_ = distribution_on_18th_frame_; + }; + + private: + unsigned _BitInt(4) scch_information_; + unsigned _BitInt(2) distribution_on_18th_frame_; +}; + +MobileManagementDownlinkLocationUpdateAccept::MobileManagementDownlinkLocationUpdateAccept(BitVector& data) + : location_update_accept_type_(LocationUpdateAcceptType(data.take<4>())) { + auto parser = Type234Parser( + data, + {MobileManagementDownlinkType34ElementIdentifiers::kSecurityDownlink, + MobileManagementDownlinkType34ElementIdentifiers::kGroupIdentityLocationAccept, + MobileManagementDownlinkType34ElementIdentifiers::kDefaultGroupAttachLifetime, + MobileManagementDownlinkType34ElementIdentifiers::kAuthenticationDownlink, + MobileManagementDownlinkType34ElementIdentifiers::kCellTypeControl, + MobileManagementDownlinkType34ElementIdentifiers::kProprietary}, + {MobileManagementDownlinkType34ElementIdentifiers::kNewRegisteredArea, + MobileManagementDownlinkType34ElementIdentifiers::kGroupIdentitySecurityRelatedInformation}); + + address_.merge(parser.parse_type2(data)); + address_.merge(parser.parse_type2(data)); + subscriber_class_ = parser.parse_type2>(data); + energy_saving_information_ = parser.parse_type2>(data); + auto ssch = parser.parse_type2(data); + if (ssch) { + ssch->to(this); + } + optional_elements_ = parser.parse_type34(data); +}; MobileManagementPacket::MobileManagementPacket(const MobileLinkEntityPacket& packet) : MobileLinkEntityPacket(packet) { @@ -17,4 +87,8 @@ MobileManagementPacket::MobileManagementPacket(const MobileLinkEntityPacket& pac } else { packet_type_ = MobileManagementUplinkPacketType(pdu_type); } + + if (packet_type_ == MobileManagementPacketType(MobileManagementDownlinkPacketType::kDLocationUpdateAccept)) { + downlink_location_update_accept_ = MobileManagementDownlinkLocationUpdateAccept(data); + } }; \ No newline at end of file From 8e5d097ab7dad3f8eaaf025519a22e841bf98c90 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Thu, 4 Jul 2024 16:48:16 +0200 Subject: [PATCH 27/34] log address in MobileManagementDownlinkLocationUpdateAccept --- include/l3/mobile_management_packet.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/include/l3/mobile_management_packet.hpp b/include/l3/mobile_management_packet.hpp index 3d837bc..4a48feb 100644 --- a/include/l3/mobile_management_packet.hpp +++ b/include/l3/mobile_management_packet.hpp @@ -271,6 +271,7 @@ struct MobileManagementDownlinkLocationUpdateAccept { inline auto operator<<(std::ostream& stream, const MobileManagementDownlinkLocationUpdateAccept& packet) -> std::ostream& { stream << "D-LOCATION UPDATE ACCEPT " << to_string(packet.location_update_accept_type_) << std::endl; + stream << " Address: " << packet.address_ << std::endl; if (packet.subscriber_class_) { stream << " subscriber class: " << std::bitset<16>(*packet.subscriber_class_) << std::endl; } From 2739803a350da8462522c909c113862ed90211e1 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Thu, 4 Jul 2024 16:55:34 +0200 Subject: [PATCH 28/34] log UpperMacCPlaneSignallingPacket data --- src/borzoi/borzoi_sender.cpp | 40 ++++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 18 deletions(-) diff --git a/src/borzoi/borzoi_sender.cpp b/src/borzoi/borzoi_sender.cpp index 5a93f26..a3be613 100644 --- a/src/borzoi/borzoi_sender.cpp +++ b/src/borzoi/borzoi_sender.cpp @@ -8,6 +8,7 @@ #include "borzoi/borzoi_sender.hpp" #include "borzoi/borzoi_converter.hpp" +#include "l2/upper_mac_packet.hpp" #include "l3/circuit_mode_control_entity_packet.hpp" #include "l3/mobile_link_entity_packet.hpp" #include "l3/mobile_management_packet.hpp" @@ -96,26 +97,29 @@ void BorzoiSender::worker() { using T = std::decay_t; if constexpr (std::is_same_v>) { /// process the parsed packet - if (auto* llc = dynamic_cast(arg.get())) { - if (llc->basic_link_information_ && - (llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithoutFcs || - llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithFcs)) { - return; - } - std::cout << *llc; - if (auto* mle = dynamic_cast(llc)) { - std::cout << *mle; - if (auto* cmce = dynamic_cast(llc)) { - std::cout << *cmce; - if (auto* sds = dynamic_cast(llc)) { - std::cout << *sds; - } - } - if (auto* mm = dynamic_cast(llc)) { - std::cout << *mm; + auto* cplane_signalling = dynamic_cast(arg.get()); + auto* llc = dynamic_cast(cplane_signalling); + + // Do not log acknowledgements + if (llc->basic_link_information_ && + (llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithoutFcs || + llc->basic_link_information_->basic_link_type_ == BasicLinkType::kBlAckWithFcs)) { + return; + } + std::cout << *cplane_signalling; + std::cout << *llc; + if (auto* mle = dynamic_cast(llc)) { + std::cout << *mle; + if (auto* cmce = dynamic_cast(llc)) { + std::cout << *cmce; + if (auto* sds = dynamic_cast(llc)) { + std::cout << *sds; } - std::cout << std::endl; } + if (auto* mm = dynamic_cast(llc)) { + std::cout << *mm; + } + std::cout << std::endl; } send_packet(arg); } else if constexpr (std::is_same_v) { From 65bbc7efe09b70446cbf329fe3dd324aca550f15 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Fri, 5 Jul 2024 14:19:33 +0200 Subject: [PATCH 29/34] fix bug in type 4 element parsing --- include/utils/type234_parser.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/utils/type234_parser.hpp b/include/utils/type234_parser.hpp index 79d9d8f..fe6bd73 100644 --- a/include/utils/type234_parser.hpp +++ b/include/utils/type234_parser.hpp @@ -91,7 +91,7 @@ template class Type234Parser { // Is this a type 4 element? if (allowed_type4_elements_.count(element_identifier)) { const auto repeated_elements = data.take<6>(); - const auto element_data = data.take_vector(length_indicator); + const auto element_data = data.take_vector(length_indicator - 6); if (elements.count(element_identifier)) { throw std::runtime_error("This element identifier already occured."); } From dd034b85df12decd2e388ebfb92c691a082243e0 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Fri, 5 Jul 2024 14:27:58 +0200 Subject: [PATCH 30/34] add parser for D-ATTACH/DETACH GROUP IDENTITY ACKNOWLEDGEMENT --- CMakeLists.txt | 1 + include/l3/mobile_management_packet.hpp | 58 +++++++++++++------------ src/l3/mobile_management_formatter.cpp | 51 ++++++++++++++++++++++ src/l3/mobile_management_packet.cpp | 16 +++++++ 4 files changed, 98 insertions(+), 28 deletions(-) create mode 100644 src/l3/mobile_management_formatter.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 242f952..32b0134 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,6 +28,7 @@ add_executable(tetra-decoder src/l3/circuit_mode_control_entity_packet.cpp src/l3/mobile_link_entity_formatter.cpp src/l3/mobile_link_entity_packet.cpp + src/l3/mobile_management_formatter.cpp src/l3/mobile_management_packet.cpp src/l3/short_data_service_formatter.cpp src/l3/short_data_service_packet.cpp diff --git a/include/l3/mobile_management_packet.hpp b/include/l3/mobile_management_packet.hpp index 4a48feb..d8b533f 100644 --- a/include/l3/mobile_management_packet.hpp +++ b/include/l3/mobile_management_packet.hpp @@ -253,6 +253,32 @@ constexpr auto to_string(MobileManagementDownlinkType34ElementIdentifiers type) } }; +enum class GroupIdentityAcceptReject { + kAllAttachmentDetachmentsAccepted, + kAtLeastOneAttachmentDetachmentRejected, +}; + +constexpr auto to_string(GroupIdentityAcceptReject type) -> const char* { + switch (type) { + case GroupIdentityAcceptReject::kAllAttachmentDetachmentsAccepted: + return "All attachment/detachments accepted"; + case GroupIdentityAcceptReject::kAtLeastOneAttachmentDetachmentRejected: + return "At least one attachment/detachment rejected"; + } +}; + +struct MobileManagementDownlinkAttachDetachGroupIdentityAcknowledgement { + GroupIdentityAcceptReject group_identity_accept_reject_; + Type234Parser::Map optional_elements_; + + MobileManagementDownlinkAttachDetachGroupIdentityAcknowledgement() = delete; + + explicit MobileManagementDownlinkAttachDetachGroupIdentityAcknowledgement(BitVector&); +}; + +auto operator<<(std::ostream& stream, const MobileManagementDownlinkAttachDetachGroupIdentityAcknowledgement& packet) + -> std::ostream&; + struct MobileManagementDownlinkLocationUpdateAccept { LocationUpdateAcceptType location_update_accept_type_; Address address_; @@ -268,41 +294,17 @@ struct MobileManagementDownlinkLocationUpdateAccept { explicit MobileManagementDownlinkLocationUpdateAccept(BitVector&); }; -inline auto operator<<(std::ostream& stream, const MobileManagementDownlinkLocationUpdateAccept& packet) - -> std::ostream& { - stream << "D-LOCATION UPDATE ACCEPT " << to_string(packet.location_update_accept_type_) << std::endl; - stream << " Address: " << packet.address_ << std::endl; - if (packet.subscriber_class_) { - stream << " subscriber class: " << std::bitset<16>(*packet.subscriber_class_) << std::endl; - } - if (packet.energy_saving_information_) { - stream << " energy saving information: " << std::bitset<14>(*packet.energy_saving_information_) << std::endl; - } - if (packet.scch_information_) { - stream << " scch information: " << std::bitset<4>(*packet.scch_information_) << std::endl; - } - if (packet.distribution_on_18th_frame_) { - stream << " distribution on 18th frame: " << std::bitset<2>(*packet.distribution_on_18th_frame_) << std::endl; - } - for (const auto& [key, value] : packet.optional_elements_) { - stream << " " << to_string(key) << " " << value << std::endl; - } - return stream; -}; +auto operator<<(std::ostream& stream, const MobileManagementDownlinkLocationUpdateAccept& packet) -> std::ostream&; struct MobileManagementPacket : public MobileLinkEntityPacket { MobileManagementPacketType packet_type_; std::optional downlink_location_update_accept_; + std::optional + downlink_attach_detach_group_identity_acknowledgement_; MobileManagementPacket() = delete; explicit MobileManagementPacket(const MobileLinkEntityPacket& packet); }; -inline auto operator<<(std::ostream& stream, const MobileManagementPacket& packet) -> std::ostream& { - stream << "MM " << to_string(packet.packet_type_) << std::endl; - if (packet.downlink_location_update_accept_) { - stream << *packet.downlink_location_update_accept_; - } - return stream; -}; \ No newline at end of file +auto operator<<(std::ostream& stream, const MobileManagementPacket& packet) -> std::ostream&; \ No newline at end of file diff --git a/src/l3/mobile_management_formatter.cpp b/src/l3/mobile_management_formatter.cpp new file mode 100644 index 0000000..b5b73ac --- /dev/null +++ b/src/l3/mobile_management_formatter.cpp @@ -0,0 +1,51 @@ +/* + * Copyright (C) 2024 Transit Live Mapping Solutions + * All rights reserved. + * + * Authors: + * Marenz Schmidl + */ + +#include "l3/mobile_management_packet.hpp" + +auto operator<<(std::ostream& stream, const MobileManagementDownlinkAttachDetachGroupIdentityAcknowledgement& packet) + -> std::ostream& { + stream << "D-ATTACH/DETACH GROUP IDENTITY ACKNOWLEDGEMENT: " << to_string(packet.group_identity_accept_reject_) + << std::endl; + for (const auto& [key, value] : packet.optional_elements_) { + stream << " " << to_string(key) << " " << value << std::endl; + } + return stream; +} + +auto operator<<(std::ostream& stream, const MobileManagementDownlinkLocationUpdateAccept& packet) -> std::ostream& { + stream << "D-LOCATION UPDATE ACCEPT: " << to_string(packet.location_update_accept_type_) << std::endl; + stream << " Address: " << packet.address_ << std::endl; + if (packet.subscriber_class_) { + stream << " subscriber class: " << std::bitset<16>(*packet.subscriber_class_) << std::endl; + } + if (packet.energy_saving_information_) { + stream << " energy saving information: " << std::bitset<14>(*packet.energy_saving_information_) << std::endl; + } + if (packet.scch_information_) { + stream << " scch information: " << std::bitset<4>(*packet.scch_information_) << std::endl; + } + if (packet.distribution_on_18th_frame_) { + stream << " distribution on 18th frame: " << std::bitset<2>(*packet.distribution_on_18th_frame_) << std::endl; + } + for (const auto& [key, value] : packet.optional_elements_) { + stream << " " << to_string(key) << " " << value << std::endl; + } + return stream; +} + +auto operator<<(std::ostream& stream, const MobileManagementPacket& packet) -> std::ostream& { + stream << "MM " << to_string(packet.packet_type_) << std::endl; + if (packet.downlink_location_update_accept_) { + stream << *packet.downlink_location_update_accept_; + } + if (packet.downlink_attach_detach_group_identity_acknowledgement_) { + stream << *packet.downlink_attach_detach_group_identity_acknowledgement_; + } + return stream; +} \ No newline at end of file diff --git a/src/l3/mobile_management_packet.cpp b/src/l3/mobile_management_packet.cpp index eb36a9e..d31fa25 100644 --- a/src/l3/mobile_management_packet.cpp +++ b/src/l3/mobile_management_packet.cpp @@ -54,6 +54,17 @@ struct ScchInformationAndDistributionOn18ThFrame { unsigned _BitInt(2) distribution_on_18th_frame_; }; +MobileManagementDownlinkAttachDetachGroupIdentityAcknowledgement:: + MobileManagementDownlinkAttachDetachGroupIdentityAcknowledgement(BitVector& data) + : group_identity_accept_reject_(GroupIdentityAcceptReject(data.take<1>())) { + auto reserved = data.take<1>(); + auto parser = Type234Parser( + data, {MobileManagementDownlinkType34ElementIdentifiers::kProprietary}, + {MobileManagementDownlinkType34ElementIdentifiers::kGroupIdentityDownlink, + MobileManagementDownlinkType34ElementIdentifiers::kGroupIdentitySecurityRelatedInformation}); + optional_elements_ = parser.parse_type34(data); +}; + MobileManagementDownlinkLocationUpdateAccept::MobileManagementDownlinkLocationUpdateAccept(BitVector& data) : location_update_accept_type_(LocationUpdateAcceptType(data.take<4>())) { auto parser = Type234Parser( @@ -91,4 +102,9 @@ MobileManagementPacket::MobileManagementPacket(const MobileLinkEntityPacket& pac if (packet_type_ == MobileManagementPacketType(MobileManagementDownlinkPacketType::kDLocationUpdateAccept)) { downlink_location_update_accept_ = MobileManagementDownlinkLocationUpdateAccept(data); } + if (packet_type_ == + MobileManagementPacketType(MobileManagementDownlinkPacketType::kDAttachDetachGroupIdentityAck)) { + downlink_attach_detach_group_identity_acknowledgement_ = + MobileManagementDownlinkAttachDetachGroupIdentityAcknowledgement(data); + } }; \ No newline at end of file From 4ede98f75ea4089c1f3ee4c551d867b00c404506 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Fri, 5 Jul 2024 14:28:55 +0200 Subject: [PATCH 31/34] fix bug in failed slots json converter --- src/borzoi/borzoi_converter.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/borzoi/borzoi_converter.cpp b/src/borzoi/borzoi_converter.cpp index b067892..0817918 100644 --- a/src/borzoi/borzoi_converter.cpp +++ b/src/borzoi/borzoi_converter.cpp @@ -47,7 +47,7 @@ auto BorzoiConverter::to_json(const Slots& slots) -> nlohmann::json { /// This may but should not throw. auto first_slot = slots.get_first_slot().get_logical_channel_data_and_crc(); message["first_slot_logical_channel"] = static_cast(first_slot.channel); - message["first_slot_logical_data"] = nlohmann::json::array(); + message["first_slot_data"] = nlohmann::json::array(); while (first_slot.data.bits_left()) { unsigned bit = first_slot.data.take<1>(); message["first_slot_logical_data"].push_back(bit); From 268b66ec4ca05f46a4095bbeeafc6864890d9388 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sat, 6 Jul 2024 10:54:05 +0200 Subject: [PATCH 32/34] parse cmce type3 elements for sds --- .../l3/circuit_mode_control_entity_packet.hpp | 61 ++++++++++++++++++- src/borzoi/borzoi_converter.cpp | 26 +++++++- .../circuit_mode_control_entity_formatter.cpp | 4 +- src/l3/circuit_mode_control_entity_packet.cpp | 14 ++++- 4 files changed, 98 insertions(+), 7 deletions(-) diff --git a/include/l3/circuit_mode_control_entity_packet.hpp b/include/l3/circuit_mode_control_entity_packet.hpp index 1d56b0a..e312594 100644 --- a/include/l3/circuit_mode_control_entity_packet.hpp +++ b/include/l3/circuit_mode_control_entity_packet.hpp @@ -11,6 +11,7 @@ #include "l3/mobile_link_entity_packet.hpp" #include "utils/address.hpp" #include "utils/bit_vector.hpp" +#include "utils/type234_parser.hpp" #include #include @@ -229,6 +230,62 @@ constexpr auto to_string(CircuitModeControlEntityPacketType type) -> const char* return std::visit([](auto&& arg) { return to_string(arg); }, type); } +enum class CircuitModeControlEntityType3ElementIdentifiers { + kReserved, + kDTMF, + kExternalSubsriberNumber, + kFacility, + kPollResponseAddresses, + kTemporaryAddress, + kDmMsAddress, + kReservedForAnyFutureSpecifiedType3Element7, + kReservedForAnyFutureSpecifiedType3Element8, + kReservedForAnyFutureSpecifiedType3Element9, + kReservedForAnyFutureSpecifiedType3Element10, + kReservedForAnyFutureSpecifiedType3Element11, + kReservedForAnyFutureSpecifiedType3Element12, + kReservedForAnyFutureSpecifiedType3Element13, + kReservedForAnyFutureSpecifiedType3Element14, + kProprietary, +}; + +constexpr auto to_string(CircuitModeControlEntityType3ElementIdentifiers type) -> const char* { + switch (type) { + case CircuitModeControlEntityType3ElementIdentifiers::kReserved: + return "Reserved"; + case CircuitModeControlEntityType3ElementIdentifiers::kDTMF: + return "DTMF"; + case CircuitModeControlEntityType3ElementIdentifiers::kExternalSubsriberNumber: + return "External subscriber number"; + case CircuitModeControlEntityType3ElementIdentifiers::kFacility: + return "Facility"; + case CircuitModeControlEntityType3ElementIdentifiers::kPollResponseAddresses: + return "Poll response addresses"; + case CircuitModeControlEntityType3ElementIdentifiers::kTemporaryAddress: + return "Temporary address"; + case CircuitModeControlEntityType3ElementIdentifiers::kDmMsAddress: + return "DM-MS address"; + case CircuitModeControlEntityType3ElementIdentifiers::kReservedForAnyFutureSpecifiedType3Element7: + return "Reserved for any future specified Type 3 element 7"; + case CircuitModeControlEntityType3ElementIdentifiers::kReservedForAnyFutureSpecifiedType3Element8: + return "Reserved for any future specified Type 3 element 8"; + case CircuitModeControlEntityType3ElementIdentifiers::kReservedForAnyFutureSpecifiedType3Element9: + return "Reserved for any future specified Type 3 element 9"; + case CircuitModeControlEntityType3ElementIdentifiers::kReservedForAnyFutureSpecifiedType3Element10: + return "Reserved for any future specified Type 3 element 10"; + case CircuitModeControlEntityType3ElementIdentifiers::kReservedForAnyFutureSpecifiedType3Element11: + return "Reserved for any future specified Type 3 element 11"; + case CircuitModeControlEntityType3ElementIdentifiers::kReservedForAnyFutureSpecifiedType3Element12: + return "Reserved for any future specified Type 3 element 12"; + case CircuitModeControlEntityType3ElementIdentifiers::kReservedForAnyFutureSpecifiedType3Element13: + return "Reserved for any future specified Type 3 element 13"; + case CircuitModeControlEntityType3ElementIdentifiers::kReservedForAnyFutureSpecifiedType3Element14: + return "Reserved for any future specified Type 3 element 14"; + case CircuitModeControlEntityType3ElementIdentifiers::kProprietary: + return "Proprietary"; + } +}; + struct SdsData { /// the area selection that is present in the uplink sds std::optional area_selection_; @@ -236,8 +293,8 @@ struct SdsData { Address address_; /// the sds data that is included int he cmce packet BitVector data_; - /// TODO: The "External subscriber number" and "DM-MS address" is not parsed! - BitVector unparsed_; + // This map may contain the "External subscriber number" and "DM-MS address" + Type234Parser::Map optional_elements_; private: SdsData() = default; diff --git a/src/borzoi/borzoi_converter.cpp b/src/borzoi/borzoi_converter.cpp index 0817918..81d341d 100644 --- a/src/borzoi/borzoi_converter.cpp +++ b/src/borzoi/borzoi_converter.cpp @@ -32,8 +32,30 @@ auto BorzoiConverter::to_json(ShortDataServicePacket* packet) -> nlohmann::json message["data"].push_back(bits); } message["arbitrary"] = nlohmann::json::object(); - message["arbitrary"]["bits_in_last_byte"] = data.bits_left(); - message["data"].push_back(data.take_all()); + if (data.bits_left() > 0) { + message["arbitrary"]["bits_in_last_byte"] = data.bits_left(); + message["data"].push_back(data.take_all()); + } else { + message["arbitrary"]["bits_in_last_byte"] = 8; + } + message["arbitrary"]["optional_fiels"] = nlohmann::json::object(); + for (const auto& [key, value] : packet->sds_data_->optional_elements_) { + auto& vec = message["arbitrary"]["optional_fiels"][to_string(key)]; + vec = nlohmann::json::object(); + vec["repeated_elements"] = value.repeated_elements; + vec["unparsed_bits"] = nlohmann::json::array(); + auto data = BitVector(value.unparsed_bits); + while (data.bits_left() >= 8) { + unsigned bits = data.take<8>(); + vec["unparsed_bits"].push_back(bits); + } + if (data.bits_left() > 0) { + vec["bits_in_last_byte"] = data.bits_left(); + vec["unparsed_bits"].push_back(data.take_all()); + } else { + vec["bits_in_last_byte"] = 8; + } + } message["time"] = get_time(); return message; diff --git a/src/l3/circuit_mode_control_entity_formatter.cpp b/src/l3/circuit_mode_control_entity_formatter.cpp index 0a999b2..b7a2787 100644 --- a/src/l3/circuit_mode_control_entity_formatter.cpp +++ b/src/l3/circuit_mode_control_entity_formatter.cpp @@ -14,7 +14,9 @@ auto operator<<(std::ostream& stream, const SdsData& sds) -> std::ostream& { } stream << " SDS Address: " << sds.address_ << std::endl; stream << " Data: " << sds.data_ << std::endl; - stream << " Unparsed: " << sds.unparsed_ << std::endl; + for (const auto& [key, value] : sds.optional_elements_) { + stream << " " << to_string(key) << " " << value << std::endl; + } return stream; }; diff --git a/src/l3/circuit_mode_control_entity_packet.cpp b/src/l3/circuit_mode_control_entity_packet.cpp index 9ea7b32..e139b00 100644 --- a/src/l3/circuit_mode_control_entity_packet.cpp +++ b/src/l3/circuit_mode_control_entity_packet.cpp @@ -38,7 +38,12 @@ auto SdsData::from_d_sds_data(BitVector& data) -> SdsData { break; } sds.data_ = data.take_vector(length_identifier); - sds.unparsed_ = data.take_vector(data.bits_left()); + auto parser = Type234Parser( + data, + {CircuitModeControlEntityType3ElementIdentifiers::kExternalSubsriberNumber, + CircuitModeControlEntityType3ElementIdentifiers::kDmMsAddress}, + {}); + sds.optional_elements_ = parser.parse_type34(data); return sds; } @@ -76,7 +81,12 @@ auto SdsData::from_u_sds_data(BitVector& data) -> SdsData { break; } sds.data_ = data.take_vector(length_identifier); - sds.unparsed_ = data.take_vector(data.bits_left()); + auto parser = Type234Parser( + data, + {CircuitModeControlEntityType3ElementIdentifiers::kExternalSubsriberNumber, + CircuitModeControlEntityType3ElementIdentifiers::kDmMsAddress}, + {}); + sds.optional_elements_ = parser.parse_type34(data); return sds; } From 2daf2a989268089bf7c2fd63f070de8816bbde06 Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Sat, 6 Jul 2024 10:56:16 +0200 Subject: [PATCH 33/34] fix typo --- src/borzoi/borzoi_converter.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/borzoi/borzoi_converter.cpp b/src/borzoi/borzoi_converter.cpp index 81d341d..5f2f011 100644 --- a/src/borzoi/borzoi_converter.cpp +++ b/src/borzoi/borzoi_converter.cpp @@ -38,9 +38,9 @@ auto BorzoiConverter::to_json(ShortDataServicePacket* packet) -> nlohmann::json } else { message["arbitrary"]["bits_in_last_byte"] = 8; } - message["arbitrary"]["optional_fiels"] = nlohmann::json::object(); + message["arbitrary"]["optional_fields"] = nlohmann::json::object(); for (const auto& [key, value] : packet->sds_data_->optional_elements_) { - auto& vec = message["arbitrary"]["optional_fiels"][to_string(key)]; + auto& vec = message["arbitrary"]["optional_fields"][to_string(key)]; vec = nlohmann::json::object(); vec["repeated_elements"] = value.repeated_elements; vec["unparsed_bits"] = nlohmann::json::array(); From 9c39215caca5a106909672109e9ee0022e15b96c Mon Sep 17 00:00:00 2001 From: Markus Schmidl Date: Mon, 8 Jul 2024 17:57:24 +0200 Subject: [PATCH 34/34] review comments --- include/l2/logical_link_control_packet.hpp | 1 + include/l2/logical_link_control_parser.hpp | 2 +- include/l3/circuit_mode_control_entity_parser.hpp | 2 +- include/l3/mobile_link_entity_parser.hpp | 2 +- include/l3/mobile_management_parser.hpp | 2 +- include/l3/short_data_service_parser.hpp | 2 +- include/utils/packet_parser.hpp | 2 +- include/utils/type234_parser.hpp | 2 ++ 8 files changed, 9 insertions(+), 6 deletions(-) diff --git a/include/l2/logical_link_control_packet.hpp b/include/l2/logical_link_control_packet.hpp index 17833cb..d4e3dec 100644 --- a/include/l2/logical_link_control_packet.hpp +++ b/include/l2/logical_link_control_packet.hpp @@ -62,6 +62,7 @@ auto operator<<(std::ostream& stream, const BasicLinkInformation& bli) -> std::o /// The packet that is parsed in the logical link control layer. Currently we only implement basic link. struct LogicalLinkControlPacket : public UpperMacCPlaneSignallingPacket { std::optional basic_link_information_; + /// The data that is passed from the Logical Link Control layer to the Mobile Link Entity BitVector tl_sdu_; LogicalLinkControlPacket() = delete; diff --git a/include/l2/logical_link_control_parser.hpp b/include/l2/logical_link_control_parser.hpp index 3f45fdf..5f22aa6 100644 --- a/include/l2/logical_link_control_parser.hpp +++ b/include/l2/logical_link_control_parser.hpp @@ -54,7 +54,7 @@ class LogicalLinkControlParser : public PacketParser std::string override { + [[nodiscard]] auto packet_name(const LogicalLinkControlPacket& packet) const -> std::string override { auto pdu_type = packet.tm_sdu_->look<4>(0); if (pdu_type == kSupplementaryLlcPdu) { diff --git a/include/l3/circuit_mode_control_entity_parser.hpp b/include/l3/circuit_mode_control_entity_parser.hpp index c2fdfd7..bb0f884 100644 --- a/include/l3/circuit_mode_control_entity_parser.hpp +++ b/include/l3/circuit_mode_control_entity_parser.hpp @@ -19,7 +19,7 @@ class CircuitModeControlEntityParser : public PacketParser std::string override { + [[nodiscard]] auto packet_name(const CircuitModeControlEntityPacket& packet) const -> std::string override { return to_string(packet.packet_type_); }; diff --git a/include/l3/mobile_link_entity_parser.hpp b/include/l3/mobile_link_entity_parser.hpp index 9ef050b..d3fd014 100644 --- a/include/l3/mobile_link_entity_parser.hpp +++ b/include/l3/mobile_link_entity_parser.hpp @@ -41,7 +41,7 @@ class MobileLinkEntityParser : public PacketParser std::string override { + [[nodiscard]] auto packet_name(const MobileLinkEntityPacket& packet) const -> std::string override { if (packet.mle_protocol_ == MobileLinkEntityProtocolDiscriminator::kMleProtocol) { auto pdu_type = packet.sdu_.look<3>(0); diff --git a/include/l3/mobile_management_parser.hpp b/include/l3/mobile_management_parser.hpp index a7d0b90..3de3782 100644 --- a/include/l3/mobile_management_parser.hpp +++ b/include/l3/mobile_management_parser.hpp @@ -19,7 +19,7 @@ class MobileManagementParser : public PacketParser std::string override { + [[nodiscard]] auto packet_name(const MobileManagementPacket& packet) const -> std::string override { return to_string(packet.packet_type_); } diff --git a/include/l3/short_data_service_parser.hpp b/include/l3/short_data_service_parser.hpp index af6762c..4abd864 100644 --- a/include/l3/short_data_service_parser.hpp +++ b/include/l3/short_data_service_parser.hpp @@ -63,7 +63,7 @@ class ShortDataServiceParser : public PacketParser std::string override { + [[nodiscard]] auto packet_name(const ShortDataServicePacket& packet) const -> std::string override { return sds_pdu_description_.at(packet.protocol_identifier_); } diff --git a/include/utils/packet_parser.hpp b/include/utils/packet_parser.hpp index 1c54da5..e2bc719 100644 --- a/include/utils/packet_parser.hpp +++ b/include/utils/packet_parser.hpp @@ -47,7 +47,7 @@ template class PacketParser { protected: /// This function needs to be implemented for each parsing layer. It should return the correct packet name. - virtual auto packet_name(const Output&) -> std::string = 0; + [[nodiscard]] virtual auto packet_name(const Output&) const -> std::string = 0; /// This function take the currently parsed packet and should forward it to the next parsing stage or return a /// unique pointer to it. diff --git a/include/utils/type234_parser.hpp b/include/utils/type234_parser.hpp index fe6bd73..696b0e2 100644 --- a/include/utils/type234_parser.hpp +++ b/include/utils/type234_parser.hpp @@ -91,6 +91,8 @@ template class Type234Parser { // Is this a type 4 element? if (allowed_type4_elements_.count(element_identifier)) { const auto repeated_elements = data.take<6>(); + // The length_indicator is the "Total length of the following type 4 Elements in bits (including the + // Number of repeated elements)" const auto element_data = data.take_vector(length_indicator - 6); if (elements.count(element_identifier)) { throw std::runtime_error("This element identifier already occured.");