From 5ebabd30475b5f610b7bd16371b02c1d03705d24 Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 20 Dec 2024 09:58:44 +0100 Subject: [PATCH 001/107] mac: increase uci ring size to account for TSAN delays --- lib/du/du_high/test_mode/mac_test_mode_adapter.cpp | 1 - lib/mac/mac_sched/uci_cell_decoder.cpp | 14 +++++++++++++- lib/mac/mac_sched/uci_cell_decoder.h | 6 +----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp b/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp index dc025c8160..04f62b5616 100644 --- a/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp +++ b/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp @@ -13,7 +13,6 @@ #include "mac_test_mode_helpers.h" #include "srsran/adt/ring_buffer.h" #include "srsran/mac/mac_factory.h" -#include "srsran/ran/csi_report/csi_report_on_pucch_helpers.h" #include "srsran/scheduler/harq_id.h" #include "srsran/scheduler/resource_grid_util.h" #include "srsran/scheduler/result/sched_result.h" diff --git a/lib/mac/mac_sched/uci_cell_decoder.cpp b/lib/mac/mac_sched/uci_cell_decoder.cpp index ae931d789e..1509fbe0b7 100644 --- a/lib/mac/mac_sched/uci_cell_decoder.cpp +++ b/lib/mac/mac_sched/uci_cell_decoder.cpp @@ -10,11 +10,23 @@ #include "uci_cell_decoder.h" #include "srsran/ran/csi_report/csi_report_on_pucch_helpers.h" +#include "srsran/scheduler/resource_grid_util.h" #include "srsran/scheduler/result/pucch_info.h" #include "srsran/scheduler/result/pusch_info.h" using namespace srsran; +/// \brief Size, in number of slots, of the ring buffer used to store the pending UCIs to be decoded. This size +/// should account for potential latencies in the PHY in forwarding the decoded UCI to the MAC. +static size_t get_ring_size(const sched_cell_configuration_request_message& cell_cfg) +{ + // Estimation of the time it takes the UL lower-layers to process and forward CRC/UCI indications. + constexpr static unsigned MAX_UL_PHY_DELAY = 40; + // Note: The history ring size has to be a multiple of the TDD frame size in slots. + // Number of slots managed by this container. + return get_allocator_ring_size_gt_min(get_max_slot_ul_alloc_delay(cell_cfg.ntn_cs_koffset) + MAX_UL_PHY_DELAY); +} + uci_cell_decoder::uci_cell_decoder(const sched_cell_configuration_request_message& cell_cfg, const du_rnti_table& rnti_table_, rlf_detector& rlf_hdlr_) : @@ -22,7 +34,7 @@ uci_cell_decoder::uci_cell_decoder(const sched_cell_configuration_request_messag cell_index(cell_cfg.cell_index), rlf_handler(rlf_hdlr_), logger(srslog::fetch_basic_logger("MAC")), - expected_uci_report_grid(MAX_GRID_SIZE) + expected_uci_report_grid(get_ring_size(cell_cfg)) { } diff --git a/lib/mac/mac_sched/uci_cell_decoder.h b/lib/mac/mac_sched/uci_cell_decoder.h index 8ef58aafe2..ce6c763e90 100644 --- a/lib/mac/mac_sched/uci_cell_decoder.h +++ b/lib/mac/mac_sched/uci_cell_decoder.h @@ -26,10 +26,6 @@ using du_rnti_table = rnti_value_table Date: Fri, 20 Dec 2024 13:37:45 +0000 Subject: [PATCH 002/107] ofh: revise log messages --- .../ofh/ru_emulator_dpdk_transceiver.cpp | 4 +- include/srsran/ofh/ecpri/ecpri_factories.h | 6 +- .../dpdk/dpdk_ethernet_port_context.h | 15 +++- .../srsran/ofh/ethernet/ethernet_factories.h | 2 +- .../srsran/ofh/serdes/ofh_serdes_factories.h | 2 + lib/ofh/ecpri/ecpri_factories.cpp | 8 +- lib/ofh/ecpri/ecpri_packet_decoder_impl.cpp | 28 ++++--- lib/ofh/ecpri/ecpri_packet_decoder_impl.h | 13 ++- .../dpdk/dpdk_ethernet_port_context.cpp | 17 ++-- .../ethernet/dpdk/dpdk_ethernet_receiver.cpp | 10 +-- .../dpdk/dpdk_ethernet_transmitter.cpp | 22 +++-- lib/ofh/ethernet/ethernet_factories.cpp | 5 +- lib/ofh/ethernet/ethernet_receiver_impl.cpp | 21 ++--- .../ethernet/ethernet_transmitter_impl.cpp | 19 +++-- .../vlan_ethernet_frame_decoder_impl.cpp | 9 +- .../vlan_ethernet_frame_decoder_impl.h | 5 +- .../receiver/ofh_closed_rx_window_handler.cpp | 26 ++++-- .../receiver/ofh_closed_rx_window_handler.h | 3 + .../ofh_data_flow_uplane_uplink_data_impl.cpp | 30 ++++--- .../ofh_data_flow_uplane_uplink_data_impl.h | 3 + ...ofh_data_flow_uplane_uplink_prach_impl.cpp | 49 ++++++----- .../ofh_data_flow_uplane_uplink_prach_impl.h | 3 + lib/ofh/receiver/ofh_message_receiver.cpp | 48 +++++++---- lib/ofh/receiver/ofh_message_receiver.h | 3 + .../ofh_message_receiver_task_dispatcher.h | 7 +- lib/ofh/receiver/ofh_receiver_factories.cpp | 10 ++- lib/ofh/receiver/ofh_receiver_impl.cpp | 4 +- ...h_uplane_prach_symbol_data_flow_writer.cpp | 18 ++-- ...ofh_uplane_prach_symbol_data_flow_writer.h | 3 + .../ofh_uplane_rx_symbol_data_flow_writer.cpp | 26 +++--- .../ofh_uplane_rx_symbol_data_flow_writer.h | 7 +- lib/ofh/serdes/ofh_serdes_factories.cpp | 6 +- ...ssage_decoder_dynamic_compression_impl.cpp | 8 +- ...message_decoder_dynamic_compression_impl.h | 13 +-- .../ofh_uplane_message_decoder_impl.cpp | 82 +++++++++++-------- .../serdes/ofh_uplane_message_decoder_impl.h | 5 +- ...essage_decoder_static_compression_impl.cpp | 3 +- ..._message_decoder_static_compression_impl.h | 13 +-- ...a_flow_cplane_scheduling_commands_impl.cpp | 28 ++++--- ...ata_flow_cplane_scheduling_commands_impl.h | 3 + ...lane_scheduling_commands_task_dispatcher.h | 14 ++-- ...fh_data_flow_uplane_downlink_data_impl.cpp | 18 ++-- .../ofh_data_flow_uplane_downlink_data_impl.h | 3 + ...ata_flow_uplane_downlink_task_dispatcher.h | 10 ++- .../ofh_downlink_handler_broadcast_impl.cpp | 10 ++- .../transmitter/ofh_downlink_handler_impl.cpp | 10 ++- .../transmitter/ofh_transmitter_factories.cpp | 10 ++- lib/ofh/transmitter/ofh_tx_window_checker.h | 8 +- ...h_uplink_request_handler_task_dispatcher.h | 12 ++- .../ecpri/ecpri_packet_decoder_impl_test.cpp | 12 +-- .../vlan_ethernet_frame_decoder_test.cpp | 4 +- ...h_uplane_prach_data_flow_notifier_test.cpp | 72 ++++++++++------ ...ane_prach_symbol_data_flow_writer_test.cpp | 2 +- ...uplane_rx_symbol_data_flow_writer_test.cpp | 2 +- ...plane_packet_decoder_dynamic_impl_test.cpp | 20 +++++ ...uplane_packet_decoder_static_impl_test.cpp | 21 +++++ 56 files changed, 531 insertions(+), 284 deletions(-) diff --git a/apps/examples/ofh/ru_emulator_dpdk_transceiver.cpp b/apps/examples/ofh/ru_emulator_dpdk_transceiver.cpp index c5062f13d7..e870446d21 100644 --- a/apps/examples/ofh/ru_emulator_dpdk_transceiver.cpp +++ b/apps/examples/ofh/ru_emulator_dpdk_transceiver.cpp @@ -71,7 +71,7 @@ void dpdk_transceiver::receive_loop() void dpdk_transceiver::receive() { std::array<::rte_mbuf*, MAX_BURST_SIZE> mbufs; - unsigned num_frames = ::rte_eth_rx_burst(port_ctx.get_port_id(), 0, mbufs.data(), MAX_BURST_SIZE); + unsigned num_frames = ::rte_eth_rx_burst(port_ctx.get_dpdk_port_id(), 0, mbufs.data(), MAX_BURST_SIZE); if (num_frames == 0) { std::this_thread::sleep_for(std::chrono::microseconds(1)); return; @@ -116,7 +116,7 @@ void dpdk_transceiver::send(span> frames) std::memcpy(data, frame.data(), frame.size()); } - unsigned nof_sent_packets = ::rte_eth_tx_burst(port_ctx.get_port_id(), 0, mbufs.data(), mbufs.size()); + unsigned nof_sent_packets = ::rte_eth_tx_burst(port_ctx.get_dpdk_port_id(), 0, mbufs.data(), mbufs.size()); if (SRSRAN_UNLIKELY(nof_sent_packets < mbufs.size())) { logger.warning("DPDK dropped '{}' packets out of a total of '{}' in the tx burst", diff --git a/include/srsran/ofh/ecpri/ecpri_factories.h b/include/srsran/ofh/ecpri/ecpri_factories.h index c58789f3a3..3a7f39a4ca 100644 --- a/include/srsran/ofh/ecpri/ecpri_factories.h +++ b/include/srsran/ofh/ecpri/ecpri_factories.h @@ -22,10 +22,12 @@ namespace ecpri { std::unique_ptr create_ecpri_packet_builder(); /// Creates and returns an eCPRI packet decoder utilizing payload size encoded in eCPRI header. -std::unique_ptr create_ecpri_packet_decoder_using_payload_size(srslog::basic_logger& logger); +std::unique_ptr create_ecpri_packet_decoder_using_payload_size(srslog::basic_logger& logger, + unsigned sector); /// Creates and returns an eCPRI packet decoder ignoring payload size encoded in eCPRI header. -std::unique_ptr create_ecpri_packet_decoder_ignoring_payload_size(srslog::basic_logger& logger); +std::unique_ptr create_ecpri_packet_decoder_ignoring_payload_size(srslog::basic_logger& logger, + unsigned sector); } // namespace ecpri } // namespace srsran diff --git a/include/srsran/ofh/ethernet/dpdk/dpdk_ethernet_port_context.h b/include/srsran/ofh/ethernet/dpdk/dpdk_ethernet_port_context.h index 2e8357dfbe..fc02abc1a0 100644 --- a/include/srsran/ofh/ethernet/dpdk/dpdk_ethernet_port_context.h +++ b/include/srsran/ofh/ethernet/dpdk/dpdk_ethernet_port_context.h @@ -39,7 +39,10 @@ struct dpdk_port_config { /// Encapsulates and manages the lifetime of the internal DPDK resources of an Ethernet port. class dpdk_port_context { - dpdk_port_context(unsigned port_id_, ::rte_mempool* mem_pool_) : port_id(port_id_), mem_pool(mem_pool_) {} + dpdk_port_context(const std::string& port_id_, unsigned dpdk_port_id_, ::rte_mempool* mem_pool_) : + port_id(port_id_), dpdk_port_id(dpdk_port_id_), mem_pool(mem_pool_) + { + } public: /// Creates and initializes a new DPDK port context with the given configuration. @@ -47,8 +50,11 @@ class dpdk_port_context ~dpdk_port_context(); - /// Returns the port identifier of this context. - unsigned get_port_id() const { return port_id; } + /// Returns the DPDK port identifier of this context . + unsigned get_dpdk_port_id() const { return dpdk_port_id; } + + /// Returns the string port identifier of this context. + const std::string& get_port_id() const { return port_id; } /// Returns the mbuf memory pool of this context. ::rte_mempool* get_mempool() { return mem_pool; } @@ -57,7 +63,8 @@ class dpdk_port_context const ::rte_mempool* get_mempool() const { return mem_pool; } private: - const unsigned port_id; + const std::string port_id; + const unsigned dpdk_port_id; ::rte_mempool* const mem_pool; }; diff --git a/include/srsran/ofh/ethernet/ethernet_factories.h b/include/srsran/ofh/ethernet/ethernet_factories.h index 2a3a8a961a..3e36ad7c9b 100644 --- a/include/srsran/ofh/ethernet/ethernet_factories.h +++ b/include/srsran/ofh/ethernet/ethernet_factories.h @@ -42,7 +42,7 @@ std::unique_ptr create_vlan_frame_builder(const vlan_frame_params std::unique_ptr create_frame_builder(const vlan_frame_params& eth_params); /// Creates an Ethernet VLAN frame decoder. -std::unique_ptr create_vlan_frame_decoder(srslog::basic_logger& logger); +std::unique_ptr create_vlan_frame_decoder(srslog::basic_logger& logger, unsigned sector_id); } // namespace ether } // namespace srsran diff --git a/include/srsran/ofh/serdes/ofh_serdes_factories.h b/include/srsran/ofh/serdes/ofh_serdes_factories.h index f726dceef7..e62bea732a 100644 --- a/include/srsran/ofh/serdes/ofh_serdes_factories.h +++ b/include/srsran/ofh/serdes/ofh_serdes_factories.h @@ -42,6 +42,7 @@ create_static_compr_method_ofh_user_plane_packet_decoder(srslog::basic_logger& subcarrier_spacing scs, cyclic_prefix cp, unsigned ru_nof_prbs, + unsigned sector_id_, std::unique_ptr decompressor, const ru_compression_params& compr_params); @@ -51,6 +52,7 @@ create_dynamic_compr_method_ofh_user_plane_packet_decoder(srslog::basic_logger& subcarrier_spacing scs, cyclic_prefix cp, unsigned ru_nof_prbs, + unsigned sector_id_, std::unique_ptr decompressor); } // namespace ofh diff --git a/lib/ofh/ecpri/ecpri_factories.cpp b/lib/ofh/ecpri/ecpri_factories.cpp index 26455752d1..67ea63712b 100644 --- a/lib/ofh/ecpri/ecpri_factories.cpp +++ b/lib/ofh/ecpri/ecpri_factories.cpp @@ -21,13 +21,13 @@ std::unique_ptr srsran::ecpri::create_ecpri_packet_builder() } std::unique_ptr -srsran::ecpri::create_ecpri_packet_decoder_using_payload_size(srslog::basic_logger& logger) +srsran::ecpri::create_ecpri_packet_decoder_using_payload_size(srslog::basic_logger& logger, unsigned sector) { - return std::make_unique(logger); + return std::make_unique(logger, sector); } std::unique_ptr -srsran::ecpri::create_ecpri_packet_decoder_ignoring_payload_size(srslog::basic_logger& logger) +srsran::ecpri::create_ecpri_packet_decoder_ignoring_payload_size(srslog::basic_logger& logger, unsigned sector) { - return std::make_unique(logger); + return std::make_unique(logger, sector); } diff --git a/lib/ofh/ecpri/ecpri_packet_decoder_impl.cpp b/lib/ofh/ecpri/ecpri_packet_decoder_impl.cpp index f543e04b89..4eb3f933eb 100644 --- a/lib/ofh/ecpri/ecpri_packet_decoder_impl.cpp +++ b/lib/ofh/ecpri/ecpri_packet_decoder_impl.cpp @@ -28,17 +28,19 @@ static void deserialize_header(ofh::network_order_binary_deserializer& deseriali } /// Checks if the given eCPRI common header is valid. -static bool is_header_valid(const common_header& header, srslog::basic_logger& logger) +static bool is_header_valid(const common_header& header, unsigned sector, srslog::basic_logger& logger) { if (header.revision != ECPRI_PROTOCOL_REVISION) { - logger.info("Dropped received eCPRI packet as the detected eCPRI protocol revision '{}' is not supported", - header.revision); + logger.info( + "Sector #{}: dropped received eCPRI packet as the detected eCPRI protocol revision '{}' is not supported", + sector, + header.revision); return false; } if (!header.is_last_packet) { - logger.info("Dropped received eCPRI packet as concatenation is not supported"); + logger.info("Sector #{}: dropped received eCPRI packet as concatenation is not supported", sector); return false; } @@ -82,8 +84,9 @@ span packet_decoder_impl::decode_header(span { // Sanity size check. if (units::bytes(packet.size()) < ECPRI_COMMON_HEADER_SIZE) { - logger.info("Dropped received eCPRI packet as its size is '{}' bytes which is smaller than the eCPRI common header " - "size which is '{}' bytes", + logger.info("Sector #{}: dropped received eCPRI packet as its size is '{}' bytes which is smaller than the eCPRI " + "common header size which is '{}' bytes", + sector, packet.size(), ECPRI_COMMON_HEADER_SIZE); @@ -93,7 +96,7 @@ span packet_decoder_impl::decode_header(span ofh::network_order_binary_deserializer deserializer(packet); deserialize_header(deserializer, params.header); - if (!is_header_valid(params.header, logger)) { + if (!is_header_valid(params.header, sector, logger)) { return {}; } @@ -104,8 +107,9 @@ span packet_decoder_use_header_payload_size::decode_payload(span< packet_parameters& params) { if (params.header.payload_size > units::bytes(packet.size())) { - logger.info("Dropped received eCPRI packet as its size is '{}' bytes and the payload size field in the header is " - "set to '{}' bytes", + logger.info("Sector #{}: dropped received eCPRI packet as its size is '{}' bytes and the payload size field in the " + "header is set to '{}' bytes", + sector, packet.size(), params.header.payload_size); @@ -124,7 +128,8 @@ span packet_decoder_use_header_payload_size::decode_payload(span< return packet.subspan(deserializer.get_offset(), (params.header.payload_size - ECPRI_REALTIME_CONTROL_PACKET_FIELDS_SIZE).value()); default: - logger.warning("Dropped received eCPRI packet as its type value '{}' is not supported", + logger.warning("Sector #{}: dropped received eCPRI packet as its type value '{}' is not supported", + sector, static_cast(params.header.msg_type)); break; } @@ -145,7 +150,8 @@ span packet_decoder_ignore_header_payload_size::decode_payload(sp params.type_params = deserialize_rt_control_parameters(deserializer); return packet.subspan(deserializer.get_offset(), deserializer.remaining_bytes()); default: - logger.warning("Dropped received eCPRI packet as its type value '{}' is not supported", + logger.warning("Sector #{}: dropped received eCPRI packet as its type value '{}' is not supported", + sector, static_cast(params.header.msg_type)); break; } diff --git a/lib/ofh/ecpri/ecpri_packet_decoder_impl.h b/lib/ofh/ecpri/ecpri_packet_decoder_impl.h index 0028c5e57c..e279184fd6 100644 --- a/lib/ofh/ecpri/ecpri_packet_decoder_impl.h +++ b/lib/ofh/ecpri/ecpri_packet_decoder_impl.h @@ -20,7 +20,7 @@ namespace ecpri { class packet_decoder_impl : public packet_decoder { public: - explicit packet_decoder_impl(srslog::basic_logger& logger_) : logger(logger_) {} + packet_decoder_impl(srslog::basic_logger& logger_, unsigned sector_) : logger(logger_), sector(sector_) {} // See interface for documentation. span decode(span packet, packet_parameters& params) override; @@ -41,13 +41,17 @@ class packet_decoder_impl : public packet_decoder protected: srslog::basic_logger& logger; + const unsigned sector; }; /// \brief eCPRI packet decoder implementation utilizing payload size encoded in a eCPRI header. class packet_decoder_use_header_payload_size : public packet_decoder_impl { public: - explicit packet_decoder_use_header_payload_size(srslog::basic_logger& logger_) : packet_decoder_impl(logger_) {} + packet_decoder_use_header_payload_size(srslog::basic_logger& logger_, unsigned sector_) : + packet_decoder_impl(logger_, sector_) + { + } private: // See interface for documentation. @@ -59,7 +63,10 @@ class packet_decoder_use_header_payload_size : public packet_decoder_impl class packet_decoder_ignore_header_payload_size : public packet_decoder_impl { public: - explicit packet_decoder_ignore_header_payload_size(srslog::basic_logger& logger_) : packet_decoder_impl(logger_) {} + packet_decoder_ignore_header_payload_size(srslog::basic_logger& logger_, unsigned sector_) : + packet_decoder_impl(logger_, sector_) + { + } private: // See interface for documentation. diff --git a/lib/ofh/ethernet/dpdk/dpdk_ethernet_port_context.cpp b/lib/ofh/ethernet/dpdk/dpdk_ethernet_port_context.cpp index 962af37840..f69c71e16f 100644 --- a/lib/ofh/ethernet/dpdk/dpdk_ethernet_port_context.cpp +++ b/lib/ofh/ethernet/dpdk/dpdk_ethernet_port_context.cpp @@ -168,28 +168,29 @@ static unsigned dpdk_port_configure(const dpdk_port_config& config, ::rte_mempoo std::shared_ptr dpdk_port_context::create(const dpdk_port_config& config) { // Create the mbuf pool only once as it is common for all ports. - static ::rte_mempool* mem_pool = []() { + static ::rte_mempool* mem_pool = [&config]() { ::rte_mempool* pool = ::rte_pktmbuf_pool_create( "OFH_MBUF_POOL", NUM_MBUFS, MBUF_CACHE_SIZE, 0, (MAX_BUFFER_SIZE + RTE_PKTMBUF_HEADROOM), ::rte_socket_id()); if (pool == nullptr) { - ::rte_exit(EXIT_FAILURE, "DPDK - Unable to create the DPDK mbuf pool\n"); + ::rte_exit(EXIT_FAILURE, "DPDK - Unable to create the DPDK mbuf pool for port '%s'\n", config.id.c_str()); } return pool; }(); - return std::shared_ptr(new dpdk_port_context(dpdk_port_configure(config, mem_pool), mem_pool)); + return std::shared_ptr( + new dpdk_port_context(config.id, dpdk_port_configure(config, mem_pool), mem_pool)); } dpdk_port_context::~dpdk_port_context() { - fmt::print("DPDK - Closing port_id '{}' ... ", port_id); - int ret = ::rte_eth_dev_stop(port_id); + fmt::print("DPDK - Closing port '{}', id = '{}' ... ", port_id, dpdk_port_id); + int ret = ::rte_eth_dev_stop(dpdk_port_id); if (ret != 0) { - fmt::print("rte_eth_dev_stop: err '{}', port_id '{}'\n", ret, port_id); + fmt::print("rte_eth_dev_stop: err '{}', port_id '{}'\n", ret, dpdk_port_id); } - ret = ::rte_eth_dev_close(port_id); + ret = ::rte_eth_dev_close(dpdk_port_id); if (ret != 0) { - fmt::print("rte_eth_dev_close: err '{}', port_id '{}'\n", rte_errno, port_id); + fmt::print("rte_eth_dev_close: err '{}', port_id '{}'\n", rte_errno, dpdk_port_id); } ::rte_mempool_free(mem_pool); diff --git a/lib/ofh/ethernet/dpdk/dpdk_ethernet_receiver.cpp b/lib/ofh/ethernet/dpdk/dpdk_ethernet_receiver.cpp index 11986bf38a..42557107b0 100644 --- a/lib/ofh/ethernet/dpdk/dpdk_ethernet_receiver.cpp +++ b/lib/ofh/ethernet/dpdk/dpdk_ethernet_receiver.cpp @@ -55,18 +55,18 @@ void dpdk_receiver_impl::start(frame_notifier& notifier_) p.set_value(); receive_loop(); })) { - report_error("Unable to start the DPDK ethernet frame receiver"); + report_error("Unable to start the DPDK ethernet frame receiver on port '{}'", port_ctx->get_port_id()); } // Block waiting for timing executor to start. fut.wait(); - logger.info("Started the DPDK ethernet frame receiver"); + logger.info("Started the DPDK ethernet frame receiver on port '{}'", port_ctx->get_port_id()); } void dpdk_receiver_impl::stop() { - logger.info("Requesting stop of the DPDK ethernet frame receiver"); + logger.info("Requesting stop of the DPDK ethernet frame receiver on port '{}'", port_ctx->get_port_id()); rx_status.store(receiver_status::stop_requested, std::memory_order_relaxed); // Wait for the receiver thread to stop. @@ -74,7 +74,7 @@ void dpdk_receiver_impl::stop() std::this_thread::sleep_for(std::chrono::milliseconds(1)); } - logger.info("Stopped the DPDK ethernet frame receiver"); + logger.info("Stopped the DPDK ethernet frame receiver on port '{}'", port_ctx->get_port_id()); } void dpdk_receiver_impl::receive_loop() @@ -97,7 +97,7 @@ void dpdk_receiver_impl::receive() std::array<::rte_mbuf*, MAX_BURST_SIZE> mbufs; trace_point dpdk_rx_tp = ofh_tracer.now(); - unsigned num_frames = ::rte_eth_rx_burst(port_ctx->get_port_id(), 0, mbufs.data(), MAX_BURST_SIZE); + unsigned num_frames = ::rte_eth_rx_burst(port_ctx->get_dpdk_port_id(), 0, mbufs.data(), MAX_BURST_SIZE); if (num_frames == 0) { ofh_tracer << instant_trace_event("ofh_receiver_wait_data", instant_trace_event::cpu_scope::thread); std::this_thread::sleep_for(std::chrono::microseconds(5)); diff --git a/lib/ofh/ethernet/dpdk/dpdk_ethernet_transmitter.cpp b/lib/ofh/ethernet/dpdk/dpdk_ethernet_transmitter.cpp index 790007acb9..546c097a6e 100644 --- a/lib/ofh/ethernet/dpdk/dpdk_ethernet_transmitter.cpp +++ b/lib/ofh/ethernet/dpdk/dpdk_ethernet_transmitter.cpp @@ -25,8 +25,10 @@ void dpdk_transmitter_impl::send(span> frames) static_vector<::rte_mbuf*, MAX_BURST_SIZE> mbufs(frame_burst.size()); if (::rte_pktmbuf_alloc_bulk(port_ctx->get_mempool(), mbufs.data(), frame_burst.size()) < 0) { - logger.warning("Not enough entries in the mempool to send '{}' frames in the DPDK Ethernet transmitter", - frame_burst.size()); + logger.warning("Not enough entries in the mempool to send '{}' frames to the NIC port '{}' in the DPDK Ethernet " + "transmitter ", + frame_burst.size(), + port_ctx->get_port_id()); return; } @@ -36,8 +38,10 @@ void dpdk_transmitter_impl::send(span> frames) if (::rte_pktmbuf_append(mbuf, frame.size()) == nullptr) { ::rte_pktmbuf_free(mbuf); - logger.warning("Unable to append '{}' bytes to the allocated mbuf in the DPDK Ethernet transmitter", - frame.size()); + logger.warning("Unable to append '{}' bytes to the allocated mbuf associated with the NIC port '{}' in the " + "DPDK Ethernet transmitter", + frame.size(), + port_ctx->get_port_id()); ::rte_pktmbuf_free_bulk(mbufs.data(), mbufs.size()); return; } @@ -48,12 +52,14 @@ void dpdk_transmitter_impl::send(span> frames) std::memcpy(data, frame.data(), frame.size()); } - unsigned nof_sent_packets = ::rte_eth_tx_burst(port_ctx->get_port_id(), 0, mbufs.data(), mbufs.size()); + unsigned nof_sent_packets = ::rte_eth_tx_burst(port_ctx->get_dpdk_port_id(), 0, mbufs.data(), mbufs.size()); if (SRSRAN_UNLIKELY(nof_sent_packets < mbufs.size())) { - logger.warning("DPDK dropped '{}' packets out of a total of '{}' in the tx burst", - mbufs.size() - nof_sent_packets, - mbufs.size()); + logger.warning( + "DPDK dropped '{}' packets out of a total of '{}' in the tx burst while sending data to the NIC port '{}'", + mbufs.size() - nof_sent_packets, + mbufs.size(), + port_ctx->get_port_id()); for (unsigned buf_idx = nof_sent_packets, last_idx = mbufs.size(); buf_idx != last_idx; ++buf_idx) { ::rte_pktmbuf_free(mbufs[buf_idx]); } diff --git a/lib/ofh/ethernet/ethernet_factories.cpp b/lib/ofh/ethernet/ethernet_factories.cpp index 4f57e31781..5a5ec621c5 100644 --- a/lib/ofh/ethernet/ethernet_factories.cpp +++ b/lib/ofh/ethernet/ethernet_factories.cpp @@ -41,7 +41,8 @@ std::unique_ptr srsran::ether::create_frame_builder(const vlan_fr return std::make_unique(eth_params); } -std::unique_ptr srsran::ether::create_vlan_frame_decoder(srslog::basic_logger& logger) +std::unique_ptr srsran::ether::create_vlan_frame_decoder(srslog::basic_logger& logger, + unsigned sector_id) { - return std::make_unique(logger); + return std::make_unique(logger, sector_id); } diff --git a/lib/ofh/ethernet/ethernet_receiver_impl.cpp b/lib/ofh/ethernet/ethernet_receiver_impl.cpp index 784c40a46e..fcf3df5a67 100644 --- a/lib/ofh/ethernet/ethernet_receiver_impl.cpp +++ b/lib/ofh/ethernet/ethernet_receiver_impl.cpp @@ -60,20 +60,21 @@ receiver_impl::receiver_impl(const std::string& interface, ::ifreq if_opts; ::strncpy(if_opts.ifr_name, interface.c_str(), IFNAMSIZ - 1); if (::ioctl(socket_fd, SIOCGIFFLAGS, &if_opts) < 0) { - report_error("Unable to get flags for NIC interface in the Ethernet receiver"); + report_error("Unable to get flags for NIC interface '{}' in the Ethernet receiver", interface); } if_opts.ifr_flags |= IFF_PROMISC; if (::ioctl(socket_fd, SIOCSIFFLAGS, &if_opts) < 0) { - report_error("Unable to set flags for NIC interface in the Ethernet receiver"); + report_error("Unable to set flags for NIC interface '{}' in the Ethernet receiver", interface); } } // Bind to device. if (::setsockopt(socket_fd, SOL_SOCKET, SO_BINDTODEVICE, interface.c_str(), interface.size()) == -1) { - report_error("Unable to bind socket in Ethernet receiver"); + report_error("Unable to bind socket to the NIC interface '{}' in Ethernet receiver", interface); } - logger.info("Opened successfully the NIC interface '{}' used by the Ethernet receiver", interface); + logger.info( + "Opened successfully the NIC interface '{}' (fd = '{}') used by the Ethernet receiver", interface, socket_fd); } receiver_impl::~receiver_impl() @@ -96,18 +97,18 @@ void receiver_impl::start(frame_notifier& notifier_) p.set_value(); receive_loop(); })) { - report_error("Unable to start the ethernet frame receiver"); + report_error("Unable to start the ethernet frame receiver, fd = '{}'", socket_fd); } // Block waiting for timing executor to start. fut.wait(); - logger.info("Started the ethernet frame receiver"); + logger.info("Started the ethernet frame receiver with fd = '{}'", socket_fd); } void receiver_impl::stop() { - logger.info("Requesting stop of the ethernet frame receiver"); + logger.info("Requesting stop of the ethernet frame receiver with fd = '{}'", socket_fd); rx_status.store(receiver_status::stop_requested, std::memory_order_relaxed); // Wait for the receiver thread to stop. @@ -115,7 +116,7 @@ void receiver_impl::stop() std::this_thread::sleep_for(std::chrono::milliseconds(100)); } - logger.info("Stopped the ethernet frame receiver"); + logger.info("Stopped the ethernet frame receiver with fd = '{}'", socket_fd); } void receiver_impl::receive_loop() @@ -154,7 +155,7 @@ void receiver_impl::receive() auto exp_buffer = buffer_pool.reserve(); if (!exp_buffer.has_value()) { - logger.warning("No buffer is available for receiving an Ethernet packet"); + logger.warning("No buffer is available for receiving an Ethernet packet on the port bound to fd = '{}'", socket_fd); return; } ethernet_rx_buffer_impl buffer = std::move(exp_buffer.value()); @@ -162,7 +163,7 @@ void receiver_impl::receive() auto nof_bytes = ::recvfrom(socket_fd, data_span.data(), BUFFER_SIZE, 0, nullptr, nullptr); if (nof_bytes < 0) { - logger.warning("Ethernet receiver call to recvfrom failed"); + logger.warning("Ethernet receiver call to recvfrom failed, fd = '{}'", socket_fd); return; } buffer.resize(nof_bytes); diff --git a/lib/ofh/ethernet/ethernet_transmitter_impl.cpp b/lib/ofh/ethernet/ethernet_transmitter_impl.cpp index 48c5ab7e78..d9be0bdc33 100644 --- a/lib/ofh/ethernet/ethernet_transmitter_impl.cpp +++ b/lib/ofh/ethernet/ethernet_transmitter_impl.cpp @@ -41,14 +41,16 @@ transmitter_impl::transmitter_impl(const gw_config& config, srslog::basic_logger // Get the MTU size of the NIC. int current_mtu = -1; if (::ioctl(socket_fd, SIOCGIFMTU, &if_idx) < 0) { - logger.warning("Could not check MTU of the NIC interface in the Ethernet transmitter"); + logger.warning("Could not check MTU of the NIC interface '{}' in the Ethernet transmitter", config.interface); } else { current_mtu = if_idx.ifr_mtu; } - report_error("Unable to set MTU size to '{}' bytes for NIC interface in the Ethernet transmitter, current MTU size " - "set to '{}' bytes", - config.mtu_size, - current_mtu); + report_error( + "Unable to set MTU size to '{}' bytes for NIC interface '{}' in the Ethernet transmitter, current MTU size " + "set to '{}' bytes", + config.mtu_size, + config.interface, + current_mtu); } // Get the index of the NIC. @@ -62,7 +64,9 @@ transmitter_impl::transmitter_impl(const gw_config& config, srslog::basic_logger socket_address.sll_halen = ETH_ALEN; std::copy(std::begin(config.mac_dst_address), std::end(config.mac_dst_address), std::begin(socket_address.sll_addr)); - logger.info("Opened successfully the NIC interface '{}' used by the Ethernet transmitter", config.interface); + logger.info("Opened successfully the NIC interface '{}' (fd = '{}') used by the Ethernet transmitter", + config.interface, + socket_fd); } transmitter_impl::~transmitter_impl() @@ -79,8 +83,9 @@ void transmitter_impl::send(span> frames) 0, reinterpret_cast<::sockaddr*>(&socket_address), sizeof(socket_address)) < 0) { - logger.warning("Ethernet transmitter call to sendto failed as it could not transmit '{}' bytes, consider tuning " + logger.warning("Ethernet transmitter with fd = '{}' could not transmit '{}' bytes, consider tuning " "the NIC system settings to obtain higher performance or use DPDK", + socket_fd, frame.size()); } } diff --git a/lib/ofh/ethernet/vlan_ethernet_frame_decoder_impl.cpp b/lib/ofh/ethernet/vlan_ethernet_frame_decoder_impl.cpp index 6163abe334..f53352f50f 100644 --- a/lib/ofh/ethernet/vlan_ethernet_frame_decoder_impl.cpp +++ b/lib/ofh/ethernet/vlan_ethernet_frame_decoder_impl.cpp @@ -21,10 +21,11 @@ span vlan_frame_decoder_impl::decode(span frame, v { // Ethernet frames should include padding bytes up to the minimum length. if (frame.size() < MIN_ETH_LEN) { - logger.debug( - "Dropped received Ethernet frame of size '{}' bytes as it is below the minimum allowed size of '{}' bytes", - frame.size(), - MIN_ETH_LEN); + logger.debug("Sector #{}: Dropped received Ethernet frame of size '{}' bytes as it is below the minimum allowed " + "size of '{}' bytes", + sector_id, + frame.size(), + MIN_ETH_LEN); return {}; } diff --git a/lib/ofh/ethernet/vlan_ethernet_frame_decoder_impl.h b/lib/ofh/ethernet/vlan_ethernet_frame_decoder_impl.h index d890e7cae9..d1a2f7c391 100644 --- a/lib/ofh/ethernet/vlan_ethernet_frame_decoder_impl.h +++ b/lib/ofh/ethernet/vlan_ethernet_frame_decoder_impl.h @@ -20,13 +20,16 @@ namespace ether { class vlan_frame_decoder_impl : public vlan_frame_decoder { public: - explicit vlan_frame_decoder_impl(srslog::basic_logger& logger_) : logger(logger_) {} + vlan_frame_decoder_impl(srslog::basic_logger& logger_, unsigned sector_id_) : logger(logger_), sector_id(sector_id_) + { + } // See interface for documentation. span decode(span frame, vlan_frame_params& eth_params) override; private: srslog::basic_logger& logger; + const unsigned sector_id; }; } // namespace ether diff --git a/lib/ofh/receiver/ofh_closed_rx_window_handler.cpp b/lib/ofh/receiver/ofh_closed_rx_window_handler.cpp index f857e1cf4e..1e22f393f3 100644 --- a/lib/ofh/receiver/ofh_closed_rx_window_handler.cpp +++ b/lib/ofh/receiver/ofh_closed_rx_window_handler.cpp @@ -22,7 +22,8 @@ closed_rx_window_handler::closed_rx_window_handler(const closed_rx_window_handle executor(*dependencies.executor), prach_repo(std::move(dependencies.prach_repo)), uplink_repo(std::move(dependencies.uplink_repo)), - notifier(std::move(dependencies.notifier)) + notifier(std::move(dependencies.notifier)), + sector_id(config.sector) { srsran_assert(prach_repo, "Invalid PRACH context repository"); srsran_assert(uplink_repo, "Invalid uplink context repository"); @@ -35,9 +36,11 @@ void closed_rx_window_handler::on_new_symbol(slot_symbol_point symbol_point) handle_uplink_context(internal_slot); handle_prach_context(internal_slot); })) { - logger.warning("Failed to dispatch task for checking for lost messages in reception for slot '{}' and symbol '{}'", - symbol_point.get_slot(), - symbol_point.get_symbol_index()); + logger.warning( + "Sector#{}: failed to dispatch task for checking for lost messages in reception for slot '{}' and symbol '{}'", + sector_id, + symbol_point.get_slot(), + symbol_point.get_symbol_index()); } } @@ -56,13 +59,15 @@ void closed_rx_window_handler::handle_uplink_context(slot_symbol_point symbol_po notifier->on_new_uplink_symbol(notification_context, std::move(ctx_value.grid)); if (log_unreceived_messages) { - logger.warning("Missed incoming User-Plane uplink messages for slot '{}', symbol '{}' and sector#{}", + logger.warning("Sector#{}: missed incoming User-Plane uplink messages for slot '{}', symbol '{}' and sector#{}", + sector_id, ctx_value.context.slot, symbol_point.get_symbol_index(), ctx_value.context.sector); } - logger.debug("Notifying incomplete UL symbol in slot '{}', symbol '{}' for sector#{}", + logger.debug("Sector#{}: notifying incomplete UL symbol in slot '{}', symbol '{}' for sector#{}", + sector_id, notification_context.slot, notification_context.symbol, notification_context.sector); @@ -88,11 +93,14 @@ void closed_rx_window_handler::handle_prach_context(slot_symbol_point symbol_poi notifier->on_new_prach_window_data(ctx_value.context, *ctx_value.buffer); if (log_unreceived_messages) { - logger.warning("Missed incoming User-Plane PRACH messages for slot '{}' and sector#{}", + logger.warning("Sector#{}: missed incoming User-Plane PRACH messages for slot '{}' and sector#{}", + sector_id, ctx_value.context.slot, ctx_value.context.sector); } - logger.debug( - "Notifying incomplete PRACH in slot '{}' for sector#{}", ctx_value.context.slot, ctx_value.context.sector); + logger.debug("Sector#{}: notifying incomplete PRACH in slot '{}' for sector#{}", + sector_id, + ctx_value.context.slot, + ctx_value.context.sector); } diff --git a/lib/ofh/receiver/ofh_closed_rx_window_handler.h b/lib/ofh/receiver/ofh_closed_rx_window_handler.h index da240d7d57..b9944aacf2 100644 --- a/lib/ofh/receiver/ofh_closed_rx_window_handler.h +++ b/lib/ofh/receiver/ofh_closed_rx_window_handler.h @@ -26,6 +26,8 @@ namespace ofh { /// Closed reception window handler configuration. struct closed_rx_window_handler_config { + /// Radio sector identifier. + unsigned sector; /// Time in number of symbols that the decoder needs to process an Open Fronthaul message. It delays closing the /// reception window. unsigned nof_symbols_to_process_uplink = 0; @@ -79,6 +81,7 @@ class closed_rx_window_handler : public ota_symbol_boundary_notifier std::shared_ptr prach_repo; std::shared_ptr uplink_repo; std::shared_ptr notifier; + const unsigned sector_id; }; } // namespace ofh diff --git a/lib/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl.cpp b/lib/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl.cpp index 50f7f99d08..6ed17bd2c6 100644 --- a/lib/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl.cpp +++ b/lib/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl.cpp @@ -21,8 +21,9 @@ data_flow_uplane_uplink_data_impl::data_flow_uplane_uplink_data_impl( logger(*dependencies.logger), ul_cplane_context_repo(std::move(dependencies.ul_cplane_context_repo)), uplane_decoder(std::move(dependencies.uplane_decoder)), - rx_symbol_writer(config.ul_eaxc, *dependencies.logger, dependencies.ul_context_repo), - notification_sender(*dependencies.logger, dependencies.ul_context_repo, dependencies.notifier) + rx_symbol_writer(config.ul_eaxc, config.sector, *dependencies.logger, dependencies.ul_context_repo), + notification_sender(*dependencies.logger, dependencies.ul_context_repo, dependencies.notifier), + sector_id(config.sector) { srsran_assert(ul_cplane_context_repo, "Invalid control plane repository"); srsran_assert(uplane_decoder, "Invalid User-Plane decoder"); @@ -51,8 +52,9 @@ bool data_flow_uplane_uplink_data_impl::should_uplane_packet_be_filtered( const uplane_message_decoder_results& results) const { if (results.params.filter_index == filter_index_type::reserved || is_a_prach_message(results.params.filter_index)) { - logger.info("Dropped received Open Fronthaul User-Plane packet for slot '{}' and symbol '{}' as decoded filter " - "index value '{}' is not valid", + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet for slot '{}' and symbol '{}' as decoded " + "filter index value '{}' is not valid", + sector_id, results.params.slot, results.params.symbol_id, to_value(results.params.filter_index)); @@ -65,8 +67,9 @@ bool data_flow_uplane_uplink_data_impl::should_uplane_packet_be_filtered( ul_cplane_context_repo->get(params.slot, params.symbol_id, params.filter_index, eaxc); if (!ex_cp_context) { - logger.info("Dropped received Open Fronthaul User-Plane packet as no data was expected for slot '{}', symbol '{}' " - "and eAxC '{}'", + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as no data was expected for slot '{}', " + "symbol '{}' and eAxC '{}'", + sector_id, params.slot, params.symbol_id, eaxc); @@ -80,15 +83,17 @@ bool data_flow_uplane_uplink_data_impl::should_uplane_packet_be_filtered( return std::any_of( results.sections.begin(), results.sections.end(), [&cp_context, this](const uplane_section_params& up_section) { if (!up_section.is_every_rb_used) { - logger.info("Dropped received Open Fronthaul User-Plane packet as 'every other resource block is used' mode " - "is not supported"); + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as 'every other resource block is " + "used' mode is not supported", + sector_id); return true; } if (!up_section.use_current_symbol_number) { - logger.info("Dropped received Open Fronthaul User-Plane packet as 'increment the current symbol number and " - "use that' mode is not supported"); + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as 'increment the current symbol " + "number and use that' mode is not supported", + sector_id); return true; } @@ -98,8 +103,9 @@ bool data_flow_uplane_uplink_data_impl::should_uplane_packet_be_filtered( (up_section.start_prb + up_section.nof_prbs) > (cp_context.prb_start + cp_context.nof_prb)); if (is_up_section_not_found_in_cp_section) { - logger.info("Dropped received Open Fronthaul User-Plane packet as PRB index range '{}:{}' does not match the " - "expected range '{}:{}'", + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as PRB index range '{}:{}' does " + "not match the expected range '{}:{}'", + sector_id, up_section.start_prb, up_section.nof_prbs, cp_context.prb_start, diff --git a/lib/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl.h b/lib/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl.h index 2e4f1ac58f..fa52ab7901 100644 --- a/lib/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl.h +++ b/lib/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl.h @@ -24,6 +24,8 @@ namespace ofh { /// Open Fronthaul User-Plane uplink data flow implementation configuration. struct data_flow_uplane_uplink_data_impl_config { + /// Radio sector identifier. + unsigned sector; /// Uplink eAxCs. static_vector ul_eaxc; }; @@ -63,6 +65,7 @@ class data_flow_uplane_uplink_data_impl : public data_flow_uplane_uplink_data std::unique_ptr uplane_decoder; uplane_rx_symbol_data_flow_writer rx_symbol_writer; uplane_rx_symbol_data_flow_notifier notification_sender; + const unsigned sector_id; }; } // namespace ofh diff --git a/lib/ofh/receiver/ofh_data_flow_uplane_uplink_prach_impl.cpp b/lib/ofh/receiver/ofh_data_flow_uplane_uplink_prach_impl.cpp index 23d2652c1f..88ac36c4c1 100644 --- a/lib/ofh/receiver/ofh_data_flow_uplane_uplink_prach_impl.cpp +++ b/lib/ofh/receiver/ofh_data_flow_uplane_uplink_prach_impl.cpp @@ -21,8 +21,9 @@ data_flow_uplane_uplink_prach_impl::data_flow_uplane_uplink_prach_impl( is_prach_cplane_enabled(config_.is_prach_cplane_enabled), ul_cplane_context_repo(std::move(dependencies.ul_cplane_context_repo)), uplane_decoder(std::move(dependencies.uplane_decoder)), - prach_iq_writter(config_.prach_eaxcs, *dependencies.logger, dependencies.prach_context_repo), - notification_sender(*dependencies.logger, dependencies.prach_context_repo, dependencies.notifier) + prach_iq_writter(config_.prach_eaxcs, config_.sector, *dependencies.logger, dependencies.prach_context_repo), + notification_sender(*dependencies.logger, dependencies.prach_context_repo, dependencies.notifier), + sector_id(config_.sector) { srsran_assert(ul_cplane_context_repo, "Invalid Control-Plane context repository"); srsran_assert(uplane_decoder, "Invalid User-Plane decoder"); @@ -33,8 +34,9 @@ bool data_flow_uplane_uplink_prach_impl::should_uplane_packet_be_filtered( const uplane_message_decoder_results& results) const { if (!is_a_prach_message(results.params.filter_index)) { - logger.info("Dropped received Open Fronthaul User-Plane packet for slot '{}' and symbol '{}' as decoded filter " - "index value '{}' is not valid", + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet for slot '{}' and symbol '{}' as decoded " + "filter index value '{}' is not valid", + sector_id, results.params.slot, results.params.symbol_id, to_value(results.params.filter_index)); @@ -52,12 +54,12 @@ bool data_flow_uplane_uplink_prach_impl::should_uplane_packet_be_filtered( ul_cplane_context_repo->get(params.slot, params.symbol_id, params.filter_index, eaxc); if (!ex_cp_context) { - logger.info( - "Dropped received Open Fronthaul User-Plane PRACH packet as no data was expected for slot '{}', symbol '{}' " - "and eAxC '{}'", - params.slot, - params.symbol_id, - eaxc); + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane PRACH packet as no data was expected for slot " + "'{}', symbol '{}' and eAxC '{}'", + sector_id, + params.slot, + params.symbol_id, + eaxc); return true; } @@ -67,29 +69,35 @@ bool data_flow_uplane_uplink_prach_impl::should_uplane_packet_be_filtered( return std::any_of( results.sections.begin(), results.sections.end(), [&cp_context, this](const uplane_section_params& up_section) { if (up_section.start_prb > MAX_NOF_PRBS - 1) { - logger.info("Dropped received Open Fronthaul User-Plane packet as the first PRB index '{}' is not valid", - up_section.start_prb); + logger.info( + "Sector#{}: dropped received Open Fronthaul User-Plane packet as the first PRB index '{}' is not valid", + sector_id, + up_section.start_prb); return true; } if (up_section.start_prb + up_section.nof_prbs > MAX_NOF_PRBS) { - logger.info("Dropped received Open Fronthaul User-Plane packet as the last PRB index '{}' is not valid", - up_section.start_prb + up_section.nof_prbs); + logger.info( + "Sector#{}: dropped received Open Fronthaul User-Plane packet as the last PRB index '{}' is not valid", + sector_id, + up_section.start_prb + up_section.nof_prbs); return true; } if (!up_section.is_every_rb_used) { - logger.info("Dropped received Open Fronthaul User-Plane packet as 'every other resource block is used' mode " - "is not supported"); + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as 'every other resource block is " + "used' mode is not supported", + sector_id); return true; } if (!up_section.use_current_symbol_number) { - logger.info("Dropped received Open Fronthaul User-Plane packet as 'increment the current symbol number and " - "use that' mode is not supported"); + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as 'increment the current symbol " + "number and use that' mode is not supported", + sector_id); return true; } @@ -99,8 +107,9 @@ bool data_flow_uplane_uplink_prach_impl::should_uplane_packet_be_filtered( (up_section.start_prb + up_section.nof_prbs) > (cp_context.prb_start + cp_context.nof_prb); if (is_up_section_not_found_in_cp_section) { - logger.info("Dropped received Open Fronthaul User-Plane packet as PRB index range '{}:{}' does not match the " - "expected range '{}:{}'", + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as PRB index range '{}:{}' does " + "not match the expected range '{}:{}'", + sector_id, up_section.start_prb, up_section.nof_prbs, cp_context.prb_start, diff --git a/lib/ofh/receiver/ofh_data_flow_uplane_uplink_prach_impl.h b/lib/ofh/receiver/ofh_data_flow_uplane_uplink_prach_impl.h index 98c542f7a8..4d48c4950b 100644 --- a/lib/ofh/receiver/ofh_data_flow_uplane_uplink_prach_impl.h +++ b/lib/ofh/receiver/ofh_data_flow_uplane_uplink_prach_impl.h @@ -23,6 +23,8 @@ namespace ofh { /// Open Fronthaul User-Plane uplink PRACH data flow implementation configuration. struct data_flow_uplane_uplink_prach_impl_config { + /// Radio sector identifier. + unsigned sector; /// PRACH Control-Plane enabled flag. bool is_prach_cplane_enabled; /// Uplink PRACH eAxCs. @@ -65,6 +67,7 @@ class data_flow_uplane_uplink_prach_impl : public data_flow_uplane_uplink_prach std::unique_ptr uplane_decoder; uplane_prach_symbol_data_flow_writer prach_iq_writter; uplane_prach_data_flow_notifier notification_sender; + const unsigned sector_id; }; } // namespace ofh diff --git a/lib/ofh/receiver/ofh_message_receiver.cpp b/lib/ofh/receiver/ofh_message_receiver.cpp index 778199117c..91417640f7 100644 --- a/lib/ofh/receiver/ofh_message_receiver.cpp +++ b/lib/ofh/receiver/ofh_message_receiver.cpp @@ -18,6 +18,7 @@ using namespace ofh; message_receiver_impl::message_receiver_impl(const message_receiver_config& config, message_receiver_dependencies&& dependencies) : logger(*dependencies.logger), + sector_id(config.sector), nof_symbols(config.nof_symbols), scs(config.scs), vlan_params(config.vlan_params), @@ -66,18 +67,20 @@ void message_receiver_impl::process_new_frame(ether::unique_rx_buffer buffer) int nof_skipped_seq_id = seq_id_checker->update_and_compare_seq_id(eaxc, (ecpri_iq_params.seq_id >> 8)); // Drop the message when it is from the past. if (nof_skipped_seq_id < 0) { - logger.info("Dropped received Open Fronthaul User-Plane packet for eAxC value '{}' as sequence identifier field is " + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet for eAxC value '{}' as sequence " + "identifier field is " "from the past", + sector_id, eaxc); return; } if (nof_skipped_seq_id > 0) { - logger.warning("Potentially lost '{}' messages sent by the RU", nof_skipped_seq_id); + logger.warning("Sector#{}: potentially lost '{}' messages sent by the RU", sector_id, nof_skipped_seq_id); } slot_symbol_point slot_point = uplane_peeker::peek_slot_symbol_point(ofh_pdu, nof_symbols, scs); if (!slot_point.get_slot().valid()) { - logger.info("Dropped received Open Fronthaul User-Plane packet as the slot field is invalid"); + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as the slot field is invalid", sector_id); return; } @@ -88,8 +91,10 @@ void message_receiver_impl::process_new_frame(ether::unique_rx_buffer buffer) // Peek the filter index and check that it is valid. filter_index_type filter_type = uplane_peeker::peek_filter_index(ofh_pdu); if (filter_type == filter_index_type::reserved) { - logger.info("Dropped received Open Fronthaul User-Plane message as the filter index field '{}' is invalid", - to_value(filter_type)); + logger.info( + "Sector#{}: dropped received Open Fronthaul User-Plane message as the filter index field '{}' is invalid", + sector_id, + to_value(filter_type)); return; } @@ -108,7 +113,9 @@ void message_receiver_impl::process_new_frame(ether::unique_rx_buffer buffer) bool message_receiver_impl::should_ecpri_packet_be_filtered(const ecpri::packet_parameters& ecpri_params) const { if (ecpri_params.header.msg_type != ecpri::message_type::iq_data) { - logger.info("Dropped received Open Fronthaul User-Plane packet as decoded eCPRI message type is not for IQ data"); + logger.info( + "Sector#{}: dropped received Open Fronthaul User-Plane packet as decoded eCPRI message type is not for IQ data", + sector_id); return true; } @@ -116,9 +123,10 @@ bool message_receiver_impl::should_ecpri_packet_be_filtered(const ecpri::packet_ const ecpri::iq_data_parameters& ecpri_iq_params = std::get(ecpri_params.type_params); if ((std::find(ul_eaxc.begin(), ul_eaxc.end(), ecpri_iq_params.pc_id) == ul_eaxc.end()) && (std::find(ul_prach_eaxc.begin(), ul_prach_eaxc.end(), ecpri_iq_params.pc_id) == ul_prach_eaxc.end())) { - logger.info( - "Dropped received Open Fronthaul User-Plane packet as decoded eAxC value '{}' is not configured in reception", - ecpri_iq_params.pc_id); + logger.info("Sector#{}: dropped received Open Fronthaul User-Plane packet as decoded eAxC value '{}' is not " + "configured in reception", + sector_id, + ecpri_iq_params.pc_id); return true; } @@ -129,25 +137,29 @@ bool message_receiver_impl::should_ecpri_packet_be_filtered(const ecpri::packet_ bool message_receiver_impl::should_ethernet_frame_be_filtered(const ether::vlan_frame_params& eth_params) const { if (eth_params.mac_src_address != vlan_params.mac_src_address) { - logger.debug( - "Dropped received Ethernet frame as source MAC addresses do not match (detected={:02X}, expected={:02X})", - span(eth_params.mac_src_address), - span(vlan_params.mac_src_address)); + logger.debug("Sector#{}: dropped received Ethernet frame as source MAC addresses do not match (detected={:02X}, " + "expected={:02X})", + sector_id, + span(eth_params.mac_src_address), + span(vlan_params.mac_src_address)); return true; } if (eth_params.mac_dst_address != vlan_params.mac_dst_address) { - logger.debug("Dropped received Ethernet frame as destination MAC addresses do not match match (detected={:02X}, " - "expected={:02X})", - span(eth_params.mac_dst_address), - span(vlan_params.mac_dst_address)); + logger.debug( + "Sector#{}: dropped received Ethernet frame as destination MAC addresses do not match match (detected={:02X}, " + "expected={:02X})", + sector_id, + span(eth_params.mac_dst_address), + span(vlan_params.mac_dst_address)); return true; } if (eth_params.eth_type != vlan_params.eth_type) { - logger.info("Dropped received Ethernet frame as decoded Ethernet type is '{}' but expected '{}'", + logger.info("Sector#{}: dropped received Ethernet frame as decoded Ethernet type is '{}' but expected '{}'", + sector_id, eth_params.eth_type, vlan_params.eth_type); diff --git a/lib/ofh/receiver/ofh_message_receiver.h b/lib/ofh/receiver/ofh_message_receiver.h index 2e57e85e5b..bf9e22b111 100644 --- a/lib/ofh/receiver/ofh_message_receiver.h +++ b/lib/ofh/receiver/ofh_message_receiver.h @@ -30,6 +30,8 @@ class rx_window_checker; /// Message receiver configuration. struct message_receiver_config { + /// Radio sector identifier. + unsigned sector; /// Number of symbols unsigned nof_symbols; /// Subcarrier spacing. @@ -100,6 +102,7 @@ class message_receiver_impl : public message_receiver private: srslog::basic_logger& logger; + const unsigned sector_id; const unsigned nof_symbols; const subcarrier_spacing scs; const ether::vlan_frame_params vlan_params; diff --git a/lib/ofh/receiver/ofh_message_receiver_task_dispatcher.h b/lib/ofh/receiver/ofh_message_receiver_task_dispatcher.h index 81df17490b..a553e48724 100644 --- a/lib/ofh/receiver/ofh_message_receiver_task_dispatcher.h +++ b/lib/ofh/receiver/ofh_message_receiver_task_dispatcher.h @@ -21,8 +21,8 @@ namespace ofh { class ofh_message_receiver_task_dispatcher : public message_receiver { public: - ofh_message_receiver_task_dispatcher(message_receiver& msg_receiver_, task_executor& executor_) : - msg_receiver(msg_receiver_), executor(executor_) + ofh_message_receiver_task_dispatcher(message_receiver& msg_receiver_, task_executor& executor_, unsigned sector_) : + msg_receiver(msg_receiver_), executor(executor_), sector(sector_) { } @@ -30,7 +30,7 @@ class ofh_message_receiver_task_dispatcher : public message_receiver void on_new_frame(ether::unique_rx_buffer buffer) override { if (!executor.execute([this, buff = std::move(buffer)]() mutable { msg_receiver.on_new_frame(std::move(buff)); })) { - srslog::fetch_basic_logger("OFH").warning("Failed to dispatch receiver task"); + srslog::fetch_basic_logger("OFH").warning("Failed to dispatch receiver task for sector#{}", sector); } } @@ -40,6 +40,7 @@ class ofh_message_receiver_task_dispatcher : public message_receiver private: message_receiver& msg_receiver; task_executor& executor; + const unsigned sector; }; } // namespace ofh diff --git a/lib/ofh/receiver/ofh_receiver_factories.cpp b/lib/ofh/receiver/ofh_receiver_factories.cpp index 6bfff07d07..13f81c0ef7 100644 --- a/lib/ofh/receiver/ofh_receiver_factories.cpp +++ b/lib/ofh/receiver/ofh_receiver_factories.cpp @@ -42,6 +42,7 @@ static std::unique_ptr create_uplane_decoder(const recei receiver_cfg.scs, receiver_cfg.cp, nof_prbs_ru, + receiver_cfg.sector, create_iq_decompressor_selector(std::move(decompr)), compr_params) : ofh::create_dynamic_compr_method_ofh_user_plane_packet_decoder( @@ -49,6 +50,7 @@ static std::unique_ptr create_uplane_decoder(const recei receiver_cfg.scs, receiver_cfg.cp, nof_prbs_ru, + receiver_cfg.sector, create_iq_decompressor_selector(std::move(decompr))); } @@ -118,11 +120,13 @@ resolve_receiver_dependencies(const receiver_config& msg_rx_dependencies.logger = &logger; if (receiver_cfg.ignore_ecpri_payload_size_field) { - msg_rx_dependencies.ecpri_decoder = ecpri::create_ecpri_packet_decoder_ignoring_payload_size(logger); + msg_rx_dependencies.ecpri_decoder = + ecpri::create_ecpri_packet_decoder_ignoring_payload_size(logger, receiver_cfg.sector); } else { - msg_rx_dependencies.ecpri_decoder = ecpri::create_ecpri_packet_decoder_using_payload_size(logger); + msg_rx_dependencies.ecpri_decoder = + ecpri::create_ecpri_packet_decoder_using_payload_size(logger, receiver_cfg.sector); } - msg_rx_dependencies.eth_frame_decoder = ether::create_vlan_frame_decoder(logger); + msg_rx_dependencies.eth_frame_decoder = ether::create_vlan_frame_decoder(logger, receiver_cfg.sector); msg_rx_dependencies.data_flow_uplink = create_uplink_data_flow(receiver_cfg, logger, notifier, std::move(ul_slot_context_repo), ul_cp_context_repo); diff --git a/lib/ofh/receiver/ofh_receiver_impl.cpp b/lib/ofh/receiver/ofh_receiver_impl.cpp index 4b73db1cfc..e70ffce615 100644 --- a/lib/ofh/receiver/ofh_receiver_impl.cpp +++ b/lib/ofh/receiver/ofh_receiver_impl.cpp @@ -19,6 +19,7 @@ static message_receiver_config get_message_receiver_configuration(const receiver { message_receiver_config config; + config.sector = rx_config.sector; config.nof_symbols = get_nsymb_per_slot(rx_config.cp); config.scs = rx_config.scs; config.vlan_params.mac_src_address = rx_config.mac_src_address; @@ -58,6 +59,7 @@ get_message_receiver_dependencies(receiver_impl_dependencies::message_rx_depende static closed_rx_window_handler_config get_closed_rx_window_handler_config(const receiver_config& config) { closed_rx_window_handler_config out_config; + out_config.sector = config.sector; out_config.warn_unreceived_ru_frames = config.warn_unreceived_ru_frames; out_config.rx_timing_params = config.rx_timing_params; // As it runs in the same executor, do not delay the reception window close. @@ -92,7 +94,7 @@ receiver_impl::receiver_impl(const receiver_config& config, receiver_impl_depend }(closed_window_handler, window_checker)), msg_receiver(get_message_receiver_configuration(config), get_message_receiver_dependencies(std::move(dependencies.msg_rx_dependencies), window_checker)), - rcv_task_dispatcher(msg_receiver, *dependencies.executor), + rcv_task_dispatcher(msg_receiver, *dependencies.executor, config.sector), ctrl(rcv_task_dispatcher) { } diff --git a/lib/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer.cpp b/lib/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer.cpp index ae491deba4..52200b831f 100644 --- a/lib/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer.cpp +++ b/lib/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer.cpp @@ -22,17 +22,19 @@ void uplane_prach_symbol_data_flow_writer::write_to_prach_buffer(unsigned prach_context prach_context = prach_context_repo->get(slot); if (prach_context.empty()) { - logger.info( - "Dropped received Open Fronthaul message as no uplink PRACH context was found for slot '{}' and eAxC '{}'", - slot, - eaxc); + logger.info("Sector#{}: dropped received Open Fronthaul message as no uplink PRACH context was found for slot '{}' " + "and eAxC '{}'", + sector_id, + slot, + eaxc); return; } // Find resource grid port with eAxC. unsigned port = std::distance(prach_eaxc.begin(), std::find(prach_eaxc.begin(), prach_eaxc.end(), eaxc)); if (port >= prach_context.get_max_nof_ports()) { - logger.info("Skipping eAxC value '{}' as the stored PRACH buffer only supports up to '{}' ports", + logger.info("Sector#{}: skipping eAxC value '{}' as the stored PRACH buffer only supports up to '{}' ports", + sector_id, eaxc, prach_context.get_max_nof_ports()); @@ -95,6 +97,10 @@ void uplane_prach_symbol_data_flow_writer::write_to_prach_buffer(unsigned // Copy the data in the buffer. prach_context_repo->write_iq(slot, port, results.params.symbol_id, start_re, prach_in_data); - logger.debug("Handling PRACH in slot '{}', symbol '{}' and port '{}'", slot, results.params.symbol_id, port); + logger.debug("Sector#{}: handling PRACH in slot '{}', symbol '{}' and port '{}'", + sector_id, + slot, + results.params.symbol_id, + port); } } diff --git a/lib/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer.h b/lib/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer.h index b1a4fe91d9..64e7dcd480 100644 --- a/lib/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer.h +++ b/lib/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer.h @@ -25,9 +25,11 @@ class uplane_prach_symbol_data_flow_writer { public: uplane_prach_symbol_data_flow_writer(span prach_eaxc_, + unsigned sector_id_, srslog::basic_logger& logger_, std::shared_ptr prach_context_repo_) : prach_eaxc(prach_eaxc_.begin(), prach_eaxc_.end()), + sector_id(sector_id_), logger(logger_), prach_context_repo(std::move(prach_context_repo_)) { @@ -39,6 +41,7 @@ class uplane_prach_symbol_data_flow_writer private: const static_vector prach_eaxc; + const unsigned sector_id; srslog::basic_logger& logger; std::shared_ptr prach_context_repo; }; diff --git a/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer.cpp b/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer.cpp index 61901ec1b3..2c7613cbb1 100644 --- a/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer.cpp +++ b/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer.cpp @@ -24,11 +24,13 @@ void uplane_rx_symbol_data_flow_writer::write_to_resource_grid(unsigned unsigned symbol = results.params.symbol_id; const uplink_context& ul_context = ul_context_repo->get(slot, symbol); if (ul_context.empty()) { - logger.warning("Dropped received Open Fronthaul message as no uplink slot context was found for slot '{}', symbol " - "'{}' and eAxC '{}'", - results.params.slot, - results.params.symbol_id, - eaxc); + logger.warning( + "Sector#{}: dropped received Open Fronthaul message as no uplink slot context was found for slot '{}', symbol " + "'{}' and eAxC '{}'", + sector_id, + results.params.slot, + results.params.symbol_id, + eaxc); return; } @@ -69,11 +71,13 @@ void uplane_rx_symbol_data_flow_writer::write_to_resource_grid(unsigned ofh_tracer << trace_event("ofh_receiver_write_rg", write_rg_tp); - logger.debug("Written IQ data into UL resource grid PRB range [{},{}), for slot '{}', symbol '{}' and port '{}'", - section.start_prb, - section.start_prb + nof_prbs_to_write, - slot, - symbol, - rg_port); + logger.debug( + "Sector#{}: written IQ data into UL resource grid PRB range [{},{}), for slot '{}', symbol '{}' and port '{}'", + sector_id, + section.start_prb, + section.start_prb + nof_prbs_to_write, + slot, + symbol, + rg_port); } } diff --git a/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer.h b/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer.h index dc595fb4be..b31cd2b79d 100644 --- a/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer.h +++ b/lib/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer.h @@ -25,9 +25,13 @@ class uplane_rx_symbol_data_flow_writer { public: uplane_rx_symbol_data_flow_writer(span ul_eaxc_, + unsigned sector_id_, srslog::basic_logger& logger_, std::shared_ptr ul_context_repo_) : - ul_eaxc(ul_eaxc_.begin(), ul_eaxc_.end()), logger(logger_), ul_context_repo(std::move(ul_context_repo_)) + ul_eaxc(ul_eaxc_.begin(), ul_eaxc_.end()), + sector_id(sector_id_), + logger(logger_), + ul_context_repo(std::move(ul_context_repo_)) { srsran_assert(!ul_eaxc.empty(), "Invalid number of uplink eAxCs"); srsran_assert(ul_context_repo, "Invalid uplink context repository"); @@ -38,6 +42,7 @@ class uplane_rx_symbol_data_flow_writer private: const static_vector ul_eaxc; + const unsigned sector_id; srslog::basic_logger& logger; std::shared_ptr ul_context_repo; }; diff --git a/lib/ofh/serdes/ofh_serdes_factories.cpp b/lib/ofh/serdes/ofh_serdes_factories.cpp index da7cc19a19..af37bef703 100644 --- a/lib/ofh/serdes/ofh_serdes_factories.cpp +++ b/lib/ofh/serdes/ofh_serdes_factories.cpp @@ -48,11 +48,12 @@ srsran::ofh::create_static_compr_method_ofh_user_plane_packet_decoder(srslog::ba subcarrier_spacing scs, cyclic_prefix cp, unsigned ru_nof_prbs, + unsigned sector_id_, std::unique_ptr decompressor, const ru_compression_params& compr_params) { return std::make_unique( - logger, scs, get_nsymb_per_slot(cp), ru_nof_prbs, std::move(decompressor), compr_params); + logger, scs, get_nsymb_per_slot(cp), ru_nof_prbs, sector_id_, std::move(decompressor), compr_params); } std::unique_ptr @@ -60,8 +61,9 @@ srsran::ofh::create_dynamic_compr_method_ofh_user_plane_packet_decoder(srslog::b subcarrier_spacing scs, cyclic_prefix cp, unsigned ru_nof_prbs, + unsigned sector_id_, std::unique_ptr decompressor) { return std::make_unique( - logger, scs, get_nsymb_per_slot(cp), ru_nof_prbs, std::move(decompressor)); + logger, scs, get_nsymb_per_slot(cp), ru_nof_prbs, sector_id_, std::move(decompressor)); } diff --git a/lib/ofh/serdes/ofh_uplane_message_decoder_dynamic_compression_impl.cpp b/lib/ofh/serdes/ofh_uplane_message_decoder_dynamic_compression_impl.cpp index c263d37731..b80ee57d45 100644 --- a/lib/ofh/serdes/ofh_uplane_message_decoder_dynamic_compression_impl.cpp +++ b/lib/ofh/serdes/ofh_uplane_message_decoder_dynamic_compression_impl.cpp @@ -20,8 +20,9 @@ uplane_message_decoder_dynamic_compression_impl::decode_compression_header( network_order_binary_deserializer& deserializer) { if (deserializer.remaining_bytes() < 2 * sizeof(uint8_t)) { - logger.info("Received an Open Fronthaul packet with size of '{}' bytes that is smaller than the user data " - "compression header length", + logger.info("Sector#{}: received an Open Fronthaul packet with size of '{}' bytes that is smaller than the user " + "data compression header length", + sector_id, deserializer.remaining_bytes()); return uplane_message_decoder_impl::decoded_section_status::incomplete; @@ -32,7 +33,8 @@ uplane_message_decoder_dynamic_compression_impl::decode_compression_header( // Consider a reserved value as malformed message. if (results.ud_comp_hdr.type == compression_type::reserved) { - logger.info("Detected malformed Open Fronthaul message as the decoded compression type '{}' is invalid", + logger.info("Sector#{}: detected malformed Open Fronthaul message as the decoded compression type '{}' is invalid", + sector_id, value & 0x0f); return uplane_message_decoder_impl::decoded_section_status::malformed; diff --git a/lib/ofh/serdes/ofh_uplane_message_decoder_dynamic_compression_impl.h b/lib/ofh/serdes/ofh_uplane_message_decoder_dynamic_compression_impl.h index f48531c9d7..7ad8f8d43b 100644 --- a/lib/ofh/serdes/ofh_uplane_message_decoder_dynamic_compression_impl.h +++ b/lib/ofh/serdes/ofh_uplane_message_decoder_dynamic_compression_impl.h @@ -23,12 +23,13 @@ class network_order_binary_deserializer; class uplane_message_decoder_dynamic_compression_impl : public uplane_message_decoder_impl { public: - explicit uplane_message_decoder_dynamic_compression_impl(srslog::basic_logger& logger_, - subcarrier_spacing scs_, - unsigned nof_symbols_, - unsigned ru_nof_prbs_, - std::unique_ptr decompressor_) : - uplane_message_decoder_impl(logger_, scs_, nof_symbols_, ru_nof_prbs_, std::move(decompressor_)) + uplane_message_decoder_dynamic_compression_impl(srslog::basic_logger& logger_, + subcarrier_spacing scs_, + unsigned nof_symbols_, + unsigned ru_nof_prbs_, + unsigned sector_id_, + std::unique_ptr decompressor_) : + uplane_message_decoder_impl(logger_, scs_, nof_symbols_, ru_nof_prbs_, sector_id_, std::move(decompressor_)) { } diff --git a/lib/ofh/serdes/ofh_uplane_message_decoder_impl.cpp b/lib/ofh/serdes/ofh_uplane_message_decoder_impl.cpp index 2a3f6e30b1..0fcec5cd40 100644 --- a/lib/ofh/serdes/ofh_uplane_message_decoder_impl.cpp +++ b/lib/ofh/serdes/ofh_uplane_message_decoder_impl.cpp @@ -44,34 +44,38 @@ bool uplane_message_decoder_impl::decode(uplane_message_decoder_results& results /// Checks the Open Fronthaul User-Plane header and returns true on success, otherwise false. static bool is_header_valid(const uplane_message_params& params, srslog::basic_logger& logger, + unsigned sector_id, unsigned nof_symbols, unsigned version) { if (params.direction != data_direction::uplink) { - logger.info("Dropped received Open Fronthaul message as it is not an uplink message"); + logger.info("Sector#{}: dropped received Open Fronthaul message as it is not an uplink message", sector_id); return false; } if (version != OFH_PAYLOAD_VERSION) { - logger.info( - "Dropped received Open Fronthaul message as its payload version is '{}' but only version '{}' is supported", - version, - OFH_PAYLOAD_VERSION); + logger.info("Sector#{}: dropped received Open Fronthaul message as its payload version is '{}' but only version " + "'{}' is supported", + sector_id, + version, + OFH_PAYLOAD_VERSION); return false; } if (params.filter_index == filter_index_type::reserved) { - logger.info("Dropped received Open Fronthaul message as its filter index value is reserved '{}'", + logger.info("Sector#{}: dropped received Open Fronthaul message as its filter index value is reserved '{}'", + sector_id, fmt::underlying(params.filter_index)); return false; } if (params.symbol_id >= nof_symbols) { - logger.info("Dropped received Open Fronthaul message as its symbol index is '{}' and this decoder supports a " - "maximum of '{}' symbols", + logger.info("Sector#{}: dropped received Open Fronthaul message as its symbol index is '{}' and this decoder " + "supports a maximum of '{}' symbols", + sector_id, params.symbol_id, nof_symbols); @@ -85,8 +89,9 @@ bool uplane_message_decoder_impl::decode_header(uplane_message_params& network_order_binary_deserializer& deserializer) { if (deserializer.remaining_bytes() < NOF_BYTES_UP_HEADER) { - logger.info("Dropped received Open Fronthaul message as its size is '{}' bytes and it is smaller than the message " - "header size", + logger.info("Sector#{}: dropped received Open Fronthaul message as its size is '{}' bytes and it is smaller than " + "the message header size", + sector_id, deserializer.remaining_bytes()); return false; @@ -111,21 +116,25 @@ bool uplane_message_decoder_impl::decode_header(uplane_message_params& // Check the subframe property. if (subframe >= NOF_SUBFRAMES_PER_FRAME) { - logger.info("Dropped received Open Fronthaul message as the decoded subframe property '{}' is invalid", subframe); + logger.info("Sector#{}: dropped received Open Fronthaul message as the decoded subframe property '{}' is invalid", + sector_id, + subframe); return false; } // Check the slot property. if (slot_id >= slot_point(scs, 0).nof_slots_per_subframe()) { - logger.info("Dropped received Open Fronthaul message as the decoded slot property '{}' is invalid", slot_id); + logger.info("Sector#{}: dropped received Open Fronthaul message as the decoded slot property '{}' is invalid", + sector_id, + slot_id); return false; } params.slot = slot_point(to_numerology_value(scs), frame, subframe, slot_id); - return is_header_valid(params, logger, nof_symbols, version); + return is_header_valid(params, logger, sector_id, nof_symbols, version); } bool uplane_message_decoder_impl::decode_all_sections(uplane_message_decoder_results& results, @@ -142,17 +151,19 @@ bool uplane_message_decoder_impl::decode_all_sections(uplane_message_decoder_res } if (status == decoded_section_status::malformed) { - logger.info( - "Dropped received Open Fronthaul message as a malformed section was decoded for slot '{}' and symbol '{}'", - results.params.slot, - results.params.symbol_id); + logger.info("Sector#{}: dropped received Open Fronthaul message as a malformed section was decoded for slot '{}' " + "and symbol '{}'", + sector_id, + results.params.slot, + results.params.symbol_id); return false; } if (results.sections.full()) { - logger.info("Dropped received Open Fronthaul message as this deserializer only supports '{}' section for slot " - "'{}' and symbol '{}'", + logger.info("Sector#{}: dropped received Open Fronthaul message as this deserializer only supports '{}' section " + "for slot '{}' and symbol '{}'", + sector_id, MAX_NOF_SUPPORTED_SECTIONS, results.params.slot, results.params.symbol_id); @@ -163,10 +174,11 @@ bool uplane_message_decoder_impl::decode_all_sections(uplane_message_decoder_res bool is_result_valid = !results.sections.empty(); if (!is_result_valid) { - logger.info( - "Dropped received Open Fronthaul message as no section was decoded correctly for slot '{}' and symbol '{}'", - results.params.slot, - results.params.symbol_id); + logger.info("Sector#{}: dropped received Open Fronthaul message as no section was decoded correctly for slot '{}' " + "and symbol '{}'", + sector_id, + results.params.slot, + results.params.symbol_id); } return is_result_valid; @@ -197,7 +209,8 @@ static void fill_results_from_decoder_section(uplane_section_params& static bool check_iq_data_size(unsigned nof_prb, network_order_binary_deserializer& deserializer, const ru_compression_params& compression_params, - srslog::basic_logger& logger) + srslog::basic_logger& logger, + unsigned sector_id) { units::bytes prb_iq_data_size( units::bits(NOF_SUBCARRIERS_PER_RB * 2 * compression_params.data_width).round_up_to_bytes().value()); @@ -208,8 +221,9 @@ static bool check_iq_data_size(unsigned nof_prb, } if (deserializer.remaining_bytes() < prb_iq_data_size.value() * nof_prb) { - logger.info("Received Open Fronthaul message size is '{}' bytes and it is smaller than the expected IQ samples " - "size of '{}'", + logger.info("Sector#{}: received Open Fronthaul message size is '{}' bytes and it is smaller than the expected IQ " + "samples size of '{}'", + sector_id, deserializer.remaining_bytes(), prb_iq_data_size.value() * nof_prb); @@ -243,7 +257,8 @@ uplane_message_decoder_impl::decode_section(uplane_message_decoder_results& r } // Check the message contains the required IQ data. - if (!check_iq_data_size(decoder_ofh_up_section.nof_prbs, deserializer, decoder_ofh_up_section.ud_comp_hdr, logger)) { + if (!check_iq_data_size( + decoder_ofh_up_section.nof_prbs, deserializer, decoder_ofh_up_section.ud_comp_hdr, logger, sector_id)) { return decoded_section_status::incomplete; } @@ -262,8 +277,10 @@ uplane_message_decoder_impl::decode_section_header(decoder_uplane_section_params network_order_binary_deserializer& deserializer) { if (deserializer.remaining_bytes() < SECTION_ID_HEADER_NO_COMPRESSION_SIZE) { - logger.info("Received Open Fronthaul message size is '{}' bytes and is smaller than the section header size", - deserializer.remaining_bytes()); + logger.info( + "Sector#{}: received Open Fronthaul message size is '{}' bytes and is smaller than the section header size", + sector_id, + deserializer.remaining_bytes()); return decoded_section_status::incomplete; } @@ -308,9 +325,10 @@ uplane_message_decoder_impl::decode_compression_length(decoder_uplane_section_pa } if (deserializer.remaining_bytes() < sizeof(uint16_t)) { - logger.info( - "Received Open Fronthaul message size is '{}' bytes and is smaller than the user data compression length", - deserializer.remaining_bytes()); + logger.info("Sector#{}: received Open Fronthaul message size is '{}' bytes and is smaller than the user data " + "compression length", + sector_id, + deserializer.remaining_bytes()); return decoded_section_status::incomplete; } diff --git a/lib/ofh/serdes/ofh_uplane_message_decoder_impl.h b/lib/ofh/serdes/ofh_uplane_message_decoder_impl.h index f6fc21096b..8c53182792 100644 --- a/lib/ofh/serdes/ofh_uplane_message_decoder_impl.h +++ b/lib/ofh/serdes/ofh_uplane_message_decoder_impl.h @@ -54,12 +54,14 @@ class uplane_message_decoder_impl : public uplane_message_decoder subcarrier_spacing scs_, unsigned nof_symbols_, unsigned ru_nof_prbs_, + unsigned sector_id_, std::unique_ptr decompressor_) : logger(logger_), decompressor(std::move(decompressor_)), scs(scs_), nof_symbols(nof_symbols_), - ru_nof_prbs(ru_nof_prbs_) + ru_nof_prbs(ru_nof_prbs_), + sector_id(sector_id_) { srsran_assert(decompressor, "Invalid IQ decompressor"); } @@ -102,6 +104,7 @@ class uplane_message_decoder_impl : public uplane_message_decoder const subcarrier_spacing scs; const unsigned nof_symbols; const unsigned ru_nof_prbs; + const unsigned sector_id; }; } // namespace ofh diff --git a/lib/ofh/serdes/ofh_uplane_message_decoder_static_compression_impl.cpp b/lib/ofh/serdes/ofh_uplane_message_decoder_static_compression_impl.cpp index 00b060f046..7dfe7ac2d1 100644 --- a/lib/ofh/serdes/ofh_uplane_message_decoder_static_compression_impl.cpp +++ b/lib/ofh/serdes/ofh_uplane_message_decoder_static_compression_impl.cpp @@ -30,9 +30,10 @@ uplane_message_decoder_static_compression_impl::uplane_message_decoder_static_co subcarrier_spacing scs_, unsigned nof_symbols_, unsigned ru_nof_prbs_, + unsigned sector_id_, std::unique_ptr decompressor_, const ru_compression_params& compression_params_) : - uplane_message_decoder_impl(logger_, scs_, nof_symbols_, ru_nof_prbs_, std::move(decompressor_)), + uplane_message_decoder_impl(logger_, scs_, nof_symbols_, ru_nof_prbs_, sector_id_, std::move(decompressor_)), compression_params(compression_params_) { } diff --git a/lib/ofh/serdes/ofh_uplane_message_decoder_static_compression_impl.h b/lib/ofh/serdes/ofh_uplane_message_decoder_static_compression_impl.h index 8726c53a9f..d599b71073 100644 --- a/lib/ofh/serdes/ofh_uplane_message_decoder_static_compression_impl.h +++ b/lib/ofh/serdes/ofh_uplane_message_decoder_static_compression_impl.h @@ -22,12 +22,13 @@ class network_order_binary_deserializer; class uplane_message_decoder_static_compression_impl : public uplane_message_decoder_impl { public: - explicit uplane_message_decoder_static_compression_impl(srslog::basic_logger& logger_, - subcarrier_spacing scs_, - unsigned nof_symbols_, - unsigned ru_nof_prbs_, - std::unique_ptr decompressor_, - const ru_compression_params& compression_params_); + uplane_message_decoder_static_compression_impl(srslog::basic_logger& logger_, + subcarrier_spacing scs_, + unsigned nof_symbols_, + unsigned ru_nof_prbs_, + unsigned sector_id_, + std::unique_ptr decompressor_, + const ru_compression_params& compression_params_); private: // See parent for documentation. diff --git a/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_impl.cpp b/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_impl.cpp index 6a1e56d788..f05a0f3917 100644 --- a/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_impl.cpp +++ b/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_impl.cpp @@ -133,6 +133,7 @@ data_flow_cplane_scheduling_commands_impl::data_flow_cplane_scheduling_commands_ logger(*dependencies.logger), nof_symbols_per_slot(get_nsymb_per_slot(config.cp)), ru_nof_prbs(config.ru_nof_prbs), + sector_id(config.sector), dl_compr_params(config.dl_compr_params), ul_compr_params(config.ul_compr_params), prach_compr_params(config.prach_compr_params), @@ -155,7 +156,8 @@ void data_flow_cplane_scheduling_commands_impl::enqueue_section_type_1_message( data_direction direction = context.direction; slot_point slot = context.slot; slot_symbol_point symbol_point(slot, 0, nof_symbols_per_slot); - logger.debug("Packing a {} type 1 Control-Plane message for slot '{}' and eAxC '{}'", + logger.debug("Sector#{}: packing a {} type 1 Control-Plane message for slot '{}' and eAxC '{}'", + sector_id, (direction == data_direction::downlink) ? "downlink" : "uplink", slot, context.eaxc); @@ -163,11 +165,12 @@ void data_flow_cplane_scheduling_commands_impl::enqueue_section_type_1_message( // Get an ethernet frame buffer. scoped_frame_buffer scoped_buffer(*frame_pool, symbol_point, message_type::control_plane, direction); if (scoped_buffer.empty()) { - logger.warning( - "Not enough space in the buffer pool to create a {} type 1 Control-Plane message for slot '{}' and eAxC '{}'", - (direction == data_direction::downlink) ? "downlink" : "uplink", - slot, - context.eaxc); + logger.warning("Sector#{}: not enough space in the buffer pool to create a {} type 1 Control-Plane message for " + "slot '{}' and eAxC '{}'", + sector_id, + (direction == data_direction::downlink) ? "downlink" : "uplink", + slot, + context.eaxc); return; } ether::frame_buffer& frame_buffer = scoped_buffer.get_next_frame(); @@ -215,13 +218,17 @@ void data_flow_cplane_scheduling_commands_impl::enqueue_section_type_3_prach_mes { slot_point slot = context.slot; slot_symbol_point symbol_point(slot, context.start_symbol, nof_symbols_per_slot); - logger.debug("Packing a type 3 PRACH Control-Plane message for slot '{}' and eAxC '{}'", slot, context.eaxc); + logger.debug("Sector#{}: packing a type 3 PRACH Control-Plane message for slot '{}' and eAxC '{}'", + sector_id, + slot, + context.eaxc); // Get an ethernet frame buffer. scoped_frame_buffer scoped_buffer(*frame_pool, symbol_point, message_type::control_plane, data_direction::uplink); if (scoped_buffer.empty()) { - logger.warning("Not enough space in the buffer pool to create a type 3 PRACH Control-Plane message for slot '{}' " - "and eAxC '{}'", + logger.warning("Sector#{}: not enough space in the buffer pool to create a type 3 PRACH Control-Plane message for " + "slot '{}' and eAxC '{}'", + sector_id, slot, context.eaxc); return; @@ -236,8 +243,9 @@ void data_flow_cplane_scheduling_commands_impl::enqueue_section_type_3_prach_mes span ofh_buffer = buffer.last(buffer.size() - offset.value()); const auto& ofh_ctrl_params = generate_prach_control_parameters(context, prach_compr_params, ru_nof_prbs); - logger.debug("Generated a PRACH request for slot '{}': numSymbols={}, startSym={}, start_re={}, scs={}, " + logger.debug("Sector#{}: generated a PRACH request for slot '{}': numSymbols={}, startSym={}, start_re={}, scs={}, " "prach_scs={}, nof_rb={}, timeOffset={}, freqOffset={}", + sector_id, slot, context.nof_repetitions, context.start_symbol, diff --git a/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_impl.h b/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_impl.h index c673d9ff91..bd52173e1e 100644 --- a/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_impl.h +++ b/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_impl.h @@ -23,6 +23,8 @@ namespace ofh { /// Open Fronthaul Control-Plane scheduling and beamforming commands data flow implementation configuration. struct data_flow_cplane_scheduling_commands_impl_config { + /// Radio sector identifier. + unsigned sector; /// RU bandwidth in PRBs. unsigned ru_nof_prbs; /// Cyclic prefix. @@ -69,6 +71,7 @@ class data_flow_cplane_scheduling_commands_impl : public data_flow_cplane_schedu srslog::basic_logger& logger; const unsigned nof_symbols_per_slot; const unsigned ru_nof_prbs; + const unsigned sector_id; const ru_compression_params dl_compr_params; const ru_compression_params ul_compr_params; const ru_compression_params prach_compr_params; diff --git a/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_task_dispatcher.h b/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_task_dispatcher.h index 2813e8647d..d9389097a2 100644 --- a/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_task_dispatcher.h +++ b/lib/ofh/transmitter/ofh_data_flow_cplane_scheduling_commands_task_dispatcher.h @@ -23,8 +23,9 @@ class data_flow_cplane_downlink_task_dispatcher : public data_flow_cplane_schedu { public: data_flow_cplane_downlink_task_dispatcher(std::unique_ptr data_flow_cplane_, - task_executor& executor_) : - data_flow_cplane(std::move(data_flow_cplane_)), executor(executor_) + task_executor& executor_, + unsigned sector_id_) : + data_flow_cplane(std::move(data_flow_cplane_)), executor(executor_), sector_id(sector_id_) { srsran_assert(data_flow_cplane, "Invalid data flow"); } @@ -33,8 +34,8 @@ class data_flow_cplane_downlink_task_dispatcher : public data_flow_cplane_schedu void enqueue_section_type_1_message(const data_flow_cplane_type_1_context& context) override { if (!executor.execute([this, context]() { data_flow_cplane->enqueue_section_type_1_message(context); })) { - srslog::fetch_basic_logger("OFH").warning("Failed to dispatch Control-Plane type 1 message for slot '{}'", - context.slot); + srslog::fetch_basic_logger("OFH").warning( + "Sector#{}: failed to dispatch Control-Plane type 1 message for slot '{}'", sector_id, context.slot); } } @@ -42,14 +43,15 @@ class data_flow_cplane_downlink_task_dispatcher : public data_flow_cplane_schedu void enqueue_section_type_3_prach_message(const data_flow_cplane_scheduling_prach_context& context) override { if (!executor.execute([this, context]() { data_flow_cplane->enqueue_section_type_3_prach_message(context); })) { - srslog::fetch_basic_logger("OFH").warning("Failed to dispatch Control-Plane type 3 message for slot '{}'", - context.slot); + srslog::fetch_basic_logger("OFH").warning( + "Sector#{}: failed to dispatch Control-Plane type 3 message for slot '{}'", sector_id, context.slot); } } private: std::unique_ptr data_flow_cplane; task_executor& executor; + const unsigned sector_id; }; } // namespace ofh diff --git a/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_data_impl.cpp b/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_data_impl.cpp index 5de2d87908..2914f196a6 100644 --- a/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_data_impl.cpp +++ b/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_data_impl.cpp @@ -65,6 +65,7 @@ data_flow_uplane_downlink_data_impl::data_flow_uplane_downlink_data_impl( logger(*dependencies.logger), nof_symbols_per_slot(get_nsymb_per_slot(config.cp)), ru_nof_prbs(config.ru_nof_prbs), + sector_id(config.sector), compr_params(config.compr_params), frame_pool(std::move(dependencies.frame_pool)), compressor_sel(std::move(dependencies.compressor_sel)), @@ -115,8 +116,9 @@ void data_flow_uplane_downlink_data_impl::enqueue_section_type_1_message_symbol_ slot_symbol_point symbol_point(context.slot, symbol_id, nof_symbols_per_slot); scoped_frame_buffer scoped_buffer(*frame_pool, symbol_point, message_type::user_plane, data_direction::downlink); if (scoped_buffer.empty()) { - logger.warning("Not enough space in the buffer pool to create a downlink User-Plane message for slot '{}' and " - "eAxC '{}', symbol_id '{}'", + logger.warning("Sector#{}: not enough space in the buffer pool to create a downlink User-Plane message for slot " + "'{}' and eAxC '{}', symbol_id '{}'", + sector_id, context.slot, context.eaxc, symbol_id); @@ -147,9 +149,10 @@ void data_flow_uplane_downlink_data_impl::enqueue_section_type_1_message_symbol_ // Skip frame buffers so small that cannot carry one PRB. if (fragment_nof_prbs == 0) { - logger.warning( - "Skipped frame buffer as it cannot store data for a single PRB, required buffer size is '{}' bytes", - data.size()); + logger.warning("Sector#{}: skipped frame buffer as it cannot store data for a single PRB, required buffer size " + "is '{}' bytes", + sector_id, + data.size()); continue; } @@ -193,8 +196,9 @@ unsigned data_flow_uplane_downlink_data_impl::enqueue_section_type_1_message_sym span eth_buffer = span(buffer).first(ether_header_size.value() + bytes_written); eth_builder->build_frame(eth_buffer); - logger.debug("Packing a downlink User-Plane message for slot '{}' and eAxC '{}', symbol_id '{}', PRB range '{}:{}', " - "size '{}' bytes", + logger.debug("Sector#{}: packing a downlink User-Plane message for slot '{}' and eAxC '{}', symbol_id '{}', PRB " + "range '{}:{}', size '{}' bytes", + sector_id, params.slot, eaxc, params.symbol_id, diff --git a/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_data_impl.h b/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_data_impl.h index 370aecc14a..5171cbaf93 100644 --- a/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_data_impl.h +++ b/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_data_impl.h @@ -31,6 +31,8 @@ namespace ofh { /// Open Fronthaul User-Plane downlink data flow implementation configuration. struct data_flow_uplane_downlink_data_impl_config { + /// Radio sector identifier. + unsigned sector; /// Cyclic prefix. cyclic_prefix cp; /// RU bandwidth in PRBs. @@ -110,6 +112,7 @@ class data_flow_uplane_downlink_data_impl : public data_flow_uplane_downlink_dat srslog::basic_logger& logger; const unsigned nof_symbols_per_slot; const unsigned ru_nof_prbs; + const unsigned sector_id; const ru_compression_params compr_params; sequence_identifier_generator up_seq_gen; std::shared_ptr frame_pool; diff --git a/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_task_dispatcher.h b/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_task_dispatcher.h index cb3edc3ebb..217b3584e4 100644 --- a/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_task_dispatcher.h +++ b/lib/ofh/transmitter/ofh_data_flow_uplane_downlink_task_dispatcher.h @@ -23,8 +23,9 @@ class data_flow_uplane_downlink_task_dispatcher : public data_flow_uplane_downli { public: data_flow_uplane_downlink_task_dispatcher(std::unique_ptr data_flow_uplane_, - task_executor& executor_) : - data_flow_uplane(std::move(data_flow_uplane_)), executor(executor_) + task_executor& executor_, + unsigned sector_id_) : + data_flow_uplane(std::move(data_flow_uplane_)), executor(executor_), sector_id(sector_id_) { srsran_assert(data_flow_uplane, "Invalid data flow"); } @@ -36,13 +37,16 @@ class data_flow_uplane_downlink_task_dispatcher : public data_flow_uplane_downli if (!executor.execute( [this, context, rg = grid.copy()]() { data_flow_uplane->enqueue_section_type_1_message(context, rg); })) { srslog::fetch_basic_logger("OFH").warning( - "Failed to dispatch message in the downlink data flow User-Plane for slot '{}'", context.slot); + "Sector#{}: failed to dispatch message in the downlink data flow User-Plane for slot '{}'", + sector_id, + context.slot); } } private: std::unique_ptr data_flow_uplane; task_executor& executor; + const unsigned sector_id; }; } // namespace ofh diff --git a/lib/ofh/transmitter/ofh_downlink_handler_broadcast_impl.cpp b/lib/ofh/transmitter/ofh_downlink_handler_broadcast_impl.cpp index d9e4c48d53..5c02dcde5f 100644 --- a/lib/ofh/transmitter/ofh_downlink_handler_broadcast_impl.cpp +++ b/lib/ofh/transmitter/ofh_downlink_handler_broadcast_impl.cpp @@ -45,6 +45,7 @@ downlink_handler_broadcast_impl::downlink_handler_broadcast_impl( data_flow_uplane(std::move(dependencies.data_flow_uplane)), window_checker( *dependencies.logger, + config.sector, calculate_nof_symbols_before_ota(config.cp, config.scs, config.dl_processing_time, config.tx_timing_params), get_nsymb_per_slot(config.cp), to_numerology_value(config.scs)), @@ -67,10 +68,11 @@ void downlink_handler_broadcast_impl::handle_dl_data(const resource_grid_context if (window_checker.is_late(context.slot)) { err_notifier.get().on_late_downlink_message({context.slot, sector_id}); - logger.warning( - "Dropped late downlink resource grid in slot '{}' and sector#{}. No OFH data will be transmitted for this slot", - context.slot, - context.sector); + logger.warning("Sector#{}: dropped late downlink resource grid in slot '{}' and sector#{}. No OFH data will be " + "transmitted for this slot", + sector_id, + context.slot, + context.sector); ofh_tracer << trace_event("ofh_handle_dl_late", tp); return; diff --git a/lib/ofh/transmitter/ofh_downlink_handler_impl.cpp b/lib/ofh/transmitter/ofh_downlink_handler_impl.cpp index 0ab1dd3deb..4dfd08cf71 100644 --- a/lib/ofh/transmitter/ofh_downlink_handler_impl.cpp +++ b/lib/ofh/transmitter/ofh_downlink_handler_impl.cpp @@ -42,6 +42,7 @@ downlink_handler_impl::downlink_handler_impl(const downlink_handler_impl_config& dl_eaxc(config.dl_eaxc), window_checker( *dependencies.logger, + config.sector, calculate_nof_symbols_before_ota(config.cp, config.scs, config.dl_processing_time, config.tx_timing_params), get_nsymb_per_slot(config.cp), to_numerology_value(config.scs)), @@ -71,10 +72,11 @@ void downlink_handler_impl::handle_dl_data(const resource_grid_context& context, if (window_checker.is_late(context.slot)) { err_notifier.get().on_late_downlink_message({context.slot, sector_id}); - logger.warning( - "Dropped late downlink resource grid in slot '{}' and sector#{}. No OFH data will be transmitted for this slot", - context.slot, - context.sector); + logger.warning("Sector#{}: dropped late downlink resource grid in slot '{}' and sector#{}. No OFH data will be " + "transmitted for this slot", + sector_id, + context.slot, + context.sector); l1_tracer << instant_trace_event{"handle_dl_data_late", instant_trace_event::cpu_scope::thread}; ofh_tracer << trace_event("ofh_handle_dl_late", tp); return; diff --git a/lib/ofh/transmitter/ofh_transmitter_factories.cpp b/lib/ofh/transmitter/ofh_transmitter_factories.cpp index d996ea69f3..b09476ec57 100644 --- a/lib/ofh/transmitter/ofh_transmitter_factories.cpp +++ b/lib/ofh/transmitter/ofh_transmitter_factories.cpp @@ -35,6 +35,7 @@ create_data_flow_cplane_sched(const transmitter_config& config.ru_nof_prbs = get_max_Nprb(bs_channel_bandwidth_to_MHz(tx_config.ru_working_bw), tx_config.scs, srsran::frequency_range::FR1); + config.sector = tx_config.sector; config.dl_compr_params = tx_config.dl_compr_params; config.ul_compr_params = tx_config.ul_compr_params; config.prach_compr_params = tx_config.prach_compr_params; @@ -68,6 +69,7 @@ create_data_flow_uplane_data(const transmitter_config& tx_config, data_flow_uplane_downlink_data_impl_config config; config.ru_nof_prbs = get_max_Nprb(bs_channel_bandwidth_to_MHz(tx_config.ru_working_bw), tx_config.scs, srsran::frequency_range::FR1); + config.sector = tx_config.sector; config.dl_eaxc = tx_config.dl_eaxc; config.compr_params = tx_config.dl_compr_params; config.cp = tx_config.cp; @@ -114,9 +116,10 @@ create_downlink_manager(const transmitter_config& tx_con auto data_flow_cplane = std::make_unique( create_data_flow_cplane_sched( tx_config, tx_config.is_downlink_static_compr_hdr_enabled, logger, frame_pool, std::move(ul_cp_context_repo)), - executor); + executor, + tx_config.sector); auto data_flow_uplane = std::make_unique( - create_data_flow_uplane_data(tx_config, logger, frame_pool), executor); + create_data_flow_uplane_data(tx_config, logger, frame_pool), executor, tx_config.sector); if (tx_config.downlink_broadcast) { downlink_handler_broadcast_impl_config dl_config; @@ -240,7 +243,8 @@ resolve_transmitter_dependencies(const transmitter_config& std::move(prach_context_repo), std::move(ul_slot_context_repo), std::move(ul_cp_context_repo)), - downlink_executor); + downlink_executor, + tx_config.sector); dependencies.eth_gateway = std::move(eth_gateway); diff --git a/lib/ofh/transmitter/ofh_tx_window_checker.h b/lib/ofh/transmitter/ofh_tx_window_checker.h index 50aea61f23..682c200a98 100644 --- a/lib/ofh/transmitter/ofh_tx_window_checker.h +++ b/lib/ofh/transmitter/ofh_tx_window_checker.h @@ -22,10 +22,12 @@ class tx_window_checker : public ota_symbol_boundary_notifier { public: tx_window_checker(srslog::basic_logger& logger_, + unsigned sector_id_, uint32_t advance_time_in_symbols_, uint32_t nof_symbols_, uint32_t numerology_) : logger(logger_), + sector_id(sector_id_), advance_time_in_symbols(advance_time_in_symbols_), nof_symbols(nof_symbols_), numerology(numerology_) @@ -53,8 +55,9 @@ class tx_window_checker : public ota_symbol_boundary_notifier return false; } - logger.debug("A late upper-PHY downlink request arrived to OFH in slot '{}_{}' with current ota_slot='{}_{}', " - "OFH processing time requires a minimum of '{}' symbols", + logger.debug("Sector#{}: a late upper-PHY downlink request arrived to OFH in slot '{}_{}' with current " + "ota_slot='{}_{}', OFH processing time requires a minimum of '{}' symbols", + sector_id, slot, 0, ota_symbol_point.get_slot(), @@ -66,6 +69,7 @@ class tx_window_checker : public ota_symbol_boundary_notifier private: srslog::basic_logger& logger; + const unsigned sector_id; const uint32_t advance_time_in_symbols; const uint32_t nof_symbols; const uint32_t numerology; diff --git a/lib/ofh/transmitter/ofh_uplink_request_handler_task_dispatcher.h b/lib/ofh/transmitter/ofh_uplink_request_handler_task_dispatcher.h index f2e447eecd..738fec5d47 100644 --- a/lib/ofh/transmitter/ofh_uplink_request_handler_task_dispatcher.h +++ b/lib/ofh/transmitter/ofh_uplink_request_handler_task_dispatcher.h @@ -22,8 +22,9 @@ class uplink_request_handler_task_dispatcher : public uplink_request_handler { public: uplink_request_handler_task_dispatcher(std::unique_ptr ul_handler_, - task_executor& executor_) : - ul_handler(std::move(ul_handler_)), executor(executor_) + task_executor& executor_, + unsigned sector_id_) : + ul_handler(std::move(ul_handler_)), executor(executor_), sector_id(sector_id_) { srsran_assert(ul_handler, "Invalid uplink request handler"); } @@ -32,7 +33,8 @@ class uplink_request_handler_task_dispatcher : public uplink_request_handler void handle_prach_occasion(const prach_buffer_context& context, prach_buffer& buffer) override { if (!executor.execute([this, context, &buffer]() { ul_handler->handle_prach_occasion(context, buffer); })) { - srslog::fetch_basic_logger("OFH").warning("Failed to dispatch PRACH occasion for slot '{}'", context.slot); + srslog::fetch_basic_logger("OFH").warning( + "Sector#{}: failed to dispatch PRACH occasion for slot '{}'", sector_id, context.slot); } } @@ -40,13 +42,15 @@ class uplink_request_handler_task_dispatcher : public uplink_request_handler void handle_new_uplink_slot(const resource_grid_context& context, const shared_resource_grid& grid) override { if (!executor.execute([this, context, rg = grid.copy()]() { ul_handler->handle_new_uplink_slot(context, rg); })) { - srslog::fetch_basic_logger("OFH").warning("Failed to dispatch new uplink slot for slot '{}'", context.slot); + srslog::fetch_basic_logger("OFH").warning( + "Sector#{}: failed to dispatch new uplink slot for slot '{}'", sector_id, context.slot); } } private: std::unique_ptr ul_handler; task_executor& executor; + const unsigned sector_id; }; } // namespace ofh diff --git a/tests/unittests/ofh/ecpri/ecpri_packet_decoder_impl_test.cpp b/tests/unittests/ofh/ecpri/ecpri_packet_decoder_impl_test.cpp index a918a35bba..c490a34044 100644 --- a/tests/unittests/ofh/ecpri/ecpri_packet_decoder_impl_test.cpp +++ b/tests/unittests/ofh/ecpri/ecpri_packet_decoder_impl_test.cpp @@ -24,7 +24,7 @@ TEST(ecpri_packet_decoder_impl_test, decode_valid_control_packet_should_pass) packet_parameters params; - packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST")); + packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST"), 0); span payload = decoder.decode(packet, params); @@ -44,7 +44,7 @@ TEST(ecpri_packet_decoder_impl_test, decode_valid_data_packet_should_pass) packet_parameters params; - packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST")); + packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST"), 0); span payload = decoder.decode(packet, params); @@ -63,7 +63,7 @@ TEST(ecpri_packet_decoder_impl_test, decode_invalid_packet_size_should_fail) packet_parameters params; - packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST")); + packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST"), 0); span payload = decoder.decode(packet, params); @@ -79,7 +79,7 @@ TEST(ecpri_packet_decoder_impl_test, decode_invalid_payload_size_should_fail) packet_parameters params; - packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST")); + packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST"), 0); span payload = decoder.decode(packet, params); @@ -95,7 +95,7 @@ TEST(ecpri_packet_decoder_impl_test, decode_invalid_ecpri_protocol_revision_shou packet_parameters params; - packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST")); + packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST"), 0); span payload = decoder.decode(packet, params); @@ -111,7 +111,7 @@ TEST(ecpri_packet_decoder_impl_test, decode_invalid_ecpri_last_packet_should_fai packet_parameters params; - packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST")); + packet_decoder_use_header_payload_size decoder(srslog::fetch_basic_logger("TEST"), 0); span payload = decoder.decode(packet, params); diff --git a/tests/unittests/ofh/ethernet/vlan_ethernet_frame_decoder_test.cpp b/tests/unittests/ofh/ethernet/vlan_ethernet_frame_decoder_test.cpp index 8f2e304acb..9e0768fe11 100644 --- a/tests/unittests/ofh/ethernet/vlan_ethernet_frame_decoder_test.cpp +++ b/tests/unittests/ofh/ethernet/vlan_ethernet_frame_decoder_test.cpp @@ -23,7 +23,7 @@ TEST(vlan_ethernet_frame_decoder_impl_test, decode_valid_vlan_ethernet_frame_sho 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - std::unique_ptr decoder = create_vlan_frame_decoder(srslog::fetch_basic_logger("TEST")); + std::unique_ptr decoder = create_vlan_frame_decoder(srslog::fetch_basic_logger("TEST"), 0); vlan_frame_params params; auto payload = decoder->decode(packet, params); @@ -42,7 +42,7 @@ TEST(vlan_ethernet_frame_decoder_impl_test, decode_small_vlan_ethernet_frame_sho 0xdf, 0xaa, 0xaa, 0xbb, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; - std::unique_ptr decoder = create_vlan_frame_decoder(srslog::fetch_basic_logger("TEST")); + std::unique_ptr decoder = create_vlan_frame_decoder(srslog::fetch_basic_logger("TEST"), 0); vlan_frame_params params; auto payload = decoder->decode(packet, params); diff --git a/tests/unittests/ofh/receiver/ofh_uplane_prach_data_flow_notifier_test.cpp b/tests/unittests/ofh/receiver/ofh_uplane_prach_data_flow_notifier_test.cpp index 68096f082b..d8063b7a36 100644 --- a/tests/unittests/ofh/receiver/ofh_uplane_prach_data_flow_notifier_test.cpp +++ b/tests/unittests/ofh/receiver/ofh_uplane_prach_data_flow_notifier_test.cpp @@ -38,14 +38,22 @@ TEST(ofh_uplane_prach_data_flow_notifier, unwritten_buffer_does_not_notify) uplane_prach_data_flow_notifier sender(srslog::fetch_basic_logger("TEST"), repo, notifier); slot_point slot(0, 0, 1); prach_buffer_dummy buffer(1); - prach_buffer_context context; - context.slot = slot; - context.format = prach_format_type::zero; - context.ports = {0}; - context.nof_td_occasions = 1; - context.nof_fd_occasions = 1; - context.pusch_scs = srsran::subcarrier_spacing::kHz30; - context.start_symbol = 0; + prach_buffer_context context = {}; + context.slot = slot; + context.format = prach_format_type::zero; + context.ports = {0}; + context.nof_td_occasions = 1; + context.nof_fd_occasions = 1; + context.pusch_scs = srsran::subcarrier_spacing::kHz30; + context.start_symbol = 0; + context.rb_offset = 0; + context.sector = 0; + context.nof_prb_ul_grid = 12; + context.restricted_set = restricted_set_config::UNRESTRICTED; + context.root_sequence_index = 0; + context.zero_correlation_zone = 0; + context.start_preamble_index = 0; + context.nof_preamble_indices = 1; repo->add(context, buffer, std::nullopt, std::nullopt); sender.notify_prach(slot); @@ -64,14 +72,22 @@ TEST(ofh_uplane_prach_data_flow_notifier, completed_long_prach_buffer_triggers_n unsigned symbol = 0; unsigned port = 0; prach_buffer_dummy buffer(1); - prach_buffer_context context; - context.slot = slot; - context.format = prach_format_type::zero; - context.ports = {0}; - context.nof_td_occasions = 1; - context.nof_fd_occasions = 1; - context.pusch_scs = srsran::subcarrier_spacing::kHz30; - context.start_symbol = 0; + prach_buffer_context context = {}; + context.slot = slot; + context.format = prach_format_type::zero; + context.ports = {0}; + context.nof_td_occasions = 1; + context.nof_fd_occasions = 1; + context.pusch_scs = srsran::subcarrier_spacing::kHz30; + context.start_symbol = 0; + context.rb_offset = 0; + context.sector = 0; + context.nof_prb_ul_grid = 12; + context.restricted_set = restricted_set_config::UNRESTRICTED; + context.root_sequence_index = 0; + context.zero_correlation_zone = 0; + context.start_preamble_index = 0; + context.nof_preamble_indices = 1; static_vector samples(839); repo->add(context, buffer, std::nullopt, std::nullopt); @@ -98,14 +114,22 @@ TEST(ofh_uplane_prach_data_flow_notifier, completed_short_prach_buffer_triggers_ slot_point slot(0, 0, 1); unsigned port = 0; prach_buffer_dummy buffer(1, false); - prach_buffer_context context; - context.slot = slot; - context.format = prach_format_type::B4; - context.ports = {0}; - context.nof_td_occasions = 1; - context.nof_fd_occasions = 1; - context.pusch_scs = srsran::subcarrier_spacing::kHz30; - context.start_symbol = 0; + prach_buffer_context context = {}; + context.slot = slot; + context.format = prach_format_type::B4; + context.ports = {0}; + context.nof_td_occasions = 1; + context.nof_fd_occasions = 1; + context.pusch_scs = srsran::subcarrier_spacing::kHz30; + context.start_symbol = 0; + context.rb_offset = 0; + context.sector = 0; + context.nof_prb_ul_grid = 12; + context.restricted_set = restricted_set_config::UNRESTRICTED; + context.root_sequence_index = 0; + context.zero_correlation_zone = 0; + context.start_preamble_index = 0; + context.nof_preamble_indices = 1; static_vector samples(139); repo->add(context, buffer, std::nullopt, std::nullopt); diff --git a/tests/unittests/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer_test.cpp b/tests/unittests/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer_test.cpp index b285cd3ad9..6c2ed0a764 100644 --- a/tests/unittests/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer_test.cpp +++ b/tests/unittests/ofh/receiver/ofh_uplane_prach_symbol_data_flow_writer_test.cpp @@ -38,7 +38,7 @@ class ofh_uplane_prach_symbol_data_flow_writer_fixture : public ::testing::TestW buffer(get_preamble_duration(format), is_long_preamble(format)), preamble_length(is_long_preamble(format) ? 839 : 139), nof_symbols(get_preamble_duration(format) == 0 ? 1U : get_preamble_duration(format)), - writer(prach_eaxc, srslog::fetch_basic_logger("TEST"), repo) + writer(prach_eaxc, 0, srslog::fetch_basic_logger("TEST"), repo) { buffer_context.slot = slot; buffer_context.format = format; diff --git a/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer_test.cpp b/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer_test.cpp index d376279258..d6818d62a4 100644 --- a/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer_test.cpp +++ b/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer_test.cpp @@ -41,7 +41,7 @@ class ofh_uplane_rx_symbol_data_flow_writer_fixture : public ::testing::Test rg_reader(nof_ports, symbol_range.stop(), nof_prb), grid(rg_reader, rg_writer), shared_grid(grid), - writer(eaxc, srslog::fetch_basic_logger("TEST"), repo) + writer(eaxc, 0, srslog::fetch_basic_logger("TEST"), repo) { results.params.slot = slot; results.params.symbol_id = symbol_id; diff --git a/tests/unittests/ofh/serdes/ofh_uplane_packet_decoder_dynamic_impl_test.cpp b/tests/unittests/ofh/serdes/ofh_uplane_packet_decoder_dynamic_impl_test.cpp index b68242b6e0..3cc5670c8e 100644 --- a/tests/unittests/ofh/serdes/ofh_uplane_packet_decoder_dynamic_impl_test.cpp +++ b/tests/unittests/ofh/serdes/ofh_uplane_packet_decoder_dynamic_impl_test.cpp @@ -49,6 +49,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, valid_packet_should_decode_correctl subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -87,6 +88,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, missing_one_iq_sample_must_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -110,6 +112,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, missing_one_prb_must_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -133,6 +136,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, dynamic_compression_with_no_compres subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -154,6 +158,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, decoding_one_section_and_failing_to subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -171,6 +176,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, missing_section_header_must_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -187,6 +193,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, missing_header_must_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -203,6 +210,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, missing_compression_header_must_fai subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -223,6 +231,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, downlink_packet_should_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -243,6 +252,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, reserved_filter_index_should_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -263,6 +273,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, symbol_index_out_of_range_should_fa subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -283,6 +294,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, invalid_subframe_should_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); for (unsigned i = 0, e = 10; i != e; ++i) { @@ -317,6 +329,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, invalid_slot_should_fail) scs, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); unsigned nof_slots = slot_point(scs, 0).nof_slots_per_subframe(); @@ -352,6 +365,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, invalid_compression_type_should_fai subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); for (uint8_t i = 0, e = static_cast(compression_type::reserved); i != e; ++i) { @@ -383,6 +397,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, none_compression_with_15_bits_shoul subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -406,6 +421,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, bfp_with_9_bits_should_pass) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -428,6 +444,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, bfp_with_15_bits_without_ud_comp_pa subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -450,6 +467,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, if_message_num_prbs_equals_zero_dec subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -473,6 +491,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, if_message_contains_one_valid_secti subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique()); uplane_message_decoder_results results; @@ -505,6 +524,7 @@ TEST(ofh_uplane_packet_decoder_dynamic_impl, message_containing_more_than_one_se subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique()); uplane_message_decoder_results results; diff --git a/tests/unittests/ofh/serdes/ofh_uplane_packet_decoder_static_impl_test.cpp b/tests/unittests/ofh/serdes/ofh_uplane_packet_decoder_static_impl_test.cpp index 11970f9a00..829c95225a 100644 --- a/tests/unittests/ofh/serdes/ofh_uplane_packet_decoder_static_impl_test.cpp +++ b/tests/unittests/ofh/serdes/ofh_uplane_packet_decoder_static_impl_test.cpp @@ -47,6 +47,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, valid_packet_should_decode_correctly subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -84,6 +85,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, missing_one_iq_sample_must_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -107,6 +109,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, missing_one_prb_must_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -130,6 +133,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, static_compression_with_compression_ subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -151,6 +155,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, decoding_one_section_and_failing_to_ subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -169,6 +174,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, missing_section_header_must_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -186,6 +192,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, missing_header_must_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -206,6 +213,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, downlink_packet_should_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -226,6 +234,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, reserved_filter_index_should_fail) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -246,6 +255,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, symbol_index_out_of_range_should_fai subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 16}); @@ -266,6 +276,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, none_compression_with_15_bits_should subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::none, 15}); @@ -286,6 +297,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, bfp_with_15_bits_should_pass) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::BFP, 15}); @@ -306,6 +318,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, bfp_with_15_bits_without_ud_comp_len subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), 273, + 0, std::make_unique(), {compression_type::BFP, 15}); @@ -329,6 +342,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, if_message_num_prbs_equals_zero_deco subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique(), {compression_type::BFP, 9}); @@ -353,6 +367,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, if_message_contains_one_valid_sectio subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique(), {compression_type::BFP, 9}); @@ -378,6 +393,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, peek_filter_index) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique(), {compression_type::BFP, 9}); @@ -393,6 +409,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, peek_filter_index_returns_reserved_o subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique(), {compression_type::BFP, 9}); @@ -413,6 +430,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, peek_prach_filter_index) subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique(), {compression_type::BFP, 9}); @@ -437,6 +455,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, peek_slot_symbol_point) scs, nof_symbols, ru_nof_prbs, + 0, std::make_unique(), {compression_type::BFP, 9}); @@ -455,6 +474,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, return_invalid_slot_point_on_packet_ scs, nof_symbols, ru_nof_prbs, + 0, std::make_unique(), {compression_type::BFP, 9}); @@ -485,6 +505,7 @@ TEST(ofh_uplane_packet_decoder_static_impl, message_containing_more_than_one_sec subcarrier_spacing::kHz30, get_nsymb_per_slot(cyclic_prefix::NORMAL), ru_nof_prbs, + 0, std::make_unique(), {compression_type::BFP, 9}); From f75a7a0170600c02b50a6f0da42d11af665cc642 Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 20 Dec 2024 10:15:12 +0100 Subject: [PATCH 003/107] sched: fix logical channel test check for pending bytes --- .../scheduler/ue_scheduling/logical_channel_test.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp b/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp index a73072f2db..b531cf152e 100644 --- a/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp @@ -218,12 +218,13 @@ TEST(dl_logical_channel_test, mac_sdu_is_scheduled_if_tb_has_space) // RLC overhead. const unsigned RLC_SEGMENTATION_OVERHEAD = 4; unsigned req_bytes = get_mac_sdu_required_bytes(rem_sdu_size); - if (req_bytes == 258) { + unsigned lc_pending_bytes = lch_mng.total_pending_bytes() - RLC_SEGMENTATION_OVERHEAD; + if (req_bytes >= 254 and req_bytes <= 258) { // Note: account for ambiguity in transition between MAC subheader sizes. - req_bytes = 259; + req_bytes++; } - ASSERT_EQ(req_bytes, lch_mng.total_pending_bytes() - RLC_SEGMENTATION_OVERHEAD) - << "incorrect calculation of remaining pending tx bytes"; + ASSERT_EQ(req_bytes, lc_pending_bytes) + << fmt::format("incorrect calculation of remaining pending tx bytes for SDU of size={}", subpdu.sched_bytes); } rem_bytes -= allocated_bytes; From aa565fdc90c413c5f7dec325b44ec855b082886a Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 3 Dec 2024 16:46:53 +0000 Subject: [PATCH 004/107] f1u: added example config of multiple F1-U sockets --- configs/cu_up_f1u_multiple_sockets.yml | 14 ++++++++++++++ 1 file changed, 14 insertions(+) create mode 100644 configs/cu_up_f1u_multiple_sockets.yml diff --git a/configs/cu_up_f1u_multiple_sockets.yml b/configs/cu_up_f1u_multiple_sockets.yml new file mode 100644 index 0000000000..5f8242f4d6 --- /dev/null +++ b/configs/cu_up_f1u_multiple_sockets.yml @@ -0,0 +1,14 @@ +cu_up: + f1u: + socket: + - + bind_addr: 127.0.3.1 # address that the NG-U socket will bind to. + udp: + max_rx_msgs: 256 # maximum packets read from the socket in a single syscall. + pool_threshold: 0.9 # Pool occupancy threshold, after which packets are dropped. + + - + bind_addr: 127.0.3.2 + udp: + max_rx_msgs: 256 # maximum packets read from the socket in a single syscall. + pool_threshold: 0.9 # Pool occupancy threshold, after which packets are dropped. From 8eb7824e36a760376f713bec6bcd074935393677 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 3 Dec 2024 16:45:53 +0000 Subject: [PATCH 005/107] cu,f1u: added cli11 for multiple socket in CU's F1-U --- apps/cu/cu.cpp | 7 ++-- apps/cu/cu_appconfig.h | 19 +++++---- apps/cu/cu_appconfig_cli11_schema.cpp | 40 +++++++++++++------ apps/cu/cu_appconfig_yaml_writer.cpp | 31 ++++++++++---- apps/services/network/CMakeLists.txt | 4 +- .../network/udp_config_yaml_writer.cpp | 24 +++++++++++ .../services/network/udp_config_yaml_writer.h | 22 ++++++++++ .../cu_up/cu_up_unit_config_yaml_writer.cpp | 9 +---- 8 files changed, 119 insertions(+), 37 deletions(-) create mode 100644 apps/services/network/udp_config_yaml_writer.cpp create mode 100644 apps/services/network/udp_config_yaml_writer.h diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index b55a2f84fc..54921f8c41 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -283,14 +283,15 @@ int main(int argc, char** argv) cu_f1u_gtpu_msg.gtpu_pcap = cu_up_dlt_pcaps.f1u.get(); std::unique_ptr cu_f1u_gtpu_demux = create_gtpu_demux(cu_f1u_gtpu_msg); udp_network_gateway_config cu_f1u_gw_config = {}; - cu_f1u_gw_config.bind_address = cu_cfg.nru_cfg.bind_addr; + cu_f1u_gw_config.bind_address = cu_cfg.f1u_cfg.f1u_socket_cfg[0].bind_addr; cu_f1u_gw_config.bind_port = GTPU_PORT; cu_f1u_gw_config.reuse_addr = false; - cu_f1u_gw_config.pool_occupancy_threshold = cu_cfg.nru_cfg.pool_occupancy_threshold; + cu_f1u_gw_config.pool_occupancy_threshold = cu_cfg.f1u_cfg.f1u_socket_cfg[0].udp_config.pool_threshold; + cu_f1u_gw_config.rx_max_mmsg = cu_cfg.f1u_cfg.f1u_socket_cfg[0].udp_config.rx_max_msgs; std::unique_ptr cu_f1u_gw = create_udp_gtpu_gateway( cu_f1u_gw_config, *epoll_broker, workers.cu_up_exec_mapper->io_ul_executor(), *workers.non_rt_low_prio_exec); std::unique_ptr cu_f1u_conn = srs_cu_up::create_split_f1u_gw( - {*cu_f1u_gw, *cu_f1u_gtpu_demux, *cu_up_dlt_pcaps.f1u, GTPU_PORT, cu_cfg.nru_cfg.ext_addr}); + {*cu_f1u_gw, *cu_f1u_gtpu_demux, *cu_up_dlt_pcaps.f1u, GTPU_PORT, cu_cfg.f1u_cfg.f1u_socket_cfg[0].ext_addr}); // Create E1AP local connector std::unique_ptr e1_gw = diff --git a/apps/cu/cu_appconfig.h b/apps/cu/cu_appconfig.h index 09851e64d1..dd53ac1872 100644 --- a/apps/cu/cu_appconfig.h +++ b/apps/cu/cu_appconfig.h @@ -12,18 +12,23 @@ #include "apps/services/buffer_pool/buffer_pool_appconfig.h" #include "apps/services/logger/logger_appconfig.h" +#include "apps/services/network/udp_cli11_schema.h" #include "apps/services/worker_manager/worker_manager_appconfig.h" #include namespace srsran { namespace srs_cu { -/// NR-U configuration -struct cu_nru_appconfig { - std::string bind_addr = "127.0.10.1"; // Bind address used by the F1-U interface - std::string ext_addr = "auto"; // External address advertised by the F1-U interface - int udp_rx_max_msgs = 256; // Max number of UDP packets received by a single syscall on the F1-U interface. - float pool_occupancy_threshold = 0.9; // Buffer pool occupancy threshold after which packets are dropped. +/// F1-U sockets configuration +struct cu_f1u_socket_appconfig { + std::string bind_addr = "127.0.10.1"; // Bind address used by the F1-U interface + std::string ext_addr = "auto"; // External address advertised by the F1-U interface + udp_appconfig udp_config; +}; + +/// F1-U configuration +struct cu_f1u_appconfig { + std::vector f1u_socket_cfg; }; /// F1AP configuration @@ -43,7 +48,7 @@ struct cu_appconfig { /// Expert configuration. expert_execution_appconfig expert_execution_cfg; /// NR-U - srs_cu::cu_nru_appconfig nru_cfg; + srs_cu::cu_f1u_appconfig f1u_cfg; /// F1AP srs_cu::cu_f1ap_appconfig f1ap_cfg; /// Buffer pool configuration. diff --git a/apps/cu/cu_appconfig_cli11_schema.cpp b/apps/cu/cu_appconfig_cli11_schema.cpp index b75625741c..0fd636641f 100644 --- a/apps/cu/cu_appconfig_cli11_schema.cpp +++ b/apps/cu/cu_appconfig_cli11_schema.cpp @@ -14,6 +14,7 @@ #include "apps/services/worker_manager/worker_manager_cli11_schema.h" #include "cu_appconfig.h" #include "srsran/support/cli11_utils.h" +#include "srsran/support/config_parsers.h" using namespace srsran; @@ -22,22 +23,37 @@ static void configure_cli11_f1ap_args(CLI::App& app, srs_cu::cu_f1ap_appconfig& add_option(app, "--bind_addr", f1ap_params.bind_addr, "F1-C bind address")->capture_default_str(); } -static void configure_cli11_nru_args(CLI::App& app, srs_cu::cu_nru_appconfig& nru_cfg) +static void configure_cli11_f1u_socket_args(CLI::App& app, srs_cu::cu_f1u_socket_appconfig& f1u_cfg) { add_option(app, "--bind_addr", - nru_cfg.bind_addr, + f1u_cfg.bind_addr, "Default local IP address interfaces bind to, unless a specific bind address is specified") ->check(CLI::ValidIPV4); app.add_option( - "--ext_addr", nru_cfg.ext_addr, "External IP address that is advertised to receive F1-U packets from the DU"); - add_option(app, "--udp_max_rx_msgs", nru_cfg.udp_rx_max_msgs, "Maximum amount of messages RX in a single syscall"); - add_option(app, - "--pool_threshold", - nru_cfg.pool_occupancy_threshold, - "Pool occupancy threshold after which packets are dropped") - ->check(CLI::Range(0.0, 1.0)); - ; + "--ext_addr", f1u_cfg.ext_addr, "External IP address that is advertised to receive F1-U packets from the DU"); + + configure_cli11_with_udp_config_schema(app, f1u_cfg.udp_config); +} + +static void configure_cli11_f1u_args(CLI::App& app, srs_cu::cu_f1u_appconfig& f1u_params) +{ + // Add option for multiple sockets, for usage with different slices, 5QIs or parallization. + auto sock_lambda = [&f1u_params](const std::vector& values) { + // Prepare the radio bearers + f1u_params.f1u_socket_cfg.resize(values.size()); + + // Format every F1-U socket configuration. + for (unsigned i = 0, e = values.size(); i != e; ++i) { + CLI::App subapp("NG-U socket parameters", "NG-U socket config, item #" + std::to_string(i)); + subapp.config_formatter(create_yaml_config_parser()); + subapp.allow_config_extras(CLI::config_extras_mode::capture); + configure_cli11_f1u_socket_args(subapp, f1u_params.f1u_socket_cfg[i]); + std::istringstream ss(values[i]); + subapp.parse_from_stream(ss); + } + }; + add_option_cell(app, "--socket", sock_lambda, "Configures UDP/IP socket parameters of the N3 interface"); } void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_cfg) @@ -58,6 +74,6 @@ void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfi // NR-U section. CLI::App* cu_up_subcmd = add_subcommand(app, "cu_up", "CU-UP parameters")->configurable(); - CLI::App* nru_subcmd = add_subcommand(*cu_up_subcmd, "nru", "NR-U parameters")->configurable(); - configure_cli11_nru_args(*nru_subcmd, cu_cfg.nru_cfg); + CLI::App* f1u_subcmd = add_subcommand(*cu_up_subcmd, "f1u", "NR-U parameters")->configurable(); + configure_cli11_f1u_args(*f1u_subcmd, cu_cfg.f1u_cfg); } diff --git a/apps/cu/cu_appconfig_yaml_writer.cpp b/apps/cu/cu_appconfig_yaml_writer.cpp index 97859d787f..2107338b63 100644 --- a/apps/cu/cu_appconfig_yaml_writer.cpp +++ b/apps/cu/cu_appconfig_yaml_writer.cpp @@ -10,6 +10,7 @@ #include "cu_appconfig_yaml_writer.h" #include "apps/services/logger/logger_appconfig_yaml_writer.h" +#include "apps/services/network/udp_config_yaml_writer.h" #include "cu_appconfig.h" using namespace srsran; @@ -27,13 +28,29 @@ static void fill_cu_appconfig_f1ap_section(YAML::Node node, const srs_cu::cu_f1a f1ap_node["bind_address"] = config.bind_addr; } -static void fill_cu_appconfig_nru_section(YAML::Node node, const srs_cu::cu_nru_appconfig& config) +static void fill_cu_up_f1u_socket_entry(YAML::Node& node, const srsran::srs_cu::cu_f1u_socket_appconfig& config) { - YAML::Node cu_up_node = node["cu_up"]; - YAML::Node nru_node = cu_up_node["nru"]; - nru_node["udp_max_rx_msgs"] = config.udp_rx_max_msgs; - nru_node["bind_addr"] = config.bind_addr; - nru_node["ext_addr"] = config.ext_addr; + node["bind_addr"] = config.bind_addr; + node["ext_addr"] = config.ext_addr; + fill_udp_config_in_yaml_schema(node["udp"], config.udp_config); +} + +static void fill_cu_up_f1u_socket_section(YAML::Node node, + const std::vector& sock_cfg) +{ + auto sock_node = node["socket"]; + for (const auto& cfg : sock_cfg) { + YAML::Node node_sock; + fill_cu_up_f1u_socket_entry(node_sock, cfg); + sock_node.push_back(node_sock); + } +} + +static void fill_cu_appconfig_f1u_section(YAML::Node node, const srs_cu::cu_f1u_appconfig& config) +{ + YAML::Node cu_up_node = node["cu_up"]; + YAML::Node f1u_node = cu_up_node["f1u"]; + fill_cu_up_f1u_socket_section(node, config.f1u_socket_cfg); } void srsran::fill_cu_appconfig_in_yaml_schema(YAML::Node& node, const cu_appconfig& config) @@ -41,5 +58,5 @@ void srsran::fill_cu_appconfig_in_yaml_schema(YAML::Node& node, const cu_appconf fill_logger_appconfig_in_yaml_schema(node, config.log_cfg); fill_cu_appconfig_buffer_pool_section(node["buffer_pool"], config.buffer_pool_config); fill_cu_appconfig_f1ap_section(node, config.f1ap_cfg); - fill_cu_appconfig_nru_section(node, config.nru_cfg); + fill_cu_appconfig_f1u_section(node, config.f1u_cfg); } diff --git a/apps/services/network/CMakeLists.txt b/apps/services/network/CMakeLists.txt index 94b7063c02..103a69e9a0 100644 --- a/apps/services/network/CMakeLists.txt +++ b/apps/services/network/CMakeLists.txt @@ -6,7 +6,9 @@ # the distribution. # -set(SOURCES udp_cli11_schema.cpp) +set(SOURCES udp_cli11_schema.cpp + udp_config_yaml_writer.cpp +) add_library(srsran_network_app_service STATIC ${SOURCES}) target_include_directories(srsran_network_app_service PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/services/network/udp_config_yaml_writer.cpp b/apps/services/network/udp_config_yaml_writer.cpp new file mode 100644 index 0000000000..911dc04669 --- /dev/null +++ b/apps/services/network/udp_config_yaml_writer.cpp @@ -0,0 +1,24 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "apps/services/network/udp_config_yaml_writer.h" +#include "apps/services/network/udp_cli11_schema.h" +#include + +namespace srsran { + +/// Fills the UDP configuration in the given YAML node. +void fill_udp_config_in_yaml_schema(YAML::Node node, const udp_appconfig& config) +{ + node["max_rx_msgs"] = config.rx_max_msgs; + node["pool_threshold"] = config.pool_threshold; +} + +} // namespace srsran diff --git a/apps/services/network/udp_config_yaml_writer.h b/apps/services/network/udp_config_yaml_writer.h new file mode 100644 index 0000000000..f77ed268d9 --- /dev/null +++ b/apps/services/network/udp_config_yaml_writer.h @@ -0,0 +1,22 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include + +namespace srsran { + +struct udp_appconfig; + +/// Fills the UDP configuration in the given YAML node. +void fill_udp_config_in_yaml_schema(YAML::Node node, const udp_appconfig& config); + +} // namespace srsran diff --git a/apps/units/o_cu_up/cu_up/cu_up_unit_config_yaml_writer.cpp b/apps/units/o_cu_up/cu_up/cu_up_unit_config_yaml_writer.cpp index 967146d4e2..1b10034b1a 100644 --- a/apps/units/o_cu_up/cu_up/cu_up_unit_config_yaml_writer.cpp +++ b/apps/units/o_cu_up/cu_up/cu_up_unit_config_yaml_writer.cpp @@ -9,23 +9,18 @@ */ #include "cu_up_unit_config_yaml_writer.h" +#include "apps/services/network/udp_config_yaml_writer.h" #include "cu_up_unit_config.h" #include "srsran/adt/span.h" using namespace srsran; -static void fill_cu_up_udp_section(YAML::Node node, const udp_appconfig& config) -{ - node["max_rx_msgs"] = config.rx_max_msgs; - node["pool_threshold"] = config.pool_threshold; -} - static void fill_cu_up_ngu_socket_entry(YAML::Node& node, const cu_up_unit_ngu_socket_config& config) { node["bind_addr"] = config.bind_addr; node["bind_interface"] = config.bind_interface; node["ext_addr"] = config.ext_addr; - fill_cu_up_udp_section(node["udp"], config.udp_config); + fill_udp_config_in_yaml_schema(node["udp"], config.udp_config); } static void fill_cu_up_ngu_socket_section(YAML::Node node, const std::vector& sock_cfg) From 0c09bf4273cd7f729dfb887fa2271d5e389f9edb Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 10 Dec 2024 17:44:27 +0000 Subject: [PATCH 006/107] cu_up,f1u: started to added f1-u session manager This was done to support multiple sockets per F1-U interface. --- .../srsran/f1u/cu_up/f1u_session_manager.h | 24 +++++++++++ lib/cu_up/pdu_session_manager_impl.h | 40 ++++++++++--------- lib/f1u/CMakeLists.txt | 2 +- lib/f1u/cu_up/f1u_session_manager_impl.cpp | 29 ++++++++++++++ lib/f1u/cu_up/f1u_session_manager_impl.h | 30 ++++++++++++++ 5 files changed, 105 insertions(+), 20 deletions(-) create mode 100644 include/srsran/f1u/cu_up/f1u_session_manager.h create mode 100644 lib/f1u/cu_up/f1u_session_manager_impl.cpp create mode 100644 lib/f1u/cu_up/f1u_session_manager_impl.h diff --git a/include/srsran/f1u/cu_up/f1u_session_manager.h b/include/srsran/f1u/cu_up/f1u_session_manager.h new file mode 100644 index 0000000000..582ef8fd72 --- /dev/null +++ b/include/srsran/f1u/cu_up/f1u_session_manager.h @@ -0,0 +1,24 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/f1u/cu_up/f1u_gateway.h" + +namespace srsran::srs_cu_up { + +class f1u_session_manager +{ +public: + virtual ~f1u_session_manager() = default; + virtual f1u_cu_up_udp_gateway& get_next_f1u_gateway() = 0; +}; + +} // namespace srsran::srs_cu_up diff --git a/lib/cu_up/pdu_session_manager_impl.h b/lib/cu_up/pdu_session_manager_impl.h index 72ae4d040c..d42509e414 100644 --- a/lib/cu_up/pdu_session_manager_impl.h +++ b/lib/cu_up/pdu_session_manager_impl.h @@ -18,6 +18,7 @@ #include "srsran/cu_up/cu_up_types.h" #include "srsran/e1ap/common/e1ap_types.h" #include "srsran/f1u/cu_up/f1u_gateway.h" +#include "srsran/f1u/cu_up/f1u_session_manager.h" #include "srsran/gtpu/gtpu_demux.h" #include "srsran/gtpu/gtpu_teid_pool.h" #include "srsran/gtpu/gtpu_tunnel_common_tx.h" @@ -73,25 +74,26 @@ class pdu_session_manager_impl final : public pdu_session_manager_ctrl drb_setup_result handle_drb_to_setup_item(pdu_session& new_session, const e1ap_drb_to_setup_item_ng_ran& drb_to_setup); - ue_index_t ue_index; - const std::map qos_cfg; - const security::sec_as_config& security_info; - const n3_interface_config& n3_config; - cu_up_test_mode_config test_mode_config; - cu_up_ue_logger& logger; - unique_timer& ue_inactivity_timer; - timer_factory ue_dl_timer_factory; - timer_factory ue_ul_timer_factory; - timer_factory ue_ctrl_timer_factory; - gtpu_teid_pool& n3_teid_allocator; - gtpu_teid_pool& f1u_teid_allocator; - gtpu_demux_ctrl& gtpu_rx_demux; - task_executor& ue_dl_exec; - task_executor& ue_ul_exec; - task_executor& ue_ctrl_exec; - task_executor& crypto_exec; - dlt_pcap& gtpu_pcap; - f1u_cu_up_gateway& f1u_gw; + ue_index_t ue_index; + const std::map qos_cfg; + const security::sec_as_config& security_info; + const n3_interface_config& n3_config; + cu_up_test_mode_config test_mode_config; + cu_up_ue_logger& logger; + unique_timer& ue_inactivity_timer; + timer_factory ue_dl_timer_factory; + timer_factory ue_ul_timer_factory; + timer_factory ue_ctrl_timer_factory; + gtpu_teid_pool& n3_teid_allocator; + gtpu_teid_pool& f1u_teid_allocator; + gtpu_demux_ctrl& gtpu_rx_demux; + task_executor& ue_dl_exec; + task_executor& ue_ul_exec; + task_executor& ue_ctrl_exec; + task_executor& crypto_exec; + dlt_pcap& gtpu_pcap; + f1u_cu_up_gateway& f1u_gw; + // f1u_session_manager& f1u_session_mngr; ngu_session_manager& ngu_session_mngr; std::map> pdu_sessions; // key is pdu_session_id }; diff --git a/lib/f1u/CMakeLists.txt b/lib/f1u/CMakeLists.txt index 1409d8c276..78b63b49ae 100644 --- a/lib/f1u/CMakeLists.txt +++ b/lib/f1u/CMakeLists.txt @@ -14,7 +14,7 @@ add_library(srsran_f1u_connector local_connector/f1u_local_connector.cpp) target_link_libraries(srsran_f1u_connector srsran_f1u_cu_up) #Split connector -add_library(srsran_f1u_cu_up_split_connector cu_up/split_connector/f1u_split_connector_factory.cpp cu_up/split_connector/f1u_split_connector.cpp ) +add_library(srsran_f1u_cu_up_split_connector cu_up/f1u_session_manager_impl.cpp cu_up/split_connector/f1u_split_connector_factory.cpp cu_up/split_connector/f1u_split_connector.cpp ) target_link_libraries(srsran_f1u_cu_up_split_connector srsran_f1u_cu_up) add_library(srsran_f1u_du_split_connector du/split_connector/f1u_split_connector.cpp du/split_connector/f1u_split_connector_factory.cpp) diff --git a/lib/f1u/cu_up/f1u_session_manager_impl.cpp b/lib/f1u/cu_up/f1u_session_manager_impl.cpp new file mode 100644 index 0000000000..f4b7e5cb0a --- /dev/null +++ b/lib/f1u/cu_up/f1u_session_manager_impl.cpp @@ -0,0 +1,29 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "f1u_session_manager_impl.h" +#include "srsran/support/srsran_assert.h" + +using namespace srsran; +using namespace srs_cu_up; + +f1u_session_manager_impl::f1u_session_manager_impl( + const std::vector>& f1u_gws_) : + f1u_gws(f1u_gws_) +{ + srsran_assert(not f1u_gws.empty(), "F1-U gateways cannot be empty"); +} + +f1u_cu_up_udp_gateway& f1u_session_manager_impl::get_next_f1u_gateway() +{ + uint32_t index = next_gw % f1u_gws.size(); + next_gw++; + return *f1u_gws[index]; +} diff --git a/lib/f1u/cu_up/f1u_session_manager_impl.h b/lib/f1u/cu_up/f1u_session_manager_impl.h new file mode 100644 index 0000000000..495c68607b --- /dev/null +++ b/lib/f1u/cu_up/f1u_session_manager_impl.h @@ -0,0 +1,30 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/f1u/cu_up/f1u_session_manager.h" + +namespace srsran::srs_cu_up { + +class f1u_session_manager_impl : public f1u_session_manager +{ +public: + ~f1u_session_manager_impl() override = default; + explicit f1u_session_manager_impl(const std::vector>& f1u_gws_); + + f1u_cu_up_udp_gateway& get_next_f1u_gateway() override; + +private: + const std::vector>& f1u_gws; + uint32_t next_gw = 0; +}; + +} // namespace srsran::srs_cu_up From ce2640449d26a1cf48d05aec513866959a8ddd6d Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Wed, 11 Dec 2024 12:59:10 +0000 Subject: [PATCH 007/107] cu_up,f1u: added unit test for f1u session manager --- .../srsran/f1u/cu_up/f1u_session_manager.h | 4 +- lib/f1u/cu_up/f1u_session_manager_impl.cpp | 5 +- lib/f1u/cu_up/f1u_session_manager_impl.h | 8 +-- tests/unittests/cu_up/cu_up_test_helpers.h | 6 +- tests/unittests/f1u/cu_up/CMakeLists.txt | 6 ++ .../cu_up/f1u_cu_up_session_manager_test.cpp | 51 +++++++++++++++++ .../cu_up/f1u_cu_up_session_manager_test.h | 55 +++++++++++++++++++ 7 files changed, 125 insertions(+), 10 deletions(-) create mode 100644 tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.cpp create mode 100644 tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.h diff --git a/include/srsran/f1u/cu_up/f1u_session_manager.h b/include/srsran/f1u/cu_up/f1u_session_manager.h index 582ef8fd72..bf97e535bf 100644 --- a/include/srsran/f1u/cu_up/f1u_session_manager.h +++ b/include/srsran/f1u/cu_up/f1u_session_manager.h @@ -17,8 +17,8 @@ namespace srsran::srs_cu_up { class f1u_session_manager { public: - virtual ~f1u_session_manager() = default; - virtual f1u_cu_up_udp_gateway& get_next_f1u_gateway() = 0; + virtual ~f1u_session_manager() = default; + virtual f1u_cu_up_gateway& get_next_f1u_gateway() = 0; }; } // namespace srsran::srs_cu_up diff --git a/lib/f1u/cu_up/f1u_session_manager_impl.cpp b/lib/f1u/cu_up/f1u_session_manager_impl.cpp index f4b7e5cb0a..252da391ab 100644 --- a/lib/f1u/cu_up/f1u_session_manager_impl.cpp +++ b/lib/f1u/cu_up/f1u_session_manager_impl.cpp @@ -14,14 +14,13 @@ using namespace srsran; using namespace srs_cu_up; -f1u_session_manager_impl::f1u_session_manager_impl( - const std::vector>& f1u_gws_) : +f1u_session_manager_impl::f1u_session_manager_impl(const std::vector>& f1u_gws_) : f1u_gws(f1u_gws_) { srsran_assert(not f1u_gws.empty(), "F1-U gateways cannot be empty"); } -f1u_cu_up_udp_gateway& f1u_session_manager_impl::get_next_f1u_gateway() +f1u_cu_up_gateway& f1u_session_manager_impl::get_next_f1u_gateway() { uint32_t index = next_gw % f1u_gws.size(); next_gw++; diff --git a/lib/f1u/cu_up/f1u_session_manager_impl.h b/lib/f1u/cu_up/f1u_session_manager_impl.h index 495c68607b..8c82014a16 100644 --- a/lib/f1u/cu_up/f1u_session_manager_impl.h +++ b/lib/f1u/cu_up/f1u_session_manager_impl.h @@ -18,13 +18,13 @@ class f1u_session_manager_impl : public f1u_session_manager { public: ~f1u_session_manager_impl() override = default; - explicit f1u_session_manager_impl(const std::vector>& f1u_gws_); + explicit f1u_session_manager_impl(const std::vector>& f1u_gws_); - f1u_cu_up_udp_gateway& get_next_f1u_gateway() override; + f1u_cu_up_gateway& get_next_f1u_gateway() override; private: - const std::vector>& f1u_gws; - uint32_t next_gw = 0; + const std::vector>& f1u_gws; + uint32_t next_gw = 0; }; } // namespace srsran::srs_cu_up diff --git a/tests/unittests/cu_up/cu_up_test_helpers.h b/tests/unittests/cu_up/cu_up_test_helpers.h index ab8bd74431..6c4dc4c74c 100644 --- a/tests/unittests/cu_up/cu_up_test_helpers.h +++ b/tests/unittests/cu_up/cu_up_test_helpers.h @@ -25,6 +25,7 @@ #include #include #include +#include constexpr auto default_wait_timeout = std::chrono::seconds(3); @@ -221,6 +222,7 @@ class dummy_f1u_gateway final : public f1u_cu_up_gateway { private: dummy_inner_f1u_bearer& bearer; + std::string bind_ip_addr = "127.0.0.1"; public: explicit dummy_f1u_gateway(dummy_inner_f1u_bearer& bearer_) : bearer(bearer_) {} @@ -249,7 +251,9 @@ class dummy_f1u_gateway final : public f1u_cu_up_gateway removed_ul_teid_list.push_back(ul_up_tnl_info.gtp_teid); } - expected get_cu_bind_address() const override { return "127.0.0.1"; } + expected get_cu_bind_address() const override { return bind_ip_addr; } + + void set_cu_bind_address(std::string ip_addr) { bind_ip_addr = std::move(ip_addr); } std::list created_ul_teid_list = {}; std::list attached_ul_teid_list = {}; diff --git a/tests/unittests/f1u/cu_up/CMakeLists.txt b/tests/unittests/f1u/cu_up/CMakeLists.txt index 3829de1ec7..4b002fe3a4 100644 --- a/tests/unittests/f1u/cu_up/CMakeLists.txt +++ b/tests/unittests/f1u/cu_up/CMakeLists.txt @@ -11,3 +11,9 @@ target_link_libraries(f1u_cu_up_bearer_test srsran_f1u_cu_up srsran_support srsl target_include_directories(f1u_cu_up_bearer_test PRIVATE ${CMAKE_SOURCE_DIR}) add_test(f1u_cu_up_bearer_test f1u_cu_up_bearer_test) gtest_discover_tests(f1u_cu_up_bearer_test) + +add_executable(f1u_cu_up_session_manager_test f1u_cu_up_session_manager_test.cpp) +target_link_libraries(f1u_cu_up_session_manager_test srsran_f1u_cu_up_split_connector srsran_support srslog gtest gtest_main) +target_include_directories(f1u_cu_up_session_manager_test PRIVATE ${CMAKE_SOURCE_DIR}) +add_test(f1u_cu_up_session_manager_test f1u_cu_up_session_manager_test) +gtest_discover_tests(f1u_cu_up_session_manager_test) diff --git a/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.cpp b/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.cpp new file mode 100644 index 0000000000..15606918d1 --- /dev/null +++ b/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.cpp @@ -0,0 +1,51 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "f1u_cu_up_session_manager_test.h" +#include + +using namespace srsran; +using namespace srs_cu_up; + +TEST_F(f1u_session_manager_test, mngr_creation) +{ + ASSERT_NE(f1u_session_mngr, nullptr); +} + +TEST_F(f1u_session_manager_test, rr_session_selection) +{ + { + const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); + expected ip_addr; + ip_addr = ngu_gw.get_cu_bind_address(); + ASSERT_TRUE(ip_addr.has_value()); + ASSERT_EQ(ip_addr.value(), "127.0.0.1"); + } + { + const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); + expected ip_addr; + ip_addr = ngu_gw.get_cu_bind_address(); + ASSERT_TRUE(ip_addr.has_value()); + ASSERT_EQ(ip_addr.value(), "127.0.0.2"); + } + { + const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); + expected ip_addr; + ip_addr = ngu_gw.get_cu_bind_address(); + ASSERT_TRUE(ip_addr.has_value()); + ASSERT_EQ(ip_addr.value(), "127.0.0.1"); + } +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.h b/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.h new file mode 100644 index 0000000000..4699bd3a94 --- /dev/null +++ b/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.h @@ -0,0 +1,55 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "lib/f1u/cu_up/f1u_session_manager_impl.h" +#include "tests/unittests/cu_up/cu_up_test_helpers.h" +#include "srsran/srslog/srslog.h" +#include + +namespace srsran::srs_cu_up { + +/// Fixture base class for PDU session manager tests +class f1u_session_manager_test : public ::testing::Test +{ + void SetUp() override + { + srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::debug); + srslog::init(); + + unsigned nof_gws = 2; + for (unsigned i = 0; i < nof_gws; i++) { + std::unique_ptr f1u_gw = std::make_unique(f1u_bearer); + std::string addr = fmt::format("127.0.0.{}", 1 + i); + f1u_gw->set_cu_bind_address(addr); + f1u_gws.push_back(std::move(f1u_gw)); + } + + // todo init ngu session manager + f1u_session_mngr = std::make_unique(f1u_gws); + } + + void TearDown() override + { + f1u_gws.clear(); + f1u_session_mngr.reset(); + + // flush logger after each test + srslog::flush(); + } + +protected: + std::unique_ptr f1u_session_mngr; + std::vector> f1u_gws; + dummy_inner_f1u_bearer f1u_bearer; +}; + +} // namespace srsran::srs_cu_up From 1b77baafef9a0685d398532a4603121bfca43137 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 12 Dec 2024 16:01:56 +0000 Subject: [PATCH 008/107] cu: added ngu_session_manager to cu app --- apps/cu/cu.cpp | 38 +++++++++++-------- .../f1u/cu_up/f1u_session_manager_factory.h | 22 +++++++++++ lib/f1u/CMakeLists.txt | 2 +- lib/f1u/cu_up/f1u_session_manager_factory.cpp | 23 +++++++++++ 4 files changed, 68 insertions(+), 17 deletions(-) create mode 100644 include/srsran/f1u/cu_up/f1u_session_manager_factory.h create mode 100644 lib/f1u/cu_up/f1u_session_manager_factory.cpp diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 54921f8c41..5254d52de3 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -29,6 +29,7 @@ #include "srsran/e1ap/gateways/e1_local_connector_factory.h" #include "srsran/e2/e2ap_config_translators.h" #include "srsran/f1ap/gateways/f1c_network_server_factory.h" +#include "srsran/f1u/cu_up/f1u_session_manager_factory.h" #include "srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h" #include "srsran/gateways/udp_network_gateway.h" #include "srsran/gtpu/gtpu_config.h" @@ -277,21 +278,26 @@ int main(int argc, char** argv) {f1c_sctp_cfg, *epoll_broker, *workers.non_rt_hi_prio_exec, *cu_cp_dlt_pcaps.f1ap}); std::unique_ptr cu_f1c_gw = srsran::create_f1c_gateway_server(f1c_server_cfg); - // Create F1-U GW (TODO factory and cleanup). - gtpu_demux_creation_request cu_f1u_gtpu_msg = {}; - cu_f1u_gtpu_msg.cfg.warn_on_drop = true; - cu_f1u_gtpu_msg.gtpu_pcap = cu_up_dlt_pcaps.f1u.get(); - std::unique_ptr cu_f1u_gtpu_demux = create_gtpu_demux(cu_f1u_gtpu_msg); - udp_network_gateway_config cu_f1u_gw_config = {}; - cu_f1u_gw_config.bind_address = cu_cfg.f1u_cfg.f1u_socket_cfg[0].bind_addr; - cu_f1u_gw_config.bind_port = GTPU_PORT; - cu_f1u_gw_config.reuse_addr = false; - cu_f1u_gw_config.pool_occupancy_threshold = cu_cfg.f1u_cfg.f1u_socket_cfg[0].udp_config.pool_threshold; - cu_f1u_gw_config.rx_max_mmsg = cu_cfg.f1u_cfg.f1u_socket_cfg[0].udp_config.rx_max_msgs; - std::unique_ptr cu_f1u_gw = create_udp_gtpu_gateway( - cu_f1u_gw_config, *epoll_broker, workers.cu_up_exec_mapper->io_ul_executor(), *workers.non_rt_low_prio_exec); - std::unique_ptr cu_f1u_conn = srs_cu_up::create_split_f1u_gw( - {*cu_f1u_gw, *cu_f1u_gtpu_demux, *cu_up_dlt_pcaps.f1u, GTPU_PORT, cu_cfg.f1u_cfg.f1u_socket_cfg[0].ext_addr}); + // Create F1-U GW. + std::vector> f1u_gws; + for (const srs_cu::cu_f1u_socket_appconfig& sock_cfg : cu_cfg.f1u_cfg.f1u_socket_cfg) { + gtpu_demux_creation_request cu_f1u_gtpu_msg = {}; + cu_f1u_gtpu_msg.cfg.warn_on_drop = true; + cu_f1u_gtpu_msg.gtpu_pcap = cu_up_dlt_pcaps.f1u.get(); + std::unique_ptr cu_f1u_gtpu_demux = create_gtpu_demux(cu_f1u_gtpu_msg); + udp_network_gateway_config cu_f1u_gw_config = {}; + cu_f1u_gw_config.bind_address = sock_cfg.bind_addr; + cu_f1u_gw_config.bind_port = GTPU_PORT; + cu_f1u_gw_config.reuse_addr = false; + cu_f1u_gw_config.pool_occupancy_threshold = sock_cfg.udp_config.pool_threshold; + cu_f1u_gw_config.rx_max_mmsg = sock_cfg.udp_config.rx_max_msgs; + std::unique_ptr cu_f1u_gw = create_udp_gtpu_gateway( + cu_f1u_gw_config, *epoll_broker, workers.cu_up_exec_mapper->io_ul_executor(), *workers.non_rt_low_prio_exec); + std::unique_ptr cu_f1u_conn = srs_cu_up::create_split_f1u_gw( + {*cu_f1u_gw, *cu_f1u_gtpu_demux, *cu_up_dlt_pcaps.f1u, GTPU_PORT, sock_cfg.ext_addr}); + f1u_gws.push_back(std::move(cu_f1u_conn)); + } + std::unique_ptr f1u_session_mngr = create_f1u_cu_up_session_manager(f1u_gws); // Create E1AP local connector std::unique_ptr e1_gw = @@ -362,7 +368,7 @@ int main(int argc, char** argv) o_cuup_unit_deps.workers = &workers; o_cuup_unit_deps.cu_up_e2_exec = workers.cu_e2_exec; o_cuup_unit_deps.e1ap_conn_client = e1_gw.get(); - o_cuup_unit_deps.f1u_gateway = cu_f1u_conn.get(); + o_cuup_unit_deps.f1u_gateway = f1u_gws[0].get(); o_cuup_unit_deps.gtpu_pcap = cu_up_dlt_pcaps.n3.get(); o_cuup_unit_deps.timers = cu_timers; o_cuup_unit_deps.io_brk = epoll_broker.get(); diff --git a/include/srsran/f1u/cu_up/f1u_session_manager_factory.h b/include/srsran/f1u/cu_up/f1u_session_manager_factory.h new file mode 100644 index 0000000000..3f0047078a --- /dev/null +++ b/include/srsran/f1u/cu_up/f1u_session_manager_factory.h @@ -0,0 +1,22 @@ +/* + * + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/f1u/cu_up/f1u_session_manager.h" + +namespace srsran::srs_cu_up { + +/// \brief Creates an F1-U bearer for the CU-UP. +std::unique_ptr +create_f1u_cu_up_session_manager(const std::vector>& f1u_gws); + +} // namespace srsran::srs_cu_up diff --git a/lib/f1u/CMakeLists.txt b/lib/f1u/CMakeLists.txt index 78b63b49ae..3b3d82b9b3 100644 --- a/lib/f1u/CMakeLists.txt +++ b/lib/f1u/CMakeLists.txt @@ -14,7 +14,7 @@ add_library(srsran_f1u_connector local_connector/f1u_local_connector.cpp) target_link_libraries(srsran_f1u_connector srsran_f1u_cu_up) #Split connector -add_library(srsran_f1u_cu_up_split_connector cu_up/f1u_session_manager_impl.cpp cu_up/split_connector/f1u_split_connector_factory.cpp cu_up/split_connector/f1u_split_connector.cpp ) +add_library(srsran_f1u_cu_up_split_connector cu_up/f1u_session_manager_impl.cpp cu_up/f1u_session_manager_factory.cpp cu_up/split_connector/f1u_split_connector_factory.cpp cu_up/split_connector/f1u_split_connector.cpp ) target_link_libraries(srsran_f1u_cu_up_split_connector srsran_f1u_cu_up) add_library(srsran_f1u_du_split_connector du/split_connector/f1u_split_connector.cpp du/split_connector/f1u_split_connector_factory.cpp) diff --git a/lib/f1u/cu_up/f1u_session_manager_factory.cpp b/lib/f1u/cu_up/f1u_session_manager_factory.cpp new file mode 100644 index 0000000000..12144ceb0f --- /dev/null +++ b/lib/f1u/cu_up/f1u_session_manager_factory.cpp @@ -0,0 +1,23 @@ +/* + * + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/f1u/cu_up/f1u_session_manager_factory.h" +#include "f1u_session_manager_impl.h" +#include "srsran/f1u/cu_up/f1u_gateway.h" + +using namespace srsran; +using namespace srs_cu_up; + +std::unique_ptr +srsran::srs_cu_up::create_f1u_cu_up_session_manager(const std::vector>& f1u_gws) +{ + return std::make_unique(f1u_gws); +} From 75826eafb86be01c111fecfa6fbfca4305b2b305 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 13 Dec 2024 11:00:54 +0000 Subject: [PATCH 009/107] f1u: added f1u session manager to split connector --- .../split_connector/f1u_split_connector.cpp | 16 ++++++++++------ .../cu_up/split_connector/f1u_split_connector.h | 13 ++++++++----- .../f1u_split_connector_factory.cpp | 4 +++- 3 files changed, 21 insertions(+), 12 deletions(-) diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp index 6349e2e78e..0751532f37 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp @@ -10,6 +10,7 @@ */ #include "f1u_split_connector.h" +#include "srsran/f1u/cu_up/f1u_session_manager.h" #include "srsran/gtpu/gtpu_tunnel_nru_factory.h" #include "srsran/ran/rb_id.h" @@ -110,12 +111,14 @@ void f1u_split_gateway_cu_bearer::stop() stopped = true; } -f1u_split_connector::f1u_split_connector(gtpu_gateway& udp_gw_, - gtpu_demux& demux_, - dlt_pcap& gtpu_pcap_, - uint16_t peer_port_, - std::string ext_addr_) : +f1u_split_connector::f1u_split_connector(f1u_session_manager& f1u_session_mngr_, + gtpu_gateway& udp_gw_, + gtpu_demux& demux_, + dlt_pcap& gtpu_pcap_, + uint16_t peer_port_, + std::string ext_addr_) : logger_cu(srslog::fetch_basic_logger("CU-F1-U")), + f1u_session_mngr(f1u_session_mngr_), peer_port(peer_port_), ext_addr(std::move(ext_addr_)), udp_gw(udp_gw_), @@ -138,7 +141,8 @@ f1u_split_connector::create_cu_bearer(uint32_t ue_i task_executor& ul_exec) { logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); - auto cu_bearer = std::make_unique( + [[maybe_unused]] auto& ugw_session2 = f1u_session_mngr.get_next_f1u_gateway(); + auto cu_bearer = std::make_unique( ue_index, drb_id, ul_up_tnl_info, rx_notifier, *udp_session, ul_exec, *this); std::unique_lock lock(map_mutex); srsran_assert(cu_map.find(ul_up_tnl_info) == cu_map.end(), diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.h b/lib/f1u/cu_up/split_connector/f1u_split_connector.h index 4854095fe3..8dd199f0e2 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.h +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.h @@ -13,6 +13,7 @@ #include "srsran/f1u/cu_up/f1u_bearer_logger.h" #include "srsran/f1u/cu_up/f1u_gateway.h" +#include "srsran/f1u/cu_up/f1u_session_manager.h" #include "srsran/gtpu/gtpu_config.h" #include "srsran/gtpu/gtpu_demux.h" #include "srsran/gtpu/gtpu_gateway.h" @@ -103,11 +104,12 @@ class f1u_split_gateway_cu_bearer final : public f1u_cu_up_gateway_bearer class f1u_split_connector final : public f1u_cu_up_udp_gateway { public: - f1u_split_connector(gtpu_gateway& udp_gw_, - gtpu_demux& demux_, - dlt_pcap& gtpu_pcap_, - uint16_t peer_port_ = GTPU_PORT, - std::string ext_addr_ = "auto"); + f1u_split_connector(f1u_session_manager& f1u_session_mngr, + gtpu_gateway& udp_gw_, + gtpu_demux& demux_, + dlt_pcap& gtpu_pcap_, + uint16_t peer_port_ = GTPU_PORT, + std::string ext_addr_ = "auto"); ~f1u_split_connector() override; f1u_cu_up_gateway* get_f1u_cu_up_gateway() { return this; } @@ -134,6 +136,7 @@ class f1u_split_connector final : public f1u_cu_up_udp_gateway std::unordered_map cu_map; std::mutex map_mutex; // shared mutex for access to cu_map + f1u_session_manager& f1u_session_mngr; uint16_t peer_port; std::string ext_addr; gtpu_gateway& udp_gw; diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp index 151fbc52b4..62f4ea016a 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp @@ -17,5 +17,7 @@ using namespace srs_cu_up; std::unique_ptr srsran::srs_cu_up::create_split_f1u_gw(f1u_cu_up_split_gateway_creation_msg msg) { - return std::make_unique(msg.udp_gw, msg.demux, msg.gtpu_pcap, msg.peer_port, msg.f1u_ext_addr); + f1u_session_manager* f1u_session_mngr = nullptr; // TODO fix this and pass in the f1u_cu_up_split_gateway_creation_msg + return std::make_unique( + *f1u_session_mngr, msg.udp_gw, msg.demux, msg.gtpu_pcap, msg.peer_port, msg.f1u_ext_addr); } From 5079188bb01cf296acb5e82faef4ba10ab055fec Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 13 Dec 2024 14:21:47 +0000 Subject: [PATCH 010/107] cu,f1u: moving f1u session manager inside the cu split connector --- apps/cu/cu.cpp | 35 ++++++++++--------- .../srsran/f1u/cu_up/f1u_session_manager.h | 6 ++-- .../f1u/cu_up/f1u_session_manager_factory.h | 2 +- .../f1u_split_connector_factory.h | 10 +++--- lib/f1u/cu_up/f1u_session_manager_factory.cpp | 2 +- lib/f1u/cu_up/f1u_session_manager_impl.cpp | 4 +-- lib/f1u/cu_up/f1u_session_manager_impl.h | 8 ++--- .../split_connector/f1u_split_connector.cpp | 27 +++++++------- .../split_connector/f1u_split_connector.h | 21 ++++++----- .../f1u_split_connector_factory.cpp | 4 +-- tests/unittests/cu_up/cu_up_test_helpers.h | 4 +-- .../cu_up/ngu_session_manager_test.h | 2 +- .../common/f1u_cu_split_connector_test.cpp | 4 ++- .../cu_up/f1u_cu_up_session_manager_test.cpp | 27 +++++++------- .../cu_up/f1u_cu_up_session_manager_test.h | 12 +++---- 15 files changed, 83 insertions(+), 85 deletions(-) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 5254d52de3..3d1a98d79f 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -279,25 +279,26 @@ int main(int argc, char** argv) std::unique_ptr cu_f1c_gw = srsran::create_f1c_gateway_server(f1c_server_cfg); // Create F1-U GW. - std::vector> f1u_gws; + // > Create GTP-U Demux. + gtpu_demux_creation_request cu_f1u_gtpu_msg = {}; + cu_f1u_gtpu_msg.cfg.warn_on_drop = true; + cu_f1u_gtpu_msg.gtpu_pcap = cu_up_dlt_pcaps.f1u.get(); + std::unique_ptr cu_f1u_gtpu_demux = create_gtpu_demux(cu_f1u_gtpu_msg); + // > Create UDP gateway(s). + std::vector> cu_f1u_gws; for (const srs_cu::cu_f1u_socket_appconfig& sock_cfg : cu_cfg.f1u_cfg.f1u_socket_cfg) { - gtpu_demux_creation_request cu_f1u_gtpu_msg = {}; - cu_f1u_gtpu_msg.cfg.warn_on_drop = true; - cu_f1u_gtpu_msg.gtpu_pcap = cu_up_dlt_pcaps.f1u.get(); - std::unique_ptr cu_f1u_gtpu_demux = create_gtpu_demux(cu_f1u_gtpu_msg); - udp_network_gateway_config cu_f1u_gw_config = {}; - cu_f1u_gw_config.bind_address = sock_cfg.bind_addr; - cu_f1u_gw_config.bind_port = GTPU_PORT; - cu_f1u_gw_config.reuse_addr = false; - cu_f1u_gw_config.pool_occupancy_threshold = sock_cfg.udp_config.pool_threshold; - cu_f1u_gw_config.rx_max_mmsg = sock_cfg.udp_config.rx_max_msgs; - std::unique_ptr cu_f1u_gw = create_udp_gtpu_gateway( + udp_network_gateway_config cu_f1u_gw_config = {}; + cu_f1u_gw_config.bind_address = sock_cfg.bind_addr; + cu_f1u_gw_config.bind_port = GTPU_PORT; + cu_f1u_gw_config.reuse_addr = false; + cu_f1u_gw_config.pool_occupancy_threshold = sock_cfg.udp_config.pool_threshold; + cu_f1u_gw_config.rx_max_mmsg = sock_cfg.udp_config.rx_max_msgs; + std::unique_ptr cu_f1u_gw = create_udp_gtpu_gateway( cu_f1u_gw_config, *epoll_broker, workers.cu_up_exec_mapper->io_ul_executor(), *workers.non_rt_low_prio_exec); - std::unique_ptr cu_f1u_conn = srs_cu_up::create_split_f1u_gw( - {*cu_f1u_gw, *cu_f1u_gtpu_demux, *cu_up_dlt_pcaps.f1u, GTPU_PORT, sock_cfg.ext_addr}); - f1u_gws.push_back(std::move(cu_f1u_conn)); + cu_f1u_gws.push_back(std::move(cu_f1u_gw)); } - std::unique_ptr f1u_session_mngr = create_f1u_cu_up_session_manager(f1u_gws); + std::unique_ptr cu_f1u_conn = + srs_cu_up::create_split_f1u_gw({cu_f1u_gws, *cu_f1u_gtpu_demux, *cu_up_dlt_pcaps.f1u, GTPU_PORT}); // Create E1AP local connector std::unique_ptr e1_gw = @@ -368,7 +369,7 @@ int main(int argc, char** argv) o_cuup_unit_deps.workers = &workers; o_cuup_unit_deps.cu_up_e2_exec = workers.cu_e2_exec; o_cuup_unit_deps.e1ap_conn_client = e1_gw.get(); - o_cuup_unit_deps.f1u_gateway = f1u_gws[0].get(); + o_cuup_unit_deps.f1u_gateway = cu_f1u_conn.get(); o_cuup_unit_deps.gtpu_pcap = cu_up_dlt_pcaps.n3.get(); o_cuup_unit_deps.timers = cu_timers; o_cuup_unit_deps.io_brk = epoll_broker.get(); diff --git a/include/srsran/f1u/cu_up/f1u_session_manager.h b/include/srsran/f1u/cu_up/f1u_session_manager.h index bf97e535bf..7c40a1a60d 100644 --- a/include/srsran/f1u/cu_up/f1u_session_manager.h +++ b/include/srsran/f1u/cu_up/f1u_session_manager.h @@ -10,15 +10,15 @@ #pragma once -#include "srsran/f1u/cu_up/f1u_gateway.h" +#include "srsran/gtpu/gtpu_gateway.h" namespace srsran::srs_cu_up { class f1u_session_manager { public: - virtual ~f1u_session_manager() = default; - virtual f1u_cu_up_gateway& get_next_f1u_gateway() = 0; + virtual ~f1u_session_manager() = default; + virtual gtpu_tnl_pdu_session& get_next_f1u_gateway() = 0; }; } // namespace srsran::srs_cu_up diff --git a/include/srsran/f1u/cu_up/f1u_session_manager_factory.h b/include/srsran/f1u/cu_up/f1u_session_manager_factory.h index 3f0047078a..c28ac89e2a 100644 --- a/include/srsran/f1u/cu_up/f1u_session_manager_factory.h +++ b/include/srsran/f1u/cu_up/f1u_session_manager_factory.h @@ -17,6 +17,6 @@ namespace srsran::srs_cu_up { /// \brief Creates an F1-U bearer for the CU-UP. std::unique_ptr -create_f1u_cu_up_session_manager(const std::vector>& f1u_gws); +create_f1u_cu_up_session_manager(const std::vector>& f1u_gws); } // namespace srsran::srs_cu_up diff --git a/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h b/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h index eacc62dac8..bb929facd0 100644 --- a/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h +++ b/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h @@ -20,11 +20,11 @@ namespace srsran::srs_cu_up { struct f1u_cu_up_split_gateway_creation_msg { - gtpu_gateway& udp_gw; - gtpu_demux& demux; - dlt_pcap& gtpu_pcap; - uint16_t peer_port; - std::string f1u_ext_addr = "auto"; + const std::vector>& udp_gws; + gtpu_demux& demux; + dlt_pcap& gtpu_pcap; + uint16_t peer_port; + std::string f1u_ext_addr = "auto"; }; std::unique_ptr create_split_f1u_gw(f1u_cu_up_split_gateway_creation_msg msg); diff --git a/lib/f1u/cu_up/f1u_session_manager_factory.cpp b/lib/f1u/cu_up/f1u_session_manager_factory.cpp index 12144ceb0f..23ba886e53 100644 --- a/lib/f1u/cu_up/f1u_session_manager_factory.cpp +++ b/lib/f1u/cu_up/f1u_session_manager_factory.cpp @@ -17,7 +17,7 @@ using namespace srsran; using namespace srs_cu_up; std::unique_ptr -srsran::srs_cu_up::create_f1u_cu_up_session_manager(const std::vector>& f1u_gws) +srsran::srs_cu_up::create_f1u_cu_up_session_manager(const std::vector>& f1u_gws) { return std::make_unique(f1u_gws); } diff --git a/lib/f1u/cu_up/f1u_session_manager_impl.cpp b/lib/f1u/cu_up/f1u_session_manager_impl.cpp index 252da391ab..e3e3b50d3f 100644 --- a/lib/f1u/cu_up/f1u_session_manager_impl.cpp +++ b/lib/f1u/cu_up/f1u_session_manager_impl.cpp @@ -14,13 +14,13 @@ using namespace srsran; using namespace srs_cu_up; -f1u_session_manager_impl::f1u_session_manager_impl(const std::vector>& f1u_gws_) : +f1u_session_manager_impl::f1u_session_manager_impl(const std::vector>& f1u_gws_) : f1u_gws(f1u_gws_) { srsran_assert(not f1u_gws.empty(), "F1-U gateways cannot be empty"); } -f1u_cu_up_gateway& f1u_session_manager_impl::get_next_f1u_gateway() +gtpu_tnl_pdu_session& f1u_session_manager_impl::get_next_f1u_gateway() { uint32_t index = next_gw % f1u_gws.size(); next_gw++; diff --git a/lib/f1u/cu_up/f1u_session_manager_impl.h b/lib/f1u/cu_up/f1u_session_manager_impl.h index 8c82014a16..0be55eeda8 100644 --- a/lib/f1u/cu_up/f1u_session_manager_impl.h +++ b/lib/f1u/cu_up/f1u_session_manager_impl.h @@ -18,13 +18,13 @@ class f1u_session_manager_impl : public f1u_session_manager { public: ~f1u_session_manager_impl() override = default; - explicit f1u_session_manager_impl(const std::vector>& f1u_gws_); + explicit f1u_session_manager_impl(const std::vector>& f1u_gws_); - f1u_cu_up_gateway& get_next_f1u_gateway() override; + gtpu_tnl_pdu_session& get_next_f1u_gateway() override; private: - const std::vector>& f1u_gws; - uint32_t next_gw = 0; + const std::vector>& f1u_gws; + uint32_t next_gw = 0; }; } // namespace srsran::srs_cu_up diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp index 0751532f37..0f65d3966b 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp @@ -111,23 +111,23 @@ void f1u_split_gateway_cu_bearer::stop() stopped = true; } -f1u_split_connector::f1u_split_connector(f1u_session_manager& f1u_session_mngr_, - gtpu_gateway& udp_gw_, - gtpu_demux& demux_, - dlt_pcap& gtpu_pcap_, - uint16_t peer_port_, - std::string ext_addr_) : +f1u_split_connector::f1u_split_connector(const std::vector>& udp_gws, + gtpu_demux& demux_, + dlt_pcap& gtpu_pcap_, + uint16_t peer_port_, + std::string ext_addr_) : logger_cu(srslog::fetch_basic_logger("CU-F1-U")), - f1u_session_mngr(f1u_session_mngr_), peer_port(peer_port_), ext_addr(std::move(ext_addr_)), - udp_gw(udp_gw_), demux(demux_), gtpu_pcap(gtpu_pcap_) { + srsran_assert(udp_gws.empty(), "Cannot create CU F1-U split connector"); gw_data_gtpu_demux_adapter = std::make_unique(); - udp_session = udp_gw.create(*gw_data_gtpu_demux_adapter); - gw_data_gtpu_demux_adapter->connect_gtpu_demux(demux); + for (unsigned i = 0; i < udp_gws.size(); ++i) { + udp_sessions[i] = udp_gws[i]->create(*gw_data_gtpu_demux_adapter); + gw_data_gtpu_demux_adapter->connect_gtpu_demux(demux); + } } f1u_split_connector::~f1u_split_connector() = default; @@ -141,9 +141,9 @@ f1u_split_connector::create_cu_bearer(uint32_t ue_i task_executor& ul_exec) { logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); - [[maybe_unused]] auto& ugw_session2 = f1u_session_mngr.get_next_f1u_gateway(); + [[maybe_unused]] auto& ugw_session2 = f1u_session_mngr->get_next_f1u_gateway(); auto cu_bearer = std::make_unique( - ue_index, drb_id, ul_up_tnl_info, rx_notifier, *udp_session, ul_exec, *this); + ue_index, drb_id, ul_up_tnl_info, rx_notifier, *udp_sessions[0], ul_exec, *this); std::unique_lock lock(map_mutex); srsran_assert(cu_map.find(ul_up_tnl_info) == cu_map.end(), "Cannot create CU gateway local bearer with already existing UL GTP Tunnel={}", @@ -232,12 +232,13 @@ void f1u_split_connector::disconnect_cu_bearer(const up_transport_layer_info& ul logger_cu.debug("Removed CU F1-U bearer with UL GTP Tunnel={}.", ul_up_tnl_info); } +// TODO this should get the ue_index and drb id to get the right bind address expected f1u_split_connector::get_cu_bind_address() const { std::string ip_address; if (ext_addr == "auto" || ext_addr == "") { - if (not udp_session->get_bind_address(ip_address)) { + if (not udp_sessions[0]->get_bind_address(ip_address)) { return make_unexpected(default_error_t{}); } } else { diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.h b/lib/f1u/cu_up/split_connector/f1u_split_connector.h index 8dd199f0e2..edfaff675c 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.h +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.h @@ -104,17 +104,17 @@ class f1u_split_gateway_cu_bearer final : public f1u_cu_up_gateway_bearer class f1u_split_connector final : public f1u_cu_up_udp_gateway { public: - f1u_split_connector(f1u_session_manager& f1u_session_mngr, - gtpu_gateway& udp_gw_, - gtpu_demux& demux_, - dlt_pcap& gtpu_pcap_, - uint16_t peer_port_ = GTPU_PORT, - std::string ext_addr_ = "auto"); + f1u_split_connector(const std::vector>& udp_gws, + gtpu_demux& demux_, + dlt_pcap& gtpu_pcap_, + uint16_t peer_port_ = GTPU_PORT, + std::string ext_addr_ = "auto"); ~f1u_split_connector() override; - f1u_cu_up_gateway* get_f1u_cu_up_gateway() { return this; } + f1u_cu_up_udp_gateway* get_f1u_cu_up_gateway() { return this; } - std::optional get_bind_port() const override { return udp_session->get_bind_port(); } + /// TODO this should get a ue_index and drb id to be able to find the right port/ip + std::optional get_bind_port() const override { return udp_sessions[0]->get_bind_port(); } std::unique_ptr create_cu_bearer(uint32_t ue_index, drb_id_t drb_id, @@ -136,11 +136,10 @@ class f1u_split_connector final : public f1u_cu_up_udp_gateway std::unordered_map cu_map; std::mutex map_mutex; // shared mutex for access to cu_map - f1u_session_manager& f1u_session_mngr; + std::unique_ptr f1u_session_mngr; uint16_t peer_port; std::string ext_addr; - gtpu_gateway& udp_gw; - std::unique_ptr udp_session; + std::vector> udp_sessions; gtpu_demux& demux; std::unique_ptr gw_data_gtpu_demux_adapter; dlt_pcap& gtpu_pcap; diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp index 62f4ea016a..c24c591f1d 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp @@ -17,7 +17,5 @@ using namespace srs_cu_up; std::unique_ptr srsran::srs_cu_up::create_split_f1u_gw(f1u_cu_up_split_gateway_creation_msg msg) { - f1u_session_manager* f1u_session_mngr = nullptr; // TODO fix this and pass in the f1u_cu_up_split_gateway_creation_msg - return std::make_unique( - *f1u_session_mngr, msg.udp_gw, msg.demux, msg.gtpu_pcap, msg.peer_port, msg.f1u_ext_addr); + return std::make_unique(msg.udp_gws, msg.demux, msg.gtpu_pcap, msg.peer_port, msg.f1u_ext_addr); } diff --git a/tests/unittests/cu_up/cu_up_test_helpers.h b/tests/unittests/cu_up/cu_up_test_helpers.h index 6c4dc4c74c..dc6e1bb4c3 100644 --- a/tests/unittests/cu_up/cu_up_test_helpers.h +++ b/tests/unittests/cu_up/cu_up_test_helpers.h @@ -260,7 +260,7 @@ class dummy_f1u_gateway final : public f1u_cu_up_gateway std::list removed_ul_teid_list = {}; }; -class dummy_ngu_gateway final : public gtpu_tnl_pdu_session +class dummy_gtpu_gateway final : public gtpu_tnl_pdu_session { public: void set_bind_address(const std::string& ip_address) { ip_addr = ip_address; } @@ -287,7 +287,7 @@ class dummy_ngu_session_manager final : public srs_cu_up::ngu_session_manager gtpu_tnl_pdu_session& get_next_ngu_gateway() override { return ngu_gw; }; private: - dummy_ngu_gateway ngu_gw; + dummy_gtpu_gateway ngu_gw; }; class dummy_e1ap final : public srs_cu_up::e1ap_control_message_handler diff --git a/tests/unittests/cu_up/ngu_session_manager_test.h b/tests/unittests/cu_up/ngu_session_manager_test.h index 389e355101..977cf5fa3d 100644 --- a/tests/unittests/cu_up/ngu_session_manager_test.h +++ b/tests/unittests/cu_up/ngu_session_manager_test.h @@ -26,7 +26,7 @@ class ngu_session_manager_test : public ::testing::Test unsigned nof_gws = 2; for (unsigned i = 0; i < nof_gws; i++) { - auto ngu_gw = std::make_unique(); + auto ngu_gw = std::make_unique(); std::string addr = fmt::format("127.0.0.{}", 1 + i); ngu_gw->set_bind_address(addr); ngu_gws.push_back(std::move(ngu_gw)); diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp index 6979342c9d..4fb63f8bbf 100644 --- a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -134,8 +134,10 @@ class f1u_cu_split_connector_test : public ::testing::Test nru_gw_config.reuse_addr = true; udp_gw = create_udp_gtpu_gateway(nru_gw_config, *epoll_broker, io_tx_executor, rx_executor); + std::vector> cu_f1u_gws; + cu_f1u_gws.push_back(std::move(udp_gw)); f1u_cu_up_split_gateway_creation_msg cu_create_msg{ - *udp_gw, *demux, dummy_pcap, tester_bind_port.value(), get_external_bind_address()}; + cu_f1u_gws, *demux, dummy_pcap, tester_bind_port.value(), get_external_bind_address()}; cu_gw = create_split_f1u_gw(cu_create_msg); cu_gw_bind_port = cu_gw->get_bind_port(); ASSERT_TRUE(cu_gw_bind_port.has_value()); diff --git a/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.cpp b/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.cpp index 15606918d1..622591f006 100644 --- a/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.cpp +++ b/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.cpp @@ -22,25 +22,22 @@ TEST_F(f1u_session_manager_test, mngr_creation) TEST_F(f1u_session_manager_test, rr_session_selection) { { - const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); - expected ip_addr; - ip_addr = ngu_gw.get_cu_bind_address(); - ASSERT_TRUE(ip_addr.has_value()); - ASSERT_EQ(ip_addr.value(), "127.0.0.1"); + const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); + std::string ip_addr; + ASSERT_TRUE(ngu_gw.get_bind_address(ip_addr)); + ASSERT_EQ(ip_addr, "127.0.0.1"); } { - const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); - expected ip_addr; - ip_addr = ngu_gw.get_cu_bind_address(); - ASSERT_TRUE(ip_addr.has_value()); - ASSERT_EQ(ip_addr.value(), "127.0.0.2"); + const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); + std::string ip_addr; + ASSERT_TRUE(ngu_gw.get_bind_address(ip_addr)); + ASSERT_EQ(ip_addr, "127.0.0.2"); } { - const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); - expected ip_addr; - ip_addr = ngu_gw.get_cu_bind_address(); - ASSERT_TRUE(ip_addr.has_value()); - ASSERT_EQ(ip_addr.value(), "127.0.0.1"); + const auto& ngu_gw = f1u_session_mngr->get_next_f1u_gateway(); + std::string ip_addr; + ASSERT_TRUE(ngu_gw.get_bind_address(ip_addr)); + ASSERT_EQ(ip_addr, "127.0.0.1"); } } diff --git a/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.h b/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.h index 4699bd3a94..beda527e96 100644 --- a/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.h +++ b/tests/unittests/f1u/cu_up/f1u_cu_up_session_manager_test.h @@ -27,9 +27,9 @@ class f1u_session_manager_test : public ::testing::Test unsigned nof_gws = 2; for (unsigned i = 0; i < nof_gws; i++) { - std::unique_ptr f1u_gw = std::make_unique(f1u_bearer); - std::string addr = fmt::format("127.0.0.{}", 1 + i); - f1u_gw->set_cu_bind_address(addr); + auto f1u_gw = std::make_unique(); + std::string addr = fmt::format("127.0.0.{}", 1 + i); + f1u_gw->set_bind_address(addr); f1u_gws.push_back(std::move(f1u_gw)); } @@ -47,9 +47,9 @@ class f1u_session_manager_test : public ::testing::Test } protected: - std::unique_ptr f1u_session_mngr; - std::vector> f1u_gws; - dummy_inner_f1u_bearer f1u_bearer; + std::unique_ptr f1u_session_mngr; + std::vector> f1u_gws; + dummy_inner_f1u_bearer f1u_bearer; }; } // namespace srsran::srs_cu_up From 18e2a4391b109686706a7ce88c38a1194d7b3378 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 13 Dec 2024 15:21:49 +0000 Subject: [PATCH 011/107] cu,f1u: get udp gw from session manager --- .../cu_up/split_connector/f1u_split_connector.cpp | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp index 0f65d3966b..9e2c43407d 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp @@ -11,6 +11,7 @@ #include "f1u_split_connector.h" #include "srsran/f1u/cu_up/f1u_session_manager.h" +#include "srsran/f1u/cu_up/f1u_session_manager_factory.h" #include "srsran/gtpu/gtpu_tunnel_nru_factory.h" #include "srsran/ran/rb_id.h" @@ -122,12 +123,13 @@ f1u_split_connector::f1u_split_connector(const std::vector(); - for (unsigned i = 0; i < udp_gws.size(); ++i) { - udp_sessions[i] = udp_gws[i]->create(*gw_data_gtpu_demux_adapter); + for (const std::unique_ptr& udp_gw : udp_gws) { + udp_sessions.push_back(udp_gw->create(*gw_data_gtpu_demux_adapter)); gw_data_gtpu_demux_adapter->connect_gtpu_demux(demux); } + f1u_session_mngr = create_f1u_cu_up_session_manager(udp_sessions); } f1u_split_connector::~f1u_split_connector() = default; @@ -141,9 +143,9 @@ f1u_split_connector::create_cu_bearer(uint32_t ue_i task_executor& ul_exec) { logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); - [[maybe_unused]] auto& ugw_session2 = f1u_session_mngr->get_next_f1u_gateway(); - auto cu_bearer = std::make_unique( - ue_index, drb_id, ul_up_tnl_info, rx_notifier, *udp_sessions[0], ul_exec, *this); + auto& udp_session = f1u_session_mngr->get_next_f1u_gateway(); + auto cu_bearer = std::make_unique( + ue_index, drb_id, ul_up_tnl_info, rx_notifier, udp_session, ul_exec, *this); std::unique_lock lock(map_mutex); srsran_assert(cu_map.find(ul_up_tnl_info) == cu_map.end(), "Cannot create CU gateway local bearer with already existing UL GTP Tunnel={}", From 24cc0f26d738dbfb35a7836aa1485e7f14227bce Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Fri, 13 Dec 2024 16:59:41 +0000 Subject: [PATCH 012/107] cu,f1u: get bind address from cu_f1u_gw_bearer itself --- include/srsran/f1u/cu_up/f1u_gateway.h | 11 ++--- .../f1u/local_connector/f1u_local_connector.h | 4 +- lib/cu_up/pdu_session_manager_impl.cpp | 14 +++--- .../split_connector/f1u_split_connector.cpp | 44 ++++++++++++++----- .../split_connector/f1u_split_connector.h | 7 ++- .../local_connector/f1u_local_connector.cpp | 3 +- tests/unittests/cu_up/cu_up_test_helpers.h | 7 ++- .../f1u/common/f1u_connector_test.cpp | 12 ++--- .../common/f1u_cu_split_connector_test.cpp | 12 ++--- 9 files changed, 71 insertions(+), 43 deletions(-) diff --git a/include/srsran/f1u/cu_up/f1u_gateway.h b/include/srsran/f1u/cu_up/f1u_gateway.h index f3910c7f88..4d55932dd7 100644 --- a/include/srsran/f1u/cu_up/f1u_gateway.h +++ b/include/srsran/f1u/cu_up/f1u_gateway.h @@ -34,7 +34,8 @@ class f1u_cu_up_gateway_bearer_rx_notifier class f1u_cu_up_gateway_bearer : public srs_cu_up::f1u_tx_pdu_notifier { public: - virtual void stop() = 0; + virtual void stop() = 0; + virtual expected get_bind_address() const = 0; }; /// This class will be used to provide the interfaces to @@ -49,10 +50,10 @@ class f1u_cu_up_gateway : public srs_cu_up::f1u_bearer_disconnector f1u_cu_up_gateway(f1u_cu_up_gateway&&) = default; f1u_cu_up_gateway& operator=(f1u_cu_up_gateway&&) = default; - virtual std::unique_ptr create_cu_bearer(uint32_t ue_index, - drb_id_t drb_id, - const srs_cu_up::f1u_config& config, - const up_transport_layer_info& ul_up_tnl_info, + virtual std::unique_ptr create_cu_bearer(uint32_t ue_index, + drb_id_t drb_id, + const srs_cu_up::f1u_config& config, + const gtpu_teid_t& ul_teid, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, task_executor& ul_exec) = 0; diff --git a/include/srsran/f1u/local_connector/f1u_local_connector.h b/include/srsran/f1u/local_connector/f1u_local_connector.h index 2d49556f30..175439ab43 100644 --- a/include/srsran/f1u/local_connector/f1u_local_connector.h +++ b/include/srsran/f1u/local_connector/f1u_local_connector.h @@ -55,6 +55,8 @@ class f1u_gateway_cu_bearer : public f1u_cu_up_gateway_bearer stopped = true; } + expected get_bind_address() const override { return "127.0.0.2"; } + void attach_du_notifier(srs_du::f1u_du_gateway_bearer_rx_notifier& notifier_, const up_transport_layer_info& dl_tnl_info_) { @@ -179,7 +181,7 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u std::unique_ptr create_cu_bearer(uint32_t ue_index, drb_id_t drb_id, const srs_cu_up::f1u_config& config, - const up_transport_layer_info& ul_up_tnl_info, + const gtpu_teid_t& ul_up_tnl_info, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, task_executor& ul_exec) override; diff --git a/lib/cu_up/pdu_session_manager_impl.cpp b/lib/cu_up/pdu_session_manager_impl.cpp index 79c25774d9..b118249a6a 100644 --- a/lib/cu_up/pdu_session_manager_impl.cpp +++ b/lib/cu_up/pdu_session_manager_impl.cpp @@ -314,8 +314,11 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& } gtpu_teid_t f1u_ul_teid = ret.value(); + new_drb->f1u_gw_bearer = f1u_gw.create_cu_bearer( + ue_index, drb_to_setup.drb_id, new_drb->f1u_cfg, f1u_ul_teid, new_drb->f1u_gateway_rx_to_nru_adapter, ue_ul_exec); + // Create UL UP TNL address. - expected bind_addr = f1u_gw.get_cu_bind_address(); + expected bind_addr = new_drb->f1u_gw_bearer->get_bind_address(); if (not bind_addr.has_value()) { logger.log_error("Could not get bind address for F1-U tunnel"); return drb_result; @@ -323,13 +326,6 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& up_transport_layer_info f1u_ul_tunnel_addr(transport_layer_address::create_from_string(bind_addr.value()), f1u_ul_teid); - new_drb->f1u_gw_bearer = f1u_gw.create_cu_bearer(ue_index, - drb_to_setup.drb_id, - new_drb->f1u_cfg, - f1u_ul_tunnel_addr, - new_drb->f1u_gateway_rx_to_nru_adapter, - ue_ul_exec); - new_drb->f1u = srs_cu_up::create_f1u_bearer(ue_index, new_drb->drb_id, f1u_ul_tunnel_addr, @@ -431,7 +427,7 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif // create new F1-U and connect it. This will automatically disconnect the old F1-U. drb->f1u_gw_bearer = f1u_gw.create_cu_bearer( - ue_index, drb->drb_id, drb->f1u_cfg, f1u_ul_tunnel_addr, drb->f1u_gateway_rx_to_nru_adapter, ue_ul_exec); + ue_index, drb->drb_id, drb->f1u_cfg, drb->f1u_ul_teid, drb->f1u_gateway_rx_to_nru_adapter, ue_ul_exec); drb->f1u = srs_cu_up::create_f1u_bearer(ue_index, drb->drb_id, diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp index 9e2c43407d..037057ee90 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp @@ -83,7 +83,7 @@ f1u_split_gateway_cu_bearer::f1u_split_gateway_cu_bearer(uint32_t drb_id_t drb_id, const up_transport_layer_info& ul_tnl_info_, f1u_cu_up_gateway_bearer_rx_notifier& cu_rx_, - gtpu_tnl_pdu_session& udp_session, + gtpu_tnl_pdu_session& udp_session_, task_executor& ul_exec_, srs_cu_up::f1u_bearer_disconnector& disconnector_) : ul_exec(ul_exec_), @@ -91,6 +91,7 @@ f1u_split_gateway_cu_bearer::f1u_split_gateway_cu_bearer(uint32_t logger("CU-F1-U", {ue_index_, drb_id, ul_tnl_info_}), disconnector(disconnector_), ul_tnl_info(ul_tnl_info_), + udp_session(udp_session_), cu_rx(cu_rx_) { gtpu_to_network_adapter = std::make_unique(); @@ -112,6 +113,20 @@ void f1u_split_gateway_cu_bearer::stop() stopped = true; } +expected f1u_split_gateway_cu_bearer::get_bind_address() const +{ + std::string ip_address; + + // if (f1u_ext_addr == "auto" || f1u_ext_addr == "") { + if (not udp_session.get_bind_address(ip_address)) { + return make_unexpected(default_error_t{}); + } + // } else { + // ip_address = f1u_ext_addr; + // } + return ip_address; +} + f1u_split_connector::f1u_split_connector(const std::vector>& udp_gws, gtpu_demux& demux_, dlt_pcap& gtpu_pcap_, @@ -138,19 +153,26 @@ std::unique_ptr f1u_split_connector::create_cu_bearer(uint32_t ue_index, drb_id_t drb_id, const srs_cu_up::f1u_config& config, - const up_transport_layer_info& ul_up_tnl_info, + const gtpu_teid_t& ul_teid, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, task_executor& ul_exec) { - logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); + logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_teid); auto& udp_session = f1u_session_mngr->get_next_f1u_gateway(); - auto cu_bearer = std::make_unique( + // Create UL UP TNL address. + std::string bind_addr; + if (not udp_session.get_bind_address(bind_addr)) { + logger_cu.error("Could not get bind address for F1-U tunnel. ue={} drb={}", ue_index, drb_id); + return nullptr; + } + up_transport_layer_info ul_up_tnl_info{transport_layer_address::create_from_string(bind_addr), ul_teid}; + auto cu_bearer = std::make_unique( ue_index, drb_id, ul_up_tnl_info, rx_notifier, udp_session, ul_exec, *this); std::unique_lock lock(map_mutex); - srsran_assert(cu_map.find(ul_up_tnl_info) == cu_map.end(), + srsran_assert(cu_map.find(ul_up_tnl_info.gtp_teid) == cu_map.end(), "Cannot create CU gateway local bearer with already existing UL GTP Tunnel={}", ul_up_tnl_info); - cu_map.insert({ul_up_tnl_info, cu_bearer.get()}); + cu_map.insert({ul_up_tnl_info.gtp_teid, cu_bearer.get()}); // create GTP-U tunnel rx gtpu_tunnel_nru_rx_creation_message msg{}; @@ -180,13 +202,13 @@ void f1u_split_connector::attach_dl_teid(const up_transport_layer_info& ul_up_tn f1u_split_gateway_cu_bearer* cu_bearer = nullptr; { std::unique_lock lock(map_mutex); - if (cu_map.find(ul_up_tnl_info) == cu_map.end()) { + if (cu_map.find(ul_up_tnl_info.gtp_teid) == cu_map.end()) { logger_cu.warning("Could not find UL GTP Tunnel at CU-CP to connect. UL GTP Tunnel={}, DL GTP Tunnel={}", ul_up_tnl_info, dl_up_tnl_info); return; } - cu_bearer = cu_map.at(ul_up_tnl_info); + cu_bearer = cu_map.at(ul_up_tnl_info.gtp_teid); } // create GTP-U tunnel tx @@ -212,11 +234,11 @@ void f1u_split_connector::disconnect_cu_bearer(const up_transport_layer_info& ul f1u_split_gateway_cu_bearer* cu_bearer = nullptr; { std::unique_lock lock(map_mutex); - if (cu_map.find(ul_up_tnl_info) == cu_map.end()) { + if (cu_map.find(ul_up_tnl_info.gtp_teid) == cu_map.end()) { logger_cu.warning("Could not disconnect CU F1-U bearer with unknown UL GTP Tunnel={}", ul_up_tnl_info); return; } - cu_bearer = cu_map.at(ul_up_tnl_info); + cu_bearer = cu_map.at(ul_up_tnl_info.gtp_teid); } // disconnect adapters @@ -229,7 +251,7 @@ void f1u_split_connector::disconnect_cu_bearer(const up_transport_layer_info& ul // Remove DL path { std::unique_lock lock(map_mutex); - cu_map.erase(ul_up_tnl_info); + cu_map.erase(ul_up_tnl_info.gtp_teid); } logger_cu.debug("Removed CU F1-U bearer with UL GTP Tunnel={}.", ul_up_tnl_info); } diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.h b/lib/f1u/cu_up/split_connector/f1u_split_connector.h index edfaff675c..7964813e69 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.h +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.h @@ -52,6 +52,8 @@ class f1u_split_gateway_cu_bearer final : public f1u_cu_up_gateway_bearer void stop() override; + expected get_bind_address() const override; + void on_new_pdu(nru_dl_message msg) override { if (tunnel_tx == nullptr) { @@ -85,6 +87,7 @@ class f1u_split_gateway_cu_bearer final : public f1u_cu_up_gateway_bearer srs_cu_up::f1u_bearer_logger logger; srs_cu_up::f1u_bearer_disconnector& disconnector; up_transport_layer_info ul_tnl_info; + gtpu_tnl_pdu_session& udp_session; std::unique_ptr tunnel_rx; std::unique_ptr tunnel_tx; @@ -119,7 +122,7 @@ class f1u_split_connector final : public f1u_cu_up_udp_gateway std::unique_ptr create_cu_bearer(uint32_t ue_index, drb_id_t drb_id, const srs_cu_up::f1u_config& config, - const up_transport_layer_info& ul_up_tnl_info, + const gtpu_teid_t& ul_teid, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, task_executor& ul_exec) override; @@ -133,7 +136,7 @@ class f1u_split_connector final : public f1u_cu_up_udp_gateway private: srslog::basic_logger& logger_cu; // Key is the UL UP TNL Info (CU-CP address and UL TEID reserved by CU-CP) - std::unordered_map cu_map; + std::unordered_map cu_map; std::mutex map_mutex; // shared mutex for access to cu_map std::unique_ptr f1u_session_mngr; diff --git a/lib/f1u/local_connector/f1u_local_connector.cpp b/lib/f1u/local_connector/f1u_local_connector.cpp index 84f4992839..cbe69f48cb 100644 --- a/lib/f1u/local_connector/f1u_local_connector.cpp +++ b/lib/f1u/local_connector/f1u_local_connector.cpp @@ -18,10 +18,11 @@ std::unique_ptr f1u_local_connector::create_cu_bearer(uint32_t ue_index, drb_id_t drb_id, const srs_cu_up::f1u_config& config, - const up_transport_layer_info& ul_up_tnl_info, + const gtpu_teid_t& ul_teid, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, task_executor& ul_exec) { + up_transport_layer_info ul_up_tnl_info{transport_layer_address::create_from_string("127.0.0.2"), ul_teid}; logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); std::unique_lock lock(map_mutex); srsran_assert(cu_map.find(ul_up_tnl_info) == cu_map.end(), diff --git a/tests/unittests/cu_up/cu_up_test_helpers.h b/tests/unittests/cu_up/cu_up_test_helpers.h index dc6e1bb4c3..38c8a5f15a 100644 --- a/tests/unittests/cu_up/cu_up_test_helpers.h +++ b/tests/unittests/cu_up/cu_up_test_helpers.h @@ -211,6 +211,8 @@ class dummy_f1u_gateway_bearer final : public f1u_cu_up_gateway_bearer void on_new_pdu(nru_dl_message sdu) final { inner.on_new_pdu(std::move(sdu)); } + expected get_bind_address() const override { return {}; } + private: bool stopped = false; dummy_inner_f1u_bearer& inner; @@ -231,12 +233,13 @@ class dummy_f1u_gateway final : public f1u_cu_up_gateway std::unique_ptr create_cu_bearer(uint32_t ue_index, drb_id_t drb_id, const srs_cu_up::f1u_config& config, - const up_transport_layer_info& ul_up_tnl_info, + const gtpu_teid_t& ul_teid, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, task_executor& ul_exec) override { - created_ul_teid_list.push_back(ul_up_tnl_info.gtp_teid); + created_ul_teid_list.push_back(ul_teid); bearer.connect_f1u_rx_sdu_notifier(rx_notifier); + up_transport_layer_info ul_up_tnl_info{transport_layer_address::create_from_string("127.0.0.2"), ul_teid}; return std::make_unique(bearer, *this, ul_up_tnl_info); } diff --git a/tests/unittests/f1u/common/f1u_connector_test.cpp b/tests/unittests/f1u/common/f1u_connector_test.cpp index 4ba7c694a6..c468c1646f 100644 --- a/tests/unittests/f1u/common/f1u_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_connector_test.cpp @@ -223,7 +223,7 @@ TEST_F(f1u_connector_test, ul_dl_flow) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -255,7 +255,7 @@ TEST_F(f1u_connector_test, destroy_bearer_cu_up) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -294,7 +294,7 @@ TEST_F(f1u_connector_test, disconnect_bearer_cu_up) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -336,7 +336,7 @@ TEST_F(f1u_connector_test, destroy_bearer_du) // Create CU TX notifier adapter dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -374,7 +374,7 @@ TEST_F(f1u_connector_test, disconnect_bearer_du) // Create CU TX notifier adapter dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -417,7 +417,7 @@ TEST_F(f1u_connector_test, update_du_f1u) // Create CU TX notifier adapter dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx1; diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp index 4fb63f8bbf..ea8f340082 100644 --- a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -249,7 +249,7 @@ TEST_F(f1u_cu_split_connector_test, send_sdu_with_dl_teid_attached) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); cu_gw->attach_dl_teid(ul_tnl, dl_tnl); ASSERT_NE(udp_tester, nullptr); @@ -282,7 +282,7 @@ TEST_F(f1u_cu_split_connector_test, send_sdu_without_dl_teid_attached) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); // Not attaching DL TEID ASSERT_NE(udp_tester, nullptr); @@ -313,7 +313,7 @@ TEST_F(f1u_cu_split_connector_test, recv_sdu_with_dl_teid_attached) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); cu_gw->attach_dl_teid(ul_tnl, dl_tnl); // Send SDU @@ -343,7 +343,7 @@ TEST_F(f1u_cu_split_connector_test, recv_sdu_without_dl_teid_attached) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); // Not attaching DL TEID // Send SDU @@ -371,7 +371,7 @@ TEST_F(f1u_cu_split_connector_test, disconnect_stops_tx) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); cu_gw->attach_dl_teid(ul_tnl, dl_tnl); ASSERT_NE(udp_tester, nullptr); @@ -429,7 +429,7 @@ TEST_F(f1u_cu_split_connector_test, destroy_bearer_disconnects_and_stops_rx) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl.gtp_teid, cu_rx, ue_worker); cu_gw->attach_dl_teid(ul_tnl, dl_tnl); // Disconnect incorrect tunnel (no effect expected) From 92a692bb04395e18b7b38903d465838d2de31886 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 16 Dec 2024 15:20:36 +0000 Subject: [PATCH 013/107] ngu: removed get_cu_bind_address interface --- apps/units/o_cu_up/o_cu_up_builder.cpp | 3 --- include/srsran/f1u/cu_up/f1u_gateway.h | 2 -- .../f1u/local_connector/f1u_local_connector.h | 2 -- lib/cu_up/pdu_session_manager_impl.cpp | 13 +++++++------ .../cu_up/split_connector/f1u_split_connector.cpp | 15 --------------- .../cu_up/split_connector/f1u_split_connector.h | 2 -- tests/unittests/cu_up/cu_up_test_helpers.h | 4 ---- .../f1u/common/f1u_cu_split_connector_test.cpp | 11 +---------- 8 files changed, 8 insertions(+), 44 deletions(-) diff --git a/apps/units/o_cu_up/o_cu_up_builder.cpp b/apps/units/o_cu_up/o_cu_up_builder.cpp index 6f9be68025..0a1e35da29 100644 --- a/apps/units/o_cu_up/o_cu_up_builder.cpp +++ b/apps/units/o_cu_up/o_cu_up_builder.cpp @@ -59,9 +59,6 @@ o_cu_up_unit srsran::build_o_cu_up(const o_cu_up_unit_config& unit_cfg, const o_ ocu_up_dependencies.cu_dependencies.gtpu_pcap = dependencies.gtpu_pcap; ocu_up_dependencies.cu_dependencies.timers = dependencies.timers; - auto address = dependencies.f1u_gateway->get_cu_bind_address(); - srsran_assert(address.has_value(), "Invalid F1-U bind address"); - // Create NG-U gateway(s). std::vector> ngu_gws; if (not unit_cfg.cu_up_cfg.ngu_cfg.no_core) { diff --git a/include/srsran/f1u/cu_up/f1u_gateway.h b/include/srsran/f1u/cu_up/f1u_gateway.h index 4d55932dd7..3a180ea424 100644 --- a/include/srsran/f1u/cu_up/f1u_gateway.h +++ b/include/srsran/f1u/cu_up/f1u_gateway.h @@ -59,8 +59,6 @@ class f1u_cu_up_gateway : public srs_cu_up::f1u_bearer_disconnector virtual void attach_dl_teid(const up_transport_layer_info& ul_up_tnl_info, const up_transport_layer_info& dl_up_tnl_info) = 0; - - virtual expected get_cu_bind_address() const = 0; }; /// This class will be used to provide the interfaces to diff --git a/include/srsran/f1u/local_connector/f1u_local_connector.h b/include/srsran/f1u/local_connector/f1u_local_connector.h index 175439ab43..0af74be332 100644 --- a/include/srsran/f1u/local_connector/f1u_local_connector.h +++ b/include/srsran/f1u/local_connector/f1u_local_connector.h @@ -206,8 +206,6 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u return fmt::format("127.0.0.{}", 1 + static_cast(gnb_du_id)); } - expected get_cu_bind_address() const override { return {"127.0.2.1"}; } - private: srslog::basic_logger& logger_cu; srslog::basic_logger& logger_du; diff --git a/lib/cu_up/pdu_session_manager_impl.cpp b/lib/cu_up/pdu_session_manager_impl.cpp index b118249a6a..3f67e43ee5 100644 --- a/lib/cu_up/pdu_session_manager_impl.cpp +++ b/lib/cu_up/pdu_session_manager_impl.cpp @@ -416,8 +416,12 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif drb->f1u_ul_teid = ret.value(); logger.log_info("Replacing F1-U tunnel. old_ul_teid={} new_ul_teid={}", old_f1u_ul_teid, drb->f1u_ul_teid); + // create new F1-U and connect it. This will automatically disconnect the old F1-U. + drb->f1u_gw_bearer = f1u_gw.create_cu_bearer( + ue_index, drb->drb_id, drb->f1u_cfg, drb->f1u_ul_teid, drb->f1u_gateway_rx_to_nru_adapter, ue_ul_exec); + // Create UL UP TNL address. - expected bind_addr = f1u_gw.get_cu_bind_address(); + expected bind_addr = drb->f1u_gw_bearer->get_bind_address(); if (not bind_addr.has_value()) { logger.log_error("Could not get bind address for F1-U tunnel"); continue; @@ -425,10 +429,6 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif up_transport_layer_info f1u_ul_tunnel_addr(transport_layer_address::create_from_string(bind_addr.value()), drb->f1u_ul_teid); - // create new F1-U and connect it. This will automatically disconnect the old F1-U. - drb->f1u_gw_bearer = f1u_gw.create_cu_bearer( - ue_index, drb->drb_id, drb->f1u_cfg, drb->f1u_ul_teid, drb->f1u_gateway_rx_to_nru_adapter, ue_ul_exec); - drb->f1u = srs_cu_up::create_f1u_bearer(ue_index, drb->drb_id, f1u_ul_tunnel_addr, @@ -463,11 +463,12 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif drb_to_mod.dl_up_params[0].up_tnl_info, drb_iter->second->f1u_ul_teid); - expected bind_addr = f1u_gw.get_cu_bind_address(); + expected bind_addr = drb_iter->second->f1u_gw_bearer->get_bind_address(); if (not bind_addr.has_value()) { logger.log_error("Could not get bind address for F1-U tunnel"); continue; } + f1u_gw.attach_dl_teid(up_transport_layer_info(transport_layer_address::create_from_string(bind_addr.value()), drb_iter->second->f1u_ul_teid), drb_to_mod.dl_up_params[0].up_tnl_info); diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp index 037057ee90..fa0c4386df 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp @@ -255,18 +255,3 @@ void f1u_split_connector::disconnect_cu_bearer(const up_transport_layer_info& ul } logger_cu.debug("Removed CU F1-U bearer with UL GTP Tunnel={}.", ul_up_tnl_info); } - -// TODO this should get the ue_index and drb id to get the right bind address -expected f1u_split_connector::get_cu_bind_address() const -{ - std::string ip_address; - - if (ext_addr == "auto" || ext_addr == "") { - if (not udp_sessions[0]->get_bind_address(ip_address)) { - return make_unexpected(default_error_t{}); - } - } else { - ip_address = ext_addr; - } - return ip_address; -} diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.h b/lib/f1u/cu_up/split_connector/f1u_split_connector.h index 7964813e69..12f410cc79 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.h +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.h @@ -131,8 +131,6 @@ class f1u_split_connector final : public f1u_cu_up_udp_gateway void disconnect_cu_bearer(const up_transport_layer_info& ul_up_tnl_info) override; - expected get_cu_bind_address() const override; - private: srslog::basic_logger& logger_cu; // Key is the UL UP TNL Info (CU-CP address and UL TEID reserved by CU-CP) diff --git a/tests/unittests/cu_up/cu_up_test_helpers.h b/tests/unittests/cu_up/cu_up_test_helpers.h index 38c8a5f15a..47e78975c3 100644 --- a/tests/unittests/cu_up/cu_up_test_helpers.h +++ b/tests/unittests/cu_up/cu_up_test_helpers.h @@ -254,10 +254,6 @@ class dummy_f1u_gateway final : public f1u_cu_up_gateway removed_ul_teid_list.push_back(ul_up_tnl_info.gtp_teid); } - expected get_cu_bind_address() const override { return bind_ip_addr; } - - void set_cu_bind_address(std::string ip_addr) { bind_ip_addr = std::move(ip_addr); } - std::list created_ul_teid_list = {}; std::list attached_ul_teid_list = {}; std::list removed_ul_teid_list = {}; diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp index ea8f340082..436915b221 100644 --- a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -464,16 +464,7 @@ TEST_F(f1u_cu_split_connector_test, destroy_bearer_disconnects_and_stops_rx) ASSERT_FALSE(rx_sdu2.has_value()); } -TEST_P(f1u_cu_split_connector_external_address_test, external_address) -{ - expected addr = cu_gw->get_cu_bind_address(); - ASSERT_TRUE(addr.has_value()); - if (external_address == "" || external_address == "auto") { - ASSERT_EQ(addr.value(), cu_gw_bind_address); - } else { - ASSERT_EQ(addr.value(), external_address); - } -} +// TODO external address should be checked on F1-U bearer itself. INSTANTIATE_TEST_SUITE_P(f1u_cu_split_connector_test_external_address, f1u_cu_split_connector_external_address_test, From 71a312ab03d3e5850734088b6aaafe374a53165c Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 16 Dec 2024 16:24:37 +0000 Subject: [PATCH 014/107] cu,f1u: fix local connector --- .../f1u/local_connector/f1u_local_connector.h | 2 +- lib/f1u/local_connector/f1u_local_connector.cpp | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/include/srsran/f1u/local_connector/f1u_local_connector.h b/include/srsran/f1u/local_connector/f1u_local_connector.h index 0af74be332..3e33ca3d1f 100644 --- a/include/srsran/f1u/local_connector/f1u_local_connector.h +++ b/include/srsran/f1u/local_connector/f1u_local_connector.h @@ -210,7 +210,7 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u srslog::basic_logger& logger_cu; srslog::basic_logger& logger_du; // Key is the UL UP TNL Info (CU-CP address and UL TEID reserved by CU-CP) - std::unordered_map cu_map; + std::unordered_map cu_map; // Key is the DL UP TNL Info (DU address and DL TEID reserved by DU) std::unordered_map du_map; diff --git a/lib/f1u/local_connector/f1u_local_connector.cpp b/lib/f1u/local_connector/f1u_local_connector.cpp index cbe69f48cb..698c05cf07 100644 --- a/lib/f1u/local_connector/f1u_local_connector.cpp +++ b/lib/f1u/local_connector/f1u_local_connector.cpp @@ -25,12 +25,12 @@ f1u_local_connector::create_cu_bearer(uint32_t ue_i up_transport_layer_info ul_up_tnl_info{transport_layer_address::create_from_string("127.0.0.2"), ul_teid}; logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); std::unique_lock lock(map_mutex); - srsran_assert(cu_map.find(ul_up_tnl_info) == cu_map.end(), + srsran_assert(cu_map.find(ul_teid) == cu_map.end(), "Cannot create CU gateway local bearer with already existing UL GTP Tunnel={}", ul_up_tnl_info); std::unique_ptr cu_bearer = std::make_unique(ue_index, drb_id, ul_up_tnl_info, rx_notifier, ul_exec, *this); - cu_map.insert({ul_up_tnl_info, cu_bearer.get()}); + cu_map.insert({ul_teid, cu_bearer.get()}); return cu_bearer; } @@ -38,7 +38,7 @@ void f1u_local_connector::attach_dl_teid(const up_transport_layer_info& ul_up_tn const up_transport_layer_info& dl_up_tnl_info) { std::unique_lock lock(map_mutex); - if (cu_map.find(ul_up_tnl_info) == cu_map.end()) { + if (cu_map.find(ul_up_tnl_info.gtp_teid) == cu_map.end()) { logger_cu.warning("Could not find UL GTP Tunnel at CU-CP to connect. UL GTP Tunnel={}, DL GTP Tunnel={}", ul_up_tnl_info, dl_up_tnl_info); @@ -54,7 +54,7 @@ void f1u_local_connector::attach_dl_teid(const up_transport_layer_info& ul_up_tn } logger_cu.debug("Connecting DU F1-U bearer. UL GTP Tunnel={}, DL GTP Tunnel={}", ul_up_tnl_info, dl_up_tnl_info); f1u_gateway_du_bearer* du_tun = du_map.at(dl_up_tnl_info); - f1u_gateway_cu_bearer* cu_tun = cu_map.at(ul_up_tnl_info); + f1u_gateway_cu_bearer* cu_tun = cu_map.at(ul_up_tnl_info.gtp_teid); cu_tun->dl_tnl_info = dl_up_tnl_info; cu_tun->attach_du_notifier(*du_tun->f1u_rx, dl_up_tnl_info); } @@ -64,7 +64,7 @@ void f1u_local_connector::disconnect_cu_bearer(const up_transport_layer_info& ul std::unique_lock lock(map_mutex); // Find bearer from ul_teid - auto bearer_it = cu_map.find(ul_up_tnl_info); + auto bearer_it = cu_map.find(ul_up_tnl_info.gtp_teid); if (bearer_it == cu_map.end()) { logger_cu.warning("Could not find UL GTP Tunnel={} at CU to remove.", ul_up_tnl_info); return; @@ -107,13 +107,13 @@ f1u_local_connector::create_du_bearer(uint32_t task_executor& ue_executor) { std::unique_lock lock(map_mutex); - if (cu_map.find(ul_up_tnl_info) == cu_map.end()) { + if (cu_map.find(ul_up_tnl_info.gtp_teid) == cu_map.end()) { logger_du.warning("Could not find CU F1-U bearer, when creating DU F1-U bearer. DL GTP Tunnel={}, UL GTP Tunnel={}", dl_up_tnl_info, ul_up_tnl_info); return nullptr; } - srsran::f1u_gateway_cu_bearer* cu_tun = cu_map.at(ul_up_tnl_info); + srsran::f1u_gateway_cu_bearer* cu_tun = cu_map.at(ul_up_tnl_info.gtp_teid); logger_du.debug("Creating DU F1-U bearer. DL GTP Tunnel={}, UL GTP Tunnel={}", dl_up_tnl_info, ul_up_tnl_info); std::unique_ptr du_bearer = @@ -136,7 +136,7 @@ void f1u_local_connector::remove_du_bearer(const up_transport_layer_info& dl_up_ } logger_du.debug("Removing DU F1-U bearer. DL GTP Tunnel={}", dl_up_tnl_info); - auto cu_bearer_it = cu_map.find(du_bearer_it->second->ul_up_tnl_info); + auto cu_bearer_it = cu_map.find(du_bearer_it->second->ul_up_tnl_info.gtp_teid); if (cu_bearer_it != cu_map.end()) { logger_du.debug("Detaching DU notifier to CU. UL GTP Tunnel={}", du_bearer_it->second->ul_up_tnl_info); cu_bearer_it->second->detach_du_notifier(dl_up_tnl_info); From 00415601b118c0b9a5028a4ddee11168785d571d Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Mon, 16 Dec 2024 17:45:35 +0000 Subject: [PATCH 015/107] cu,f1u: fix unit tests after refactor --- tests/unittests/cu_up/cu_up_test_helpers.h | 2 +- .../f1u/common/f1u_cu_split_connector_test.cpp | 15 +-------------- 2 files changed, 2 insertions(+), 15 deletions(-) diff --git a/tests/unittests/cu_up/cu_up_test_helpers.h b/tests/unittests/cu_up/cu_up_test_helpers.h index 47e78975c3..32a58dc56d 100644 --- a/tests/unittests/cu_up/cu_up_test_helpers.h +++ b/tests/unittests/cu_up/cu_up_test_helpers.h @@ -211,7 +211,7 @@ class dummy_f1u_gateway_bearer final : public f1u_cu_up_gateway_bearer void on_new_pdu(nru_dl_message sdu) final { inner.on_new_pdu(std::move(sdu)); } - expected get_bind_address() const override { return {}; } + expected get_bind_address() const override { return "127.0.0.2"; } private: bool stopped = false; diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp index 436915b221..7ce4efaca0 100644 --- a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -223,15 +223,6 @@ class f1u_cu_split_connector_test : public ::testing::Test srslog::basic_logger& udp_logger_cu = srslog::fetch_basic_logger("UDP-GW", false); }; -class f1u_cu_split_connector_external_address_test : public f1u_cu_split_connector_test, - public ::testing::WithParamInterface -{ - std::string get_external_bind_address() override { return external_address; } - -protected: - std::string external_address = GetParam(); -}; - } // namespace /// Test the instantiation of a new entity @@ -465,11 +456,7 @@ TEST_F(f1u_cu_split_connector_test, destroy_bearer_disconnects_and_stops_rx) } // TODO external address should be checked on F1-U bearer itself. - -INSTANTIATE_TEST_SUITE_P(f1u_cu_split_connector_test_external_address, - f1u_cu_split_connector_external_address_test, - ::testing::Values("auto", "", "8.8.8.8")); - +// int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); From 0b875a30462469536eb8a91c56ea4e1ff7d17f51 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Tue, 17 Dec 2024 17:13:09 +0000 Subject: [PATCH 016/107] cu,f1u: fix ext_bind_address --- apps/cu/cu.cpp | 1 + apps/cu/cu_appconfig.h | 3 +-- apps/cu/cu_appconfig_cli11_schema.cpp | 4 +--- apps/cu/cu_appconfig_yaml_writer.cpp | 2 +- apps/services/network/udp_cli11_schema.cpp | 2 ++ apps/services/network/udp_cli11_schema.h | 2 ++ configs/cu_up_f1u_multiple_sockets.yml | 12 ++++++++---- .../split_connector/f1u_split_connector_factory.h | 1 - .../srsran/f1u/local_connector/f1u_local_connector.h | 2 +- include/srsran/gateways/udp_network_gateway.h | 1 + .../split_connector/f1u_split_connector_factory.cpp | 2 +- lib/gateways/udp_network_gateway_impl.cpp | 12 ++++++++++-- .../f1u/common/f1u_cu_split_connector_test.cpp | 5 +---- 13 files changed, 30 insertions(+), 19 deletions(-) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 3d1a98d79f..c0881d974f 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -289,6 +289,7 @@ int main(int argc, char** argv) for (const srs_cu::cu_f1u_socket_appconfig& sock_cfg : cu_cfg.f1u_cfg.f1u_socket_cfg) { udp_network_gateway_config cu_f1u_gw_config = {}; cu_f1u_gw_config.bind_address = sock_cfg.bind_addr; + cu_f1u_gw_config.ext_bind_addr = sock_cfg.udp_config.ext_addr; cu_f1u_gw_config.bind_port = GTPU_PORT; cu_f1u_gw_config.reuse_addr = false; cu_f1u_gw_config.pool_occupancy_threshold = sock_cfg.udp_config.pool_threshold; diff --git a/apps/cu/cu_appconfig.h b/apps/cu/cu_appconfig.h index dd53ac1872..d056447c53 100644 --- a/apps/cu/cu_appconfig.h +++ b/apps/cu/cu_appconfig.h @@ -22,7 +22,6 @@ namespace srs_cu { /// F1-U sockets configuration struct cu_f1u_socket_appconfig { std::string bind_addr = "127.0.10.1"; // Bind address used by the F1-U interface - std::string ext_addr = "auto"; // External address advertised by the F1-U interface udp_appconfig udp_config; }; @@ -47,7 +46,7 @@ struct cu_appconfig { logger_appconfig log_cfg; /// Expert configuration. expert_execution_appconfig expert_execution_cfg; - /// NR-U + /// F1-U srs_cu::cu_f1u_appconfig f1u_cfg; /// F1AP srs_cu::cu_f1ap_appconfig f1ap_cfg; diff --git a/apps/cu/cu_appconfig_cli11_schema.cpp b/apps/cu/cu_appconfig_cli11_schema.cpp index 0fd636641f..7105b28a1d 100644 --- a/apps/cu/cu_appconfig_cli11_schema.cpp +++ b/apps/cu/cu_appconfig_cli11_schema.cpp @@ -30,8 +30,6 @@ static void configure_cli11_f1u_socket_args(CLI::App& app, srs_cu::cu_f1u_socket f1u_cfg.bind_addr, "Default local IP address interfaces bind to, unless a specific bind address is specified") ->check(CLI::ValidIPV4); - app.add_option( - "--ext_addr", f1u_cfg.ext_addr, "External IP address that is advertised to receive F1-U packets from the DU"); configure_cli11_with_udp_config_schema(app, f1u_cfg.udp_config); } @@ -53,7 +51,7 @@ static void configure_cli11_f1u_args(CLI::App& app, srs_cu::cu_f1u_appconfig& f1 subapp.parse_from_stream(ss); } }; - add_option_cell(app, "--socket", sock_lambda, "Configures UDP/IP socket parameters of the N3 interface"); + add_option_cell(app, "--socket", sock_lambda, "Configures UDP/IP socket parameters of the F1-U interface"); } void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_cfg) diff --git a/apps/cu/cu_appconfig_yaml_writer.cpp b/apps/cu/cu_appconfig_yaml_writer.cpp index 2107338b63..105e8622d0 100644 --- a/apps/cu/cu_appconfig_yaml_writer.cpp +++ b/apps/cu/cu_appconfig_yaml_writer.cpp @@ -31,7 +31,7 @@ static void fill_cu_appconfig_f1ap_section(YAML::Node node, const srs_cu::cu_f1a static void fill_cu_up_f1u_socket_entry(YAML::Node& node, const srsran::srs_cu::cu_f1u_socket_appconfig& config) { node["bind_addr"] = config.bind_addr; - node["ext_addr"] = config.ext_addr; + node["ext_addr"] = config.udp_config.ext_addr; fill_udp_config_in_yaml_schema(node["udp"], config.udp_config); } diff --git a/apps/services/network/udp_cli11_schema.cpp b/apps/services/network/udp_cli11_schema.cpp index 116e3d2628..cee256b106 100644 --- a/apps/services/network/udp_cli11_schema.cpp +++ b/apps/services/network/udp_cli11_schema.cpp @@ -27,6 +27,8 @@ static void configure_cli11_udp_args(CLI::App& app, udp_appconfig& udp_params) void srsran::configure_cli11_with_udp_config_schema(CLI::App& app, udp_appconfig& config) { + app.add_option("--ext_addr", config.ext_addr, "External IP address that is advertised by the UDP GW"); + CLI::App* udp_subcmd = add_subcommand(app, "udp", "UDP parameters")->configurable(); configure_cli11_udp_args(*udp_subcmd, config); } diff --git a/apps/services/network/udp_cli11_schema.h b/apps/services/network/udp_cli11_schema.h index 1afa8449a5..c1836026de 100644 --- a/apps/services/network/udp_cli11_schema.h +++ b/apps/services/network/udp_cli11_schema.h @@ -23,6 +23,8 @@ struct udp_appconfig { float pool_threshold = 0.9; /// Differentiated Services Code Point value. std::optional dscp; + // External address advertised by the UDP-GW. + std::string ext_addr = "auto"; }; /// \brief Configures the given CLI11 application with the UDP application configuration schema. diff --git a/configs/cu_up_f1u_multiple_sockets.yml b/configs/cu_up_f1u_multiple_sockets.yml index 5f8242f4d6..2ad9381b43 100644 --- a/configs/cu_up_f1u_multiple_sockets.yml +++ b/configs/cu_up_f1u_multiple_sockets.yml @@ -2,13 +2,17 @@ cu_up: f1u: socket: - - bind_addr: 127.0.3.1 # address that the NG-U socket will bind to. + bind_addr: 127.0.3.1 # Address that the F1-U socket will bind to. + #ext_addr: 8.8.8.8 # Address that is reported as the F1-U bind address. + # If empty, the real bind address is used. udp: - max_rx_msgs: 256 # maximum packets read from the socket in a single syscall. + max_rx_msgs: 256 # maximum packets read from the socket in a single syscall. pool_threshold: 0.9 # Pool occupancy threshold, after which packets are dropped. - - bind_addr: 127.0.3.2 + bind_addr: 127.0.3.2 # Address that the F1-U socket will bind to. + #ext_addr: 8.8.8.8 # Address that is reported as the F1-U bind address. + # If empty, the real bind address is used. udp: - max_rx_msgs: 256 # maximum packets read from the socket in a single syscall. + max_rx_msgs: 256 # maximum packets read from the socket in a single syscall. pool_threshold: 0.9 # Pool occupancy threshold, after which packets are dropped. diff --git a/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h b/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h index bb929facd0..64666ea0e0 100644 --- a/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h +++ b/include/srsran/f1u/cu_up/split_connector/f1u_split_connector_factory.h @@ -24,7 +24,6 @@ struct f1u_cu_up_split_gateway_creation_msg { gtpu_demux& demux; dlt_pcap& gtpu_pcap; uint16_t peer_port; - std::string f1u_ext_addr = "auto"; }; std::unique_ptr create_split_f1u_gw(f1u_cu_up_split_gateway_creation_msg msg); diff --git a/include/srsran/f1u/local_connector/f1u_local_connector.h b/include/srsran/f1u/local_connector/f1u_local_connector.h index 3e33ca3d1f..ed7260ed2e 100644 --- a/include/srsran/f1u/local_connector/f1u_local_connector.h +++ b/include/srsran/f1u/local_connector/f1u_local_connector.h @@ -181,7 +181,7 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u std::unique_ptr create_cu_bearer(uint32_t ue_index, drb_id_t drb_id, const srs_cu_up::f1u_config& config, - const gtpu_teid_t& ul_up_tnl_info, + const gtpu_teid_t& ul_teid, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, task_executor& ul_exec) override; diff --git a/include/srsran/gateways/udp_network_gateway.h b/include/srsran/gateways/udp_network_gateway.h index 928c967eda..e37009bec2 100644 --- a/include/srsran/gateways/udp_network_gateway.h +++ b/include/srsran/gateways/udp_network_gateway.h @@ -23,6 +23,7 @@ struct udp_network_gateway_config : common_network_gateway_config { unsigned rx_max_mmsg = 256; float pool_occupancy_threshold = 0.9; std::optional dscp; + std::string ext_bind_addr = "auto"; }; /// Interface to inject PDUs into gateway entity. diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp index c24c591f1d..6d4ffda91e 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector_factory.cpp @@ -17,5 +17,5 @@ using namespace srs_cu_up; std::unique_ptr srsran::srs_cu_up::create_split_f1u_gw(f1u_cu_up_split_gateway_creation_msg msg) { - return std::make_unique(msg.udp_gws, msg.demux, msg.gtpu_pcap, msg.peer_port, msg.f1u_ext_addr); + return std::make_unique(msg.udp_gws, msg.demux, msg.gtpu_pcap, msg.peer_port); } diff --git a/lib/gateways/udp_network_gateway_impl.cpp b/lib/gateways/udp_network_gateway_impl.cpp index d07913f08b..c4d9ea6e2f 100644 --- a/lib/gateways/udp_network_gateway_impl.cpp +++ b/lib/gateways/udp_network_gateway_impl.cpp @@ -32,7 +32,10 @@ udp_network_gateway_impl::udp_network_gateway_impl(udp_network_gateway_config io_tx_executor(io_tx_executor_), io_rx_executor(io_rx_executor_) { - logger.info("UDP GW configured. rx_max_mmsg={} pool_thres={}", config.rx_max_mmsg, config.pool_occupancy_threshold); + logger.info("UDP GW configured. rx_max_mmsg={} pool_thres={} ext_bind_addr={}", + config.rx_max_mmsg, + config.pool_occupancy_threshold, + config.ext_bind_addr); } bool udp_network_gateway_impl::subscribe_to(io_broker& broker) @@ -329,6 +332,11 @@ bool udp_network_gateway_impl::get_bind_address(std::string& ip_address) const return false; } + if (config.ext_bind_addr != "" and config.ext_bind_addr != "auto") { + ip_address = config.ext_bind_addr; + return true; + } + sockaddr_storage gw_addr_storage = {}; sockaddr* gw_addr = (sockaddr*)&gw_addr_storage; socklen_t gw_addr_len = sizeof(gw_addr_storage); @@ -354,8 +362,8 @@ bool udp_network_gateway_impl::get_bind_address(std::string& ip_address) const logger.error("Unhandled address family in UDP network gateway with sock_fd={}", sock_fd.value()); return false; } - ip_address = addr_str; + logger.debug("Read bind address of UDP network gateway: {}", ip_address); return true; } diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp index 7ce4efaca0..3f4e355c2d 100644 --- a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -136,8 +136,7 @@ class f1u_cu_split_connector_test : public ::testing::Test std::vector> cu_f1u_gws; cu_f1u_gws.push_back(std::move(udp_gw)); - f1u_cu_up_split_gateway_creation_msg cu_create_msg{ - cu_f1u_gws, *demux, dummy_pcap, tester_bind_port.value(), get_external_bind_address()}; + f1u_cu_up_split_gateway_creation_msg cu_create_msg{cu_f1u_gws, *demux, dummy_pcap, tester_bind_port.value()}; cu_gw = create_split_f1u_gw(cu_create_msg); cu_gw_bind_port = cu_gw->get_bind_port(); ASSERT_TRUE(cu_gw_bind_port.has_value()); @@ -196,8 +195,6 @@ class f1u_cu_split_connector_test : public ::testing::Test udp_tester->handle_pdu(std::move(pdu), addr_storage); } - virtual std::string get_external_bind_address() { return "auto"; } - inline_task_executor rx_executor; manual_task_worker ue_worker{128}; std::unique_ptr epoll_broker; From 2a5f330dc1d9e5a846d7e05044f0c5b9a64bb239 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Wed, 18 Dec 2024 10:50:51 +0000 Subject: [PATCH 017/107] cu,f1u: rm ul ip address from create_cu_bearer in local connector --- .../f1u/local_connector/f1u_local_connector.h | 17 +++++++++++------ .../split_connector/f1u_split_connector.cpp | 5 ----- lib/f1u/local_connector/f1u_local_connector.cpp | 9 ++++----- .../f1u/common/f1u_cu_split_connector_test.cpp | 2 -- 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/include/srsran/f1u/local_connector/f1u_local_connector.h b/include/srsran/f1u/local_connector/f1u_local_connector.h index ed7260ed2e..6a191139c0 100644 --- a/include/srsran/f1u/local_connector/f1u_local_connector.h +++ b/include/srsran/f1u/local_connector/f1u_local_connector.h @@ -33,13 +33,13 @@ class f1u_gateway_cu_bearer : public f1u_cu_up_gateway_bearer public: f1u_gateway_cu_bearer(uint32_t ue_index, drb_id_t drb_id, - const up_transport_layer_info& ul_tnl_info_, + const gtpu_teid_t& ul_teid_, f1u_cu_up_gateway_bearer_rx_notifier& cu_rx_, task_executor& ul_exec_, srs_cu_up::f1u_bearer_disconnector& disconnector_) : - logger("CU-F1-U", {ue_index, drb_id, ul_tnl_info_}), + ul_tnl_info(transport_layer_address::create_from_string(ul_ip_addr), ul_teid_), + logger("CU-F1-U", {ue_index, drb_id, ul_tnl_info}), disconnector(disconnector_), - ul_tnl_info(ul_tnl_info_), cu_rx(cu_rx_), ul_exec(ul_exec_) { @@ -55,7 +55,7 @@ class f1u_gateway_cu_bearer : public f1u_cu_up_gateway_bearer stopped = true; } - expected get_bind_address() const override { return "127.0.0.2"; } + expected get_bind_address() const override { return ul_ip_addr; } void attach_du_notifier(srs_du::f1u_du_gateway_bearer_rx_notifier& notifier_, const up_transport_layer_info& dl_tnl_info_) @@ -85,11 +85,16 @@ class f1u_gateway_cu_bearer : public f1u_cu_up_gateway_bearer } private: - bool stopped = false; + bool stopped = false; + + // On local connector, the local IP address does not exist. + // We can then hard-code the address to any valid IP. + const std::string ul_ip_addr = "127.0.10.1"; + up_transport_layer_info ul_tnl_info; + srs_cu_up::f1u_bearer_logger logger; srs_du::f1u_du_gateway_bearer_rx_notifier* notifier = nullptr; srs_cu_up::f1u_bearer_disconnector& disconnector; - up_transport_layer_info ul_tnl_info; public: /// Holds notifier that will point to NR-U bearer on the UL path diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp index fa0c4386df..4f28564692 100644 --- a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp @@ -116,14 +116,9 @@ void f1u_split_gateway_cu_bearer::stop() expected f1u_split_gateway_cu_bearer::get_bind_address() const { std::string ip_address; - - // if (f1u_ext_addr == "auto" || f1u_ext_addr == "") { if (not udp_session.get_bind_address(ip_address)) { return make_unexpected(default_error_t{}); } - // } else { - // ip_address = f1u_ext_addr; - // } return ip_address; } diff --git a/lib/f1u/local_connector/f1u_local_connector.cpp b/lib/f1u/local_connector/f1u_local_connector.cpp index 698c05cf07..1a5faa338a 100644 --- a/lib/f1u/local_connector/f1u_local_connector.cpp +++ b/lib/f1u/local_connector/f1u_local_connector.cpp @@ -22,14 +22,13 @@ f1u_local_connector::create_cu_bearer(uint32_t ue_i f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, task_executor& ul_exec) { - up_transport_layer_info ul_up_tnl_info{transport_layer_address::create_from_string("127.0.0.2"), ul_teid}; - logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); + logger_cu.info("Created CU gateway local bearer with. ul_teid={}", ul_teid); std::unique_lock lock(map_mutex); srsran_assert(cu_map.find(ul_teid) == cu_map.end(), - "Cannot create CU gateway local bearer with already existing UL GTP Tunnel={}", - ul_up_tnl_info); + "Cannot create CU gateway local bearer, UL TEID already exists. ul_teid={}", + ul_teid); std::unique_ptr cu_bearer = - std::make_unique(ue_index, drb_id, ul_up_tnl_info, rx_notifier, ul_exec, *this); + std::make_unique(ue_index, drb_id, ul_teid, rx_notifier, ul_exec, *this); cu_map.insert({ul_teid, cu_bearer.get()}); return cu_bearer; } diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp index 3f4e355c2d..19d5251821 100644 --- a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -452,8 +452,6 @@ TEST_F(f1u_cu_split_connector_test, destroy_bearer_disconnects_and_stops_rx) ASSERT_FALSE(rx_sdu2.has_value()); } -// TODO external address should be checked on F1-U bearer itself. -// int main(int argc, char** argv) { ::testing::InitGoogleTest(&argc, argv); From ef8156c58634e824b353971c7137a09a5a39a5d2 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 19 Dec 2024 12:46:37 +0000 Subject: [PATCH 018/107] gnb,cu: fix ext address for NG-U --- apps/services/network/udp_cli11_schema.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/services/network/udp_cli11_schema.cpp b/apps/services/network/udp_cli11_schema.cpp index cee256b106..67d7aea7eb 100644 --- a/apps/services/network/udp_cli11_schema.cpp +++ b/apps/services/network/udp_cli11_schema.cpp @@ -27,7 +27,8 @@ static void configure_cli11_udp_args(CLI::App& app, udp_appconfig& udp_params) void srsran::configure_cli11_with_udp_config_schema(CLI::App& app, udp_appconfig& config) { - app.add_option("--ext_addr", config.ext_addr, "External IP address that is advertised by the UDP GW"); + add_option(app, "--ext_addr", config.ext_addr, "External IP address that is advertised for receiving UDP packets.") + ->check(CLI::ValidIPV4 | CLI::IsMember({"auto"})); CLI::App* udp_subcmd = add_subcommand(app, "udp", "UDP parameters")->configurable(); configure_cli11_udp_args(*udp_subcmd, config); From 770933808c5bb720b0d602fec936f9bd474e100f Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 19 Dec 2024 13:45:37 +0000 Subject: [PATCH 019/107] cu: autoderive F1-U configuration none is provided --- apps/cu/cu.cpp | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index c0881d974f..130f5e8f84 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -142,7 +142,8 @@ static void fill_cu_worker_manager_config(worker_manager_config& config, const c config.low_prio_sched_config = unit_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg; } -static void autoderive_cu_up_parameters_after_parsing(o_cu_up_unit_config& o_cu_up_cfg, +static void autoderive_cu_up_parameters_after_parsing(cu_appconfig& cu_config, + o_cu_up_unit_config& o_cu_up_cfg, const cu_cp_unit_config& cu_cp_cfg) { // If no UPF is configured, we set the UPF configuration from the CU-CP AMF configuration. @@ -151,6 +152,11 @@ static void autoderive_cu_up_parameters_after_parsing(o_cu_up_unit_config& o sock_cfg.bind_addr = cu_cp_cfg.amf_config.amf.bind_addr; o_cu_up_cfg.cu_up_cfg.ngu_cfg.ngu_socket_cfg.push_back(sock_cfg); } + // If no F1-U socket configuration is derived, we set a default configuration. + if (cu_config.f1u_cfg.f1u_socket_cfg.empty()) { + srs_cu::cu_f1u_socket_appconfig sock_cfg; + cu_config.f1u_cfg.f1u_socket_cfg.push_back(sock_cfg); + } } int main(int argc, char** argv) @@ -186,12 +192,12 @@ int main(int argc, char** argv) o_cu_up_app_unit->on_parsing_configuration_registration(app); // Set the callback for the app calling all the autoderivation functions. - app.callback([&app, &o_cu_cp_app_unit, &o_cu_up_app_unit]() { + app.callback([&app, &cu_cfg, &o_cu_cp_app_unit, &o_cu_up_app_unit]() { o_cu_cp_app_unit->on_configuration_parameters_autoderivation(app); o_cu_up_app_unit->on_configuration_parameters_autoderivation(app); - autoderive_cu_up_parameters_after_parsing(o_cu_up_app_unit->get_o_cu_up_unit_config(), - o_cu_cp_app_unit->get_o_cu_cp_unit_config().cucp_cfg); + autoderive_cu_up_parameters_after_parsing( + cu_cfg, o_cu_up_app_unit->get_o_cu_up_unit_config(), o_cu_cp_app_unit->get_o_cu_cp_unit_config().cucp_cfg); }); // Parse arguments. From fef24a5173054b2dcb29d8b0c379b2e1163c0824 Mon Sep 17 00:00:00 2001 From: Fabian Eckermann Date: Wed, 18 Dec 2024 15:24:13 +0100 Subject: [PATCH 020/107] ci: add paging zmq test --- .gitlab/ci/e2e/.env | 2 +- tests/e2e/tests/paging.py | 67 +++++++++++++++++++++++++++++++++++ tests/e2e/tests/steps/stub.py | 53 +++++++++++++++++++++++++++ 3 files changed, 121 insertions(+), 1 deletion(-) create mode 100644 tests/e2e/tests/paging.py diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env index bb6906acae..cd72961fa8 100644 --- a/.gitlab/ci/e2e/.env +++ b/.gitlab/ci/e2e/.env @@ -1,6 +1,6 @@ SRSGNB_REGISTRY_URI=registry.gitlab.com/softwareradiosystems/srsgnb RETINA_REGISTRY_PREFIX=registry.gitlab.com/softwareradiosystems/ci/retina -RETINA_VERSION=0.59.2 +RETINA_VERSION=0.59.3 UBUNTU_VERSION=24.04 AMARISOFT_VERSION=2023-09-08 SRSUE_VERSION=23.11 diff --git a/tests/e2e/tests/paging.py b/tests/e2e/tests/paging.py new file mode 100644 index 0000000000..f2b73f6452 --- /dev/null +++ b/tests/e2e/tests/paging.py @@ -0,0 +1,67 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the distribution. +# + +""" +Paging Tests +""" +import logging +from time import sleep + +from pytest import mark +from retina.client.manager import RetinaTestManager +from retina.launcher.artifacts import RetinaTestData +from retina.launcher.utils import param +from retina.protocol.fivegc_pb2_grpc import FiveGCStub +from retina.protocol.gnb_pb2_grpc import GNBStub +from retina.protocol.ue_pb2_grpc import UEStub + +from .steps.configuration import configure_test_parameters, get_minimum_sample_rate_for_bandwidth +from .steps.stub import ping, ping_from_5gc, start_network, ue_await_release, ue_start_and_attach + + +@mark.parametrize( + "band, common_scs, bandwidth", + ( + param(3, 15, 10, id="band:%s-scs:%s-bandwidth:%s"), + param(41, 30, 10, id="band:%s-scs:%s-bandwidth:%s"), + ), +) +@mark.zmq_single_ue +# pylint: disable=too-many-arguments,too-many-positional-arguments +def test_zmq_paging( + retina_manager: RetinaTestManager, + retina_data: RetinaTestData, + ue: UEStub, + fivegc: FiveGCStub, + gnb: GNBStub, + band: int, + common_scs: int, + bandwidth: int, +): + """ + ZMQ Paging test + """ + + configure_test_parameters( + retina_manager=retina_manager, + retina_data=retina_data, + band=band, + common_scs=common_scs, + bandwidth=bandwidth, + sample_rate=None, + global_timing_advance=0, + time_alignment_calibration=0, + cu_cp_inactivity_timer=1, + ) + + logging.info("Paging Test") + start_network([ue], gnb, fivegc) + ue_attach_info_dict = ue_start_and_attach([ue], gnb, fivegc) + ping(ue_attach_info_dict, fivegc, 10) + if ue_await_release(ue): + ping_from_5gc(ue_attach_info_dict, fivegc, 10) diff --git a/tests/e2e/tests/steps/stub.py b/tests/e2e/tests/steps/stub.py index ccb6a19881..c44576d9c3 100644 --- a/tests/e2e/tests/steps/stub.py +++ b/tests/e2e/tests/steps/stub.py @@ -50,6 +50,7 @@ GNB_STARTUP_TIMEOUT: int = 2 # GNB delay (we wait x seconds and check it's still alive). UE later and has a big timeout FIVEGC_STARTUP_TIMEOUT: int = RF_MAX_TIMEOUT ATTACH_TIMEOUT: int = 90 +RELEASE_TIMEOUT: int = 90 INTER_UE_START_PERIOD: int = 0 @@ -221,6 +222,27 @@ def ue_start_and_attach( return ue_attach_info_dict +def ue_await_release( + ue: UEStub, + release_timeout: int = RELEASE_TIMEOUT, +) -> bool: + """ + Wait until an UEs is released from already running gnb and 5gc + """ + + # Await release + ue_release_result: bool = False + with suppress(grpc.RpcError): + ue_release_result = ue.WaitUntilReleased(UInt32Value(value=release_timeout)) == Empty() + + if ue_release_result: + logging.info("UE [%s] released", id(ue)) + else: + pytest.fail("Release timeout reached") + + return ue_release_result + + def start_kpm_mon_xapp(ric: NearRtRicStub, report_service_style: int = 1, metrics: str = "DRB.UEThpDl") -> None: """ Start KPM Monitor xAPP in RIC @@ -359,6 +381,37 @@ def _print_ping_result(msg: str, task: grpc.Future): logging.error(ErrorReportedByAgent(err)) +def ping_from_5gc( + ue_attach_info_dict: Dict[UEStub, UEAttachedInfo], fivegc: FiveGCStub, ping_count, time_step: int = 0 +): + """ + Ping command from a 5GC to a UE + """ + ping_task_array = ping_start_from_5gc(ue_attach_info_dict, fivegc, ping_count, time_step) + ping_wait_until_finish(ping_task_array) + + +def ping_start_from_5gc( + ue_attach_info_dict: Dict[UEStub, UEAttachedInfo], fivegc: FiveGCStub, ping_count, time_step: float = 0 +) -> List[grpc.Future]: + """ + Ping command between a 5GC and an UE + """ + + # Launch ping (5gc -> ue) for each attached ue in parallel + + ping_task_array: List[grpc.Future] = [] + for ue_attached_info in ue_attach_info_dict.values(): + fivegc_to_ue: grpc.Future = fivegc.Ping.future(PingRequest(address=ue_attached_info.ipv4, count=ping_count)) + fivegc_to_ue.add_done_callback( + lambda _task, _msg=f"[{ue_attached_info.ipv4}] 5GC -> UE": _print_ping_result(_msg, _task) + ) + ping_task_array.append(fivegc_to_ue) + sleep(time_step) + + return ping_task_array + + def iperf_parallel( ue_attach_info_dict: Dict[UEStub, UEAttachedInfo], fivegc: FiveGCStub, From 247d8599f2d2057ca1a647a9c77fb306839efe08 Mon Sep 17 00:00:00 2001 From: Fabian Eckermann Date: Thu, 19 Dec 2024 17:20:44 +0100 Subject: [PATCH 021/107] ci: add paging cots test --- tests/e2e/tests/paging.py | 47 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/tests/e2e/tests/paging.py b/tests/e2e/tests/paging.py index f2b73f6452..3a19bfdbea 100644 --- a/tests/e2e/tests/paging.py +++ b/tests/e2e/tests/paging.py @@ -65,3 +65,50 @@ def test_zmq_paging( ping(ue_attach_info_dict, fivegc, 10) if ue_await_release(ue): ping_from_5gc(ue_attach_info_dict, fivegc, 10) + + +@mark.parametrize( + "band, common_scs, bandwidth", + ( + param(3, 15, 10, id="band:%s-scs:%s-bandwidth:%s"), + param(78, 30, 20, id="band:%s-scs:%s-bandwidth:%s"), + ), +) +@mark.android +@mark.flaky( + reruns=2, + only_rerun=["failed to start", "Exception calling application", "Attach timeout reached", "Some packages got lost"], +) +# pylint: disable=too-many-arguments,too-many-positional-arguments +def test_cots_paging( + retina_manager: RetinaTestManager, + retina_data: RetinaTestData, + ue: UEStub, + fivegc: FiveGCStub, + gnb: GNBStub, + band: int, + common_scs: int, + bandwidth: int, +): + """ + COTS Paging test + """ + + configure_test_parameters( + retina_manager=retina_manager, + retina_data=retina_data, + band=band, + common_scs=common_scs, + bandwidth=bandwidth, + sample_rate=get_minimum_sample_rate_for_bandwidth(bandwidth), + global_timing_advance=-1, + time_alignment_calibration="auto", + cu_cp_inactivity_timer=1, + ) + + logging.info("Paging Test") + start_network([ue], gnb, fivegc) + ue_attach_info_dict = ue_start_and_attach([ue], gnb, fivegc) + ping(ue_attach_info_dict, fivegc, 10) + sleep(5) + ping_from_5gc(ue_attach_info_dict, fivegc, 10) From 38ddc9df4c639b62b9b4747b22fd32b474114709 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20S=C3=A1ez?= Date: Thu, 19 Dec 2024 20:54:10 +0100 Subject: [PATCH 022/107] ci: add stop to paging tests --- tests/e2e/tests/paging.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/tests/e2e/tests/paging.py b/tests/e2e/tests/paging.py index 3a19bfdbea..7efe78c608 100644 --- a/tests/e2e/tests/paging.py +++ b/tests/e2e/tests/paging.py @@ -21,7 +21,7 @@ from retina.protocol.ue_pb2_grpc import UEStub from .steps.configuration import configure_test_parameters, get_minimum_sample_rate_for_bandwidth -from .steps.stub import ping, ping_from_5gc, start_network, ue_await_release, ue_start_and_attach +from .steps.stub import ping, ping_from_5gc, start_network, stop, ue_await_release, ue_start_and_attach @mark.parametrize( @@ -65,6 +65,13 @@ def test_zmq_paging( ping(ue_attach_info_dict, fivegc, 10) if ue_await_release(ue): ping_from_5gc(ue_attach_info_dict, fivegc, 10) + stop( + [ue], + gnb, + fivegc, + retina_data, + warning_as_errors=True, + ) @mark.parametrize( @@ -112,3 +119,10 @@ def test_cots_paging( ping(ue_attach_info_dict, fivegc, 10) sleep(5) ping_from_5gc(ue_attach_info_dict, fivegc, 10) + stop( + [ue], + gnb, + fivegc, + retina_data, + warning_as_errors=False, + ) From b4212cf69995f62171e034a184055859eac607c5 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Wed, 4 Dec 2024 15:40:46 +0100 Subject: [PATCH 023/107] pcaps: flush PCAPs when cleanup_signal_handler is called --- apps/cu/cu.cpp | 20 +++++---- apps/du/du.cpp | 13 ++++-- apps/examples/ofh/ru_emulator.cpp | 4 +- apps/examples/phy/radio_ssb.cpp | 4 +- apps/examples/radio/radio_util_sample.cpp | 4 +- apps/examples/radio/rx_power_analyzer.cpp | 4 +- apps/gnb/gnb.cpp | 25 ++++++----- .../o_du_high/o_du_high_unit_pcap_factory.h | 33 +++++++++++++- apps/units/o_cu_cp/pcap_factory.h | 29 +++++++++++- apps/units/o_cu_up/pcap_factory.h | 25 ++++++++++- include/srsran/pcap/dlt_pcap.h | 4 +- include/srsran/pcap/mac_pcap.h | 6 ++- include/srsran/pcap/rlc_pcap.h | 4 +- include/srsran/support/signal_handling.h | 2 +- include/srsran/support/signal_observer.h | 42 +++++++++++++++++ include/srsran/support/signal_observer_impl.h | 45 +++++++++++++++++++ lib/pcap/dlt_pcap_impl.h | 2 + lib/pcap/mac_pcap_impl.h | 2 + lib/pcap/rlc_pcap_impl.h | 2 + lib/support/signal_handling.cpp | 4 +- .../e1ap/gateways/e1_gateway_test.cpp | 1 + tests/unittests/e2/common/e2_test_helpers.h | 1 + .../f1ap/gateways/f1c_gateway_test.cpp | 1 + 23 files changed, 235 insertions(+), 42 deletions(-) create mode 100644 include/srsran/support/signal_observer.h create mode 100644 include/srsran/support/signal_observer_impl.h diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 130f5e8f84..19417a47d7 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -43,6 +43,7 @@ #include "srsran/support/io/io_broker_factory.h" #include "srsran/support/io/io_timer_source.h" #include "srsran/support/signal_handling.h" +#include "srsran/support/signal_observer_impl.h" #include "srsran/support/sysinfo.h" #include "srsran/support/timers.h" #include "srsran/support/tracing/event_tracing.h" @@ -78,14 +79,17 @@ static void populate_cli11_generic_args(CLI::App& app) } /// Function to call when the application is interrupted. -static void interrupt_signal_handler() +static void interrupt_signal_handler(int signal) { is_app_running = false; } +static signal_observable_impl cleanup_signal_observable; + /// Function to call when the application is going to be forcefully shutdown. -static void cleanup_signal_handler() +static void cleanup_signal_handler(int signal) { + cleanup_signal_observable.notify_signal(signal); srslog::flush(); } @@ -264,10 +268,10 @@ int main(int argc, char** argv) worker_manager workers{worker_manager_cfg}; // Create layer specific PCAPs. - o_cu_cp_dlt_pcaps cu_cp_dlt_pcaps = - create_o_cu_cp_dlt_pcap(o_cu_cp_app_unit->get_o_cu_cp_unit_config(), *workers.get_executor_getter()); - o_cu_up_dlt_pcaps cu_up_dlt_pcaps = - create_o_cu_up_dlt_pcaps(o_cu_up_app_unit->get_o_cu_up_unit_config(), *workers.get_executor_getter()); + o_cu_cp_dlt_pcaps cu_cp_dlt_pcaps = create_o_cu_cp_dlt_pcap( + o_cu_cp_app_unit->get_o_cu_cp_unit_config(), *workers.get_executor_getter(), cleanup_signal_observable); + o_cu_up_dlt_pcaps cu_up_dlt_pcaps = create_o_cu_up_dlt_pcaps( + o_cu_up_app_unit->get_o_cu_up_unit_config(), *workers.get_executor_getter(), cleanup_signal_observable); // Create IO broker. const auto& low_prio_cpu_mask = cu_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask; @@ -412,8 +416,8 @@ int main(int argc, char** argv) // Close PCAPs cu_logger.info("Closing PCAP files..."); - cu_cp_dlt_pcaps.close(); - cu_up_dlt_pcaps.close(); + cu_cp_dlt_pcaps.reset(); + cu_up_dlt_pcaps.reset(); cu_logger.info("PCAP files successfully closed."); // Stop workers diff --git a/apps/du/du.cpp b/apps/du/du.cpp index 5453c7b68f..d72bc1d8c5 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -37,6 +37,7 @@ #include "srsran/support/cpu_features.h" #include "srsran/support/io/io_broker_factory.h" #include "srsran/support/signal_handling.h" +#include "srsran/support/signal_observer_impl.h" #include "srsran/support/tracing/event_tracing.h" #include "srsran/support/versioning/build_info.h" #include "srsran/support/versioning/version.h" @@ -69,14 +70,17 @@ static void populate_cli11_generic_args(CLI::App& app) } /// Function to call when the application is interrupted. -static void interrupt_signal_handler() +static void interrupt_signal_handler(int signal) { is_app_running = false; } +static signal_observable_impl cleanup_signal_observable; + /// Function to call when the application is going to be forcefully shutdown. -static void cleanup_signal_handler() +static void cleanup_signal_handler(int signal) { + cleanup_signal_observable.notify_signal(signal); srslog::flush(); } @@ -245,7 +249,8 @@ int main(int argc, char** argv) io_broker_config io_broker_cfg(low_prio_cpu_mask); std::unique_ptr epoll_broker = create_io_broker(io_broker_type::epoll, io_broker_cfg); - flexible_o_du_pcaps du_pcaps = create_o_du_pcaps(o_du_app_unit->get_o_du_high_unit_config(), workers); + flexible_o_du_pcaps du_pcaps = + create_o_du_pcaps(o_du_app_unit->get_o_du_high_unit_config(), workers, cleanup_signal_observable); // Instantiate F1-C client gateway. std::unique_ptr f1c_gw = create_f1c_client_gateway(du_cfg.f1ap_cfg.cu_cp_address, @@ -328,7 +333,7 @@ int main(int argc, char** argv) du_inst.get_power_controller().stop(); du_logger.info("Closing PCAP files..."); - du_pcaps.close(); + du_pcaps.reset(); du_logger.info("PCAP files successfully closed."); du_logger.info("Stopping executors..."); diff --git a/apps/examples/ofh/ru_emulator.cpp b/apps/examples/ofh/ru_emulator.cpp index 41dba2fcb5..b4f7ae9f8f 100644 --- a/apps/examples/ofh/ru_emulator.cpp +++ b/apps/examples/ofh/ru_emulator.cpp @@ -777,13 +777,13 @@ static std::atomic is_app_running = {true}; static constexpr unsigned MAX_CONFIG_FILES = 6; /// Function to call when the application is interrupted. -static void interrupt_signal_handler() +static void interrupt_signal_handler(int signal) { is_app_running = false; } /// Function to call when the application is going to be forcefully shutdown. -static void cleanup_signal_handler() +static void cleanup_signal_handler(int signal) { srslog::flush(); } diff --git a/apps/examples/phy/radio_ssb.cpp b/apps/examples/phy/radio_ssb.cpp index 499c93e05f..e67021c464 100644 --- a/apps/examples/phy/radio_ssb.cpp +++ b/apps/examples/phy/radio_ssb.cpp @@ -274,13 +274,13 @@ static void stop_execution() } /// Function to call when the application is interrupted. -static void interrupt_signal_handler() +static void interrupt_signal_handler(int signal) { stop_execution(); } /// Function to call when the application is going to be forcefully shutdown. -static void cleanup_signal_handler() +static void cleanup_signal_handler(int signal) { srslog::flush(); } diff --git a/apps/examples/radio/radio_util_sample.cpp b/apps/examples/radio/radio_util_sample.cpp index f613f9f094..27fcd48178 100644 --- a/apps/examples/radio/radio_util_sample.cpp +++ b/apps/examples/radio/radio_util_sample.cpp @@ -168,7 +168,7 @@ static std::atomic stop = {false}; static std::unique_ptr radio = nullptr; /// Function to call when the application is interrupted. -static void interrupt_signal_handler() +static void interrupt_signal_handler(int signal) { if (radio != nullptr) { radio->stop(); @@ -177,7 +177,7 @@ static void interrupt_signal_handler() } /// Function to call when the application is going to be forcefully shutdown. -static void cleanup_signal_handler() +static void cleanup_signal_handler(int signal) { srslog::flush(); } diff --git a/apps/examples/radio/rx_power_analyzer.cpp b/apps/examples/radio/rx_power_analyzer.cpp index 3b9c359f5f..78c6236f45 100644 --- a/apps/examples/radio/rx_power_analyzer.cpp +++ b/apps/examples/radio/rx_power_analyzer.cpp @@ -68,7 +68,7 @@ static std::atomic stop = {true}; static std::unique_ptr radio = nullptr; /// Function to call when the application is interrupted. -static void interrupt_signal_handler() +static void interrupt_signal_handler(int signal) { if (radio != nullptr) { radio->stop(); @@ -77,7 +77,7 @@ static void interrupt_signal_handler() } /// Function to call when the application is going to be forcefully shutdown. -static void cleanup_signal_handler() +static void cleanup_signal_handler(int signal) { srslog::flush(); } diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index 0ff81cacf0..0d8cb86ecf 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -43,6 +43,7 @@ #include "srsran/support/cpu_features.h" #include "srsran/support/io/io_broker_factory.h" #include "srsran/support/signal_handling.h" +#include "srsran/support/signal_observer_impl.h" #include "srsran/support/tracing/event_tracing.h" #include "srsran/support/versioning/build_info.h" #include "srsran/support/versioning/version.h" @@ -80,14 +81,17 @@ static void populate_cli11_generic_args(CLI::App& app) } /// Function to call when the application is interrupted. -static void interrupt_signal_handler() +static void interrupt_signal_handler(int signal) { is_app_running = false; } +static signal_observable_impl cleanup_signal_observable; + /// Function to call when the application is going to be forcefully shutdown. -static void cleanup_signal_handler() +static void cleanup_signal_handler(int signal) { + cleanup_signal_observable.notify_signal(signal); srslog::flush(); } @@ -325,11 +329,12 @@ int main(int argc, char** argv) // We disable one accordingly. o_cu_up_app_unit->get_o_cu_up_unit_config().cu_up_cfg.pcap_cfg.disable_e1_pcaps(); o_du_app_unit->get_o_du_high_unit_config().du_high_cfg.config.pcaps.disable_f1_pcaps(); - o_cu_cp_dlt_pcaps cu_cp_dlt_pcaps = - create_o_cu_cp_dlt_pcap(o_cu_cp_app_unit->get_o_cu_cp_unit_config(), *workers.get_executor_getter()); - o_cu_up_dlt_pcaps cu_up_dlt_pcaps = - create_o_cu_up_dlt_pcaps(o_cu_up_app_unit->get_o_cu_up_unit_config(), *workers.get_executor_getter()); - flexible_o_du_pcaps du_pcaps = create_o_du_pcaps(o_du_app_unit->get_o_du_high_unit_config(), workers); + o_cu_cp_dlt_pcaps cu_cp_dlt_pcaps = create_o_cu_cp_dlt_pcap( + o_cu_cp_app_unit->get_o_cu_cp_unit_config(), *workers.get_executor_getter(), cleanup_signal_observable); + o_cu_up_dlt_pcaps cu_up_dlt_pcaps = create_o_cu_up_dlt_pcaps( + o_cu_up_app_unit->get_o_cu_up_unit_config(), *workers.get_executor_getter(), cleanup_signal_observable); + flexible_o_du_pcaps du_pcaps = + create_o_du_pcaps(o_du_app_unit->get_o_du_high_unit_config(), workers, cleanup_signal_observable); std::unique_ptr f1c_gw = create_f1c_local_connector(f1c_local_connector_config{*cu_cp_dlt_pcaps.f1ap}); @@ -481,9 +486,9 @@ int main(int argc, char** argv) o_cucp_obj.get_cu_cp().stop(); gnb_logger.info("Closing PCAP files..."); - cu_cp_dlt_pcaps.close(); - cu_up_dlt_pcaps.close(); - du_pcaps.close(); + cu_cp_dlt_pcaps.reset(); + cu_up_dlt_pcaps.reset(); + du_pcaps.reset(); gnb_logger.info("PCAP files successfully closed."); gnb_logger.info("Stopping executors..."); diff --git a/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h b/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h index d9dab50676..763852025b 100644 --- a/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h +++ b/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h @@ -27,7 +27,29 @@ struct flexible_o_du_pcaps { // MAC and RLC PCAPs std::unique_ptr mac; std::unique_ptr rlc; - void close() + + /// \brief Close (and flush) the PCAPs without destroying the objects. + void close() + { + if (f1ap) { + f1ap->close(); + } + if (f1u) { + f1u->close(); + } + if (e2ap) { + e2ap->close(); + } + if (mac) { + mac->close(); + } + if (rlc) { + rlc->close(); + } + } + + /// \brief Destroy (close and flush) the PCAPs. + void reset() { f1ap.reset(); f1u.reset(); @@ -38,20 +60,24 @@ struct flexible_o_du_pcaps { }; /// Creates the PCAPs of the O-RAN DU. -inline flexible_o_du_pcaps create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers) +inline flexible_o_du_pcaps +create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers, signal_observable& signal_source) { flexible_o_du_pcaps pcaps; const auto& pcap_cfg = config.du_high_cfg.config.pcaps; pcaps.f1ap = pcap_cfg.f1ap.enabled ? create_f1ap_pcap(pcap_cfg.f1ap.filename, workers.get_executor("pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.f1ap.get()); pcaps.f1u = pcap_cfg.f1u.enabled ? create_gtpu_pcap(pcap_cfg.f1u.filename, workers.get_executor("f1u_pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.f1u.get()); pcaps.e2ap = config.e2_cfg.pcaps.enabled ? create_e2ap_pcap(config.e2_cfg.pcaps.filename, workers.get_executor("pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.e2ap.get()); if (pcap_cfg.mac.type != "dlt" && pcap_cfg.mac.type != "udp") { report_error("Invalid type for MAC PCAP. type={}\n", pcap_cfg.mac.type); @@ -61,6 +87,7 @@ inline flexible_o_du_pcaps create_o_du_pcaps(const o_du_high_unit_config& config pcap_cfg.mac.type == "dlt" ? mac_pcap_type::dlt : mac_pcap_type::udp, workers.get_executor("mac_pcap_exec")) : create_null_mac_pcap(); + signal_source.attach(pcaps.mac.get()); if (pcap_cfg.rlc.rb_type != "all" && pcap_cfg.rlc.rb_type != "srb" && pcap_cfg.rlc.rb_type != "drb") { report_error("Invalid rb_type for RLC PCAP. rb_type={}\n", pcap_cfg.rlc.rb_type); @@ -71,6 +98,8 @@ inline flexible_o_du_pcaps create_o_du_pcaps(const o_du_high_unit_config& config pcap_cfg.rlc.rb_type != "drb", pcap_cfg.rlc.rb_type != "srb") : create_null_rlc_pcap(); + signal_source.attach(pcaps.rlc.get()); + return pcaps; } diff --git a/apps/units/o_cu_cp/pcap_factory.h b/apps/units/o_cu_cp/pcap_factory.h index def7262a82..a42bbfd662 100644 --- a/apps/units/o_cu_cp/pcap_factory.h +++ b/apps/units/o_cu_cp/pcap_factory.h @@ -22,7 +22,25 @@ struct o_cu_cp_dlt_pcaps { std::unique_ptr e1ap; std::unique_ptr e2ap; + /// \brief Close (and flush) the PCAPs without destroying the objects. void close() + { + if (ngap) { + ngap->close(); + } + if (f1ap) { + f1ap->close(); + } + if (e1ap) { + e1ap->close(); + } + if (e2ap) { + e2ap->close(); + } + } + + /// \brief Destroy (close and flush) the PCAPs. + void reset() { ngap.reset(); f1ap.reset(); @@ -33,19 +51,28 @@ struct o_cu_cp_dlt_pcaps { /// Creates the DLT PCAPs of the O-RAN CU-CP. inline o_cu_cp_dlt_pcaps create_o_cu_cp_dlt_pcap(const o_cu_cp_unit_config& config, - worker_manager_executor_getter& exec_getter) + worker_manager_executor_getter& exec_getter, + signal_observable& signal_source) { o_cu_cp_dlt_pcaps pcaps; const cu_cp_unit_pcap_config& pcap_cfg = config.cucp_cfg.pcap_cfg; pcaps.ngap = pcap_cfg.ngap.enabled ? create_ngap_pcap(pcap_cfg.ngap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.ngap.get()); + pcaps.f1ap = pcap_cfg.f1ap.enabled ? create_f1ap_pcap(pcap_cfg.f1ap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.f1ap.get()); + pcaps.e1ap = pcap_cfg.e1ap.enabled ? create_e1ap_pcap(pcap_cfg.e1ap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.e1ap.get()); + pcaps.e2ap = config.e2_cfg.pcaps.enabled ? create_e2ap_pcap(config.e2_cfg.pcaps.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.e2ap.get()); + return pcaps; } diff --git a/apps/units/o_cu_up/pcap_factory.h b/apps/units/o_cu_up/pcap_factory.h index 76fe561a08..2337550362 100644 --- a/apps/units/o_cu_up/pcap_factory.h +++ b/apps/units/o_cu_up/pcap_factory.h @@ -21,7 +21,23 @@ struct o_cu_up_dlt_pcaps { std::unique_ptr f1u; std::unique_ptr e1ap; std::unique_ptr e2ap; - void close() + + /// \brief Close (and flush) the PCAPs without destroying the objects. + void close() + { + if (n3) { + n3->close(); + } + if (f1u) { + f1u->close(); + } + if (e1ap) { + e1ap->close(); + } + } + + /// \brief Destroy (close and flush) the PCAPs. + void reset() { n3.reset(); f1u.reset(); @@ -32,23 +48,28 @@ struct o_cu_up_dlt_pcaps { /// Creates the DLT PCAPs of the O-RAN CU-UP. inline o_cu_up_dlt_pcaps create_o_cu_up_dlt_pcaps(const o_cu_up_unit_config& unit_cfg, - worker_manager_executor_getter& exec_getter) + worker_manager_executor_getter& exec_getter, + signal_observable& signal_source) { o_cu_up_dlt_pcaps pcaps; const auto& cu_pcaps = unit_cfg.cu_up_cfg.pcap_cfg; pcaps.e1ap = cu_pcaps.e1ap.enabled ? create_e1ap_pcap(cu_pcaps.e1ap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.e1ap.get()); pcaps.n3 = cu_pcaps.n3.enabled ? create_gtpu_pcap(cu_pcaps.n3.filename, exec_getter.get_executor("n3_pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.n3.get()); pcaps.f1u = cu_pcaps.f1u.enabled ? create_gtpu_pcap(cu_pcaps.f1u.filename, exec_getter.get_executor("f1u_pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.f1u.get()); pcaps.e2ap = unit_cfg.e2_cfg.pcaps.enabled ? create_e2ap_pcap(unit_cfg.e2_cfg.pcaps.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); + signal_source.attach(pcaps.e2ap.get()); return pcaps; } diff --git a/include/srsran/pcap/dlt_pcap.h b/include/srsran/pcap/dlt_pcap.h index 937bc042ab..91499359dd 100644 --- a/include/srsran/pcap/dlt_pcap.h +++ b/include/srsran/pcap/dlt_pcap.h @@ -13,13 +13,14 @@ #include "srsran/adt/byte_buffer.h" #include "srsran/adt/span.h" #include +#include namespace srsran { class task_executor; /// \brief Interface class for writing a DLT PCAP to a file. -class dlt_pcap +class dlt_pcap : public signal_observer { public: virtual ~dlt_pcap() = default; @@ -46,6 +47,7 @@ class null_dlt_pcap : public dlt_pcap bool is_write_enabled() const override { return false; } void push_pdu(const_span pdu) override {} void push_pdu(byte_buffer pdu) override {} + void handle_signal(int signal) override {} }; } // namespace srsran diff --git a/include/srsran/pcap/mac_pcap.h b/include/srsran/pcap/mac_pcap.h index a7429d3d68..d65cf0ae84 100644 --- a/include/srsran/pcap/mac_pcap.h +++ b/include/srsran/pcap/mac_pcap.h @@ -12,6 +12,7 @@ #include "srsran/adt/byte_buffer.h" #include "srsran/adt/span.h" +#include "srsran/support/signal_observer.h" #include namespace srsran { @@ -58,7 +59,7 @@ struct mac_nr_context_info { enum class mac_pcap_type { udp, dlt }; /// \brief Interface class for writing a MAC PCAP to a file. -class mac_pcap +class mac_pcap : public signal_observer { public: virtual ~mac_pcap() = default; @@ -82,6 +83,7 @@ class null_mac_pcap : public mac_pcap bool is_write_enabled() const override { return false; } void push_pdu(const mac_nr_context_info& context, const_span pdu) override {} void push_pdu(const mac_nr_context_info& context, byte_buffer pdu) override {} + void handle_signal(int signal) override {} }; -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/include/srsran/pcap/rlc_pcap.h b/include/srsran/pcap/rlc_pcap.h index e03702382c..39e229769c 100644 --- a/include/srsran/pcap/rlc_pcap.h +++ b/include/srsran/pcap/rlc_pcap.h @@ -14,6 +14,7 @@ #include "srsran/ran/du_types.h" #include "srsran/ran/rb_id.h" #include "srsran/rlc/rlc_config.h" +#include "srsran/support/signal_observer.h" #include "fmt/format.h" #include @@ -22,7 +23,7 @@ namespace srsran { struct pcap_rlc_pdu_context; /// \brief Interface class for writing a RLC PCAP to a file. -class rlc_pcap +class rlc_pcap : public signal_observer { public: virtual ~rlc_pcap() = default; @@ -98,6 +99,7 @@ class null_rlc_pcap : public rlc_pcap bool is_write_enabled() const override { return false; } void push_pdu(const pcap_rlc_pdu_context& context, const span pdu) override {} void push_pdu(const pcap_rlc_pdu_context& context, const byte_buffer_slice& pdu) override {} + void handle_signal(int signal) override {} }; // Pre-defined values for data fields of the PCAP PDU context as defined in Wireshark's "packet-rlc-nr.h" diff --git a/include/srsran/support/signal_handling.h b/include/srsran/support/signal_handling.h index adcd34e390..de11cf03ab 100644 --- a/include/srsran/support/signal_handling.h +++ b/include/srsran/support/signal_handling.h @@ -14,7 +14,7 @@ namespace srsran { -using srsran_signal_handler = void (*)(); +using srsran_signal_handler = void (*)(int signal); /// \brief Registers the specified function to be called when the user interrupts the program execution (eg: via /// Ctrl+C). diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h new file mode 100644 index 0000000000..0a212d1039 --- /dev/null +++ b/include/srsran/support/signal_observer.h @@ -0,0 +1,42 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +namespace srsran { + +class signal_observer; + +class signal_observable +{ +public: + virtual ~signal_observable(){}; + virtual void attach(signal_observer* observer) = 0; + virtual void detach(signal_observer* observer) = 0; + virtual void notify_signal(int signal) = 0; +}; + +class signal_observer +{ +public: + virtual ~signal_observer() + { + if (current_observable) { + current_observable->detach(this); + } + } + void set_current_observable(signal_observable* observable) { current_observable = observable; } + virtual void handle_signal(int signal) = 0; + +private: + signal_observable* current_observable = nullptr; +}; + +} // namespace srsran diff --git a/include/srsran/support/signal_observer_impl.h b/include/srsran/support/signal_observer_impl.h new file mode 100644 index 0000000000..7491dc8a5a --- /dev/null +++ b/include/srsran/support/signal_observer_impl.h @@ -0,0 +1,45 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/support/signal_observer.h" +#include +namespace srsran { + +class signal_observable_impl : public signal_observable +{ +public: + virtual ~signal_observable_impl() {} + + void attach(signal_observer* observer) override + { + observers.push_back(observer); + observer->set_current_observable(this); + } + + void detach(signal_observer* observer) override + { + observers.remove(observer); + observer->set_current_observable(nullptr); + } + + void notify_signal(int signal) override + { + for (auto observer : observers) { + observer->handle_signal(signal); + } + } + +private: + std::list observers; +}; + +} // namespace srsran diff --git a/lib/pcap/dlt_pcap_impl.h b/lib/pcap/dlt_pcap_impl.h index c89cad7dd9..d6a299ab28 100644 --- a/lib/pcap/dlt_pcap_impl.h +++ b/lib/pcap/dlt_pcap_impl.h @@ -40,6 +40,8 @@ class dlt_pcap_impl final : public dlt_pcap void push_pdu(const_span pdu) override; + void handle_signal(int signal) override { close(); } + private: srslog::basic_logger& logger; backend_pcap_writer writer; diff --git a/lib/pcap/mac_pcap_impl.h b/lib/pcap/mac_pcap_impl.h index 5bf9e1bd2c..53a97d328c 100644 --- a/lib/pcap/mac_pcap_impl.h +++ b/lib/pcap/mac_pcap_impl.h @@ -45,6 +45,8 @@ class mac_pcap_impl final : public mac_pcap void push_pdu(const mac_nr_context_info& context, byte_buffer pdu) override; + void handle_signal(int signal) override { close(); } + private: srslog::basic_logger& logger; mac_pcap_type type; diff --git a/lib/pcap/rlc_pcap_impl.h b/lib/pcap/rlc_pcap_impl.h index 024541fb5f..4e3d12f066 100644 --- a/lib/pcap/rlc_pcap_impl.h +++ b/lib/pcap/rlc_pcap_impl.h @@ -36,6 +36,8 @@ class rlc_pcap_impl final : public rlc_pcap void push_pdu(const pcap_rlc_pdu_context& context, const byte_buffer_slice& pdu) override; + void handle_signal(int signal) override { close(); } + private: srslog::basic_logger& logger; bool srb_enabled = true; diff --git a/lib/support/signal_handling.cpp b/lib/support/signal_handling.cpp index 023a134da3..7d5c0e5d31 100644 --- a/lib/support/signal_handling.cpp +++ b/lib/support/signal_handling.cpp @@ -38,7 +38,7 @@ static void signal_handler(int signal) case SIGALRM: fmt::print(stderr, "Could not stop application after {} seconds. Forcing exit.\n", TERMINATION_TIMEOUT_S); if (auto handler = cleanup_handler.exchange(nullptr)) { - handler(); + handler(signal); } ::raise(SIGKILL); [[fallthrough]]; @@ -46,7 +46,7 @@ static void signal_handler(int signal) // All other registered signals try to stop the application gracefully. // Call the user handler, if present, and remove it so that further signals are treated by the default handler. if (auto handler = interrupt_handler.exchange(nullptr)) { - handler(); + handler(signal); } else { return; } diff --git a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp index bb29463457..f5a45912f9 100644 --- a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp +++ b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp @@ -33,6 +33,7 @@ class dummy_dlt_pcap final : public dlt_pcap bool is_write_enabled() const override { return enabled; } void push_pdu(const_span pdu) override { last_sdus.push_blocking(byte_buffer::create(pdu).value()); } virtual void push_pdu(byte_buffer pdu) override { last_sdus.push_blocking(std::move(pdu)); } + void handle_signal(int signal) override { close(); } }; class e1_link : public srs_cu_cp::cu_cp_e1_handler diff --git a/tests/unittests/e2/common/e2_test_helpers.h b/tests/unittests/e2/common/e2_test_helpers.h index daffadd5e1..1ec54c809d 100644 --- a/tests/unittests/e2/common/e2_test_helpers.h +++ b/tests/unittests/e2/common/e2_test_helpers.h @@ -104,6 +104,7 @@ class dummy_e2ap_pcap : public dlt_pcap bool is_write_enabled() const override { return false; } void push_pdu(const_span pdu) override {} void push_pdu(byte_buffer pdu) override {} + void handle_signal(int signal) override {} }; inline e2_message generate_e2_setup_request(std::string oid) diff --git a/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp b/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp index 0ad66be325..b949ab5a05 100644 --- a/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp +++ b/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp @@ -34,6 +34,7 @@ class dummy_dlt_pcap final : public dlt_pcap bool is_write_enabled() const override { return enabled; } void push_pdu(const_span pdu) override { last_sdus.push_blocking(byte_buffer::create(pdu).value()); } void push_pdu(byte_buffer pdu) override { last_sdus.push_blocking(std::move(pdu)); } + void handle_signal(int signal) override { close(); } }; class f1c_link : public srs_cu_cp::cu_cp_f1c_handler From 0b734288f334639002f2014153895320b137756c Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 17 Dec 2024 14:53:37 +0100 Subject: [PATCH 024/107] pcaps: add flush function --- include/srsran/pcap/dlt_pcap.h | 2 ++ include/srsran/pcap/mac_pcap.h | 2 ++ include/srsran/pcap/rlc_pcap.h | 2 ++ lib/pcap/backend_pcap_writer.cpp | 31 ++++++++++++++++--- lib/pcap/backend_pcap_writer.h | 4 +++ lib/pcap/dlt_pcap_impl.cpp | 5 +++ lib/pcap/dlt_pcap_impl.h | 2 ++ lib/pcap/mac_pcap_impl.cpp | 5 +++ lib/pcap/mac_pcap_impl.h | 2 ++ lib/pcap/pcap_file_writer.cpp | 23 ++++++++++---- lib/pcap/pcap_file_writer.h | 1 + lib/pcap/rlc_pcap_impl.cpp | 5 +++ lib/pcap/rlc_pcap_impl.h | 2 ++ .../e1ap/gateways/e1_gateway_test.cpp | 1 + tests/unittests/e2/common/e2_test_helpers.h | 1 + .../f1ap/gateways/f1c_gateway_test.cpp | 1 + 16 files changed, 78 insertions(+), 11 deletions(-) diff --git a/include/srsran/pcap/dlt_pcap.h b/include/srsran/pcap/dlt_pcap.h index 91499359dd..7d413c560d 100644 --- a/include/srsran/pcap/dlt_pcap.h +++ b/include/srsran/pcap/dlt_pcap.h @@ -25,6 +25,7 @@ class dlt_pcap : public signal_observer public: virtual ~dlt_pcap() = default; + virtual void flush() = 0; virtual void close() = 0; virtual bool is_write_enabled() const = 0; virtual void push_pdu(const_span pdu) = 0; @@ -43,6 +44,7 @@ std::unique_ptr create_e2ap_pcap(const std::string& filename, task_exe class null_dlt_pcap : public dlt_pcap { public: + void flush() override {} void close() override {} bool is_write_enabled() const override { return false; } void push_pdu(const_span pdu) override {} diff --git a/include/srsran/pcap/mac_pcap.h b/include/srsran/pcap/mac_pcap.h index d65cf0ae84..c5f6d4d4d7 100644 --- a/include/srsran/pcap/mac_pcap.h +++ b/include/srsran/pcap/mac_pcap.h @@ -64,6 +64,7 @@ class mac_pcap : public signal_observer public: virtual ~mac_pcap() = default; + virtual void flush() = 0; virtual void close() = 0; virtual bool is_write_enabled() const = 0; virtual void push_pdu(const mac_nr_context_info& context, const_span pdu) = 0; @@ -79,6 +80,7 @@ std::unique_ptr create_null_mac_pcap(); class null_mac_pcap : public mac_pcap { public: + void flush() override {} void close() override {} bool is_write_enabled() const override { return false; } void push_pdu(const mac_nr_context_info& context, const_span pdu) override {} diff --git a/include/srsran/pcap/rlc_pcap.h b/include/srsran/pcap/rlc_pcap.h index 39e229769c..4c8432264e 100644 --- a/include/srsran/pcap/rlc_pcap.h +++ b/include/srsran/pcap/rlc_pcap.h @@ -28,6 +28,7 @@ class rlc_pcap : public signal_observer public: virtual ~rlc_pcap() = default; + virtual void flush() = 0; virtual void close() = 0; virtual bool is_write_enabled() const = 0; virtual void push_pdu(const pcap_rlc_pdu_context& context, const span pdu) = 0; @@ -95,6 +96,7 @@ class null_rlc_pcap : public rlc_pcap public: ~null_rlc_pcap() override = default; + void flush() override {} void close() override {} bool is_write_enabled() const override { return false; } void push_pdu(const pcap_rlc_pdu_context& context, const span pdu) override {} diff --git a/lib/pcap/backend_pcap_writer.cpp b/lib/pcap/backend_pcap_writer.cpp index a9c14234e4..743ab3504b 100644 --- a/lib/pcap/backend_pcap_writer.cpp +++ b/lib/pcap/backend_pcap_writer.cpp @@ -58,6 +58,27 @@ backend_pcap_writer::~backend_pcap_writer() close(); } +void backend_pcap_writer::flush() +{ + if (not is_write_enabled()) { + logger.warning("Cannot flush {} PCAP. Cause: The PCAP file is closed", layer_name); + return; + } + if (not backend_exec.defer([this]() { flush_impl(); })) { + logger.warning("Cannot flush {} PCAP. Cause: Task executor queue is full", layer_name); + } +} + +void backend_pcap_writer::flush_impl() +{ + if (not is_write_enabled()) { + logger.warning("Cannot flush {} PCAP. Cause: The PCAP file is closed", layer_name); + return; + } + + writer.flush(); +} + void backend_pcap_writer::close() { bool prev_value = is_open.exchange(false, std::memory_order_relaxed); @@ -66,7 +87,7 @@ void backend_pcap_writer::close() return; } - logger.debug("Scheduling the closing of the \"{}\" pcap writer", layer_name); + logger.debug("Scheduling the closing of the \"{}\" PCAP writer", layer_name); // The pcap writing is still enabled. Dispatch closing of the pcap writer to backend executor. // Note: We block waiting until the pcap finishes closing. @@ -84,7 +105,7 @@ void backend_pcap_writer::write_pdu(byte_buffer pdu) return; } if (not is_write_enabled()) { - logger.warning("Dropped {} PCAP PDU. Cause: The pcap file is closed", layer_name); + logger.warning("Dropped {} PCAP PDU. Cause: The PCAP file is closed", layer_name); return; } if (not backend_exec.defer([this, pdu = std::move(pdu)]() { write_pdu_impl(pdu); })) { @@ -98,7 +119,7 @@ void backend_pcap_writer::write_pdu(pcap_pdu_data pdu) return; } if (not is_write_enabled()) { - logger.warning("Dropped {} PCAP PDU. Cause: The pcap file is closed", layer_name); + logger.warning("Dropped {} PCAP PDU. Cause: The PCAP file is closed", layer_name); return; } if (not backend_exec.defer([this, pdu = std::move(pdu)]() { write_context_pdu_impl(pdu); })) { @@ -109,7 +130,7 @@ void backend_pcap_writer::write_pdu(pcap_pdu_data pdu) void backend_pcap_writer::write_pdu_impl(const byte_buffer& pdu) { if (not is_write_enabled()) { - logger.warning("Dropped {} PCAP PDU. Cause: The pcap file is closed", layer_name); + logger.warning("Dropped {} PCAP PDU. Cause: The PCAP file is closed", layer_name); return; } @@ -124,7 +145,7 @@ void backend_pcap_writer::write_pdu_impl(const byte_buffer& pdu) void backend_pcap_writer::write_context_pdu_impl(const pcap_pdu_data& pdu) { if (not is_write_enabled()) { - logger.warning("Dropped {} PCAP PDU. Cause: The pcap file is closed", layer_name); + logger.warning("Dropped {} PCAP PDU. Cause: The PCAP file is closed", layer_name); return; } diff --git a/lib/pcap/backend_pcap_writer.h b/lib/pcap/backend_pcap_writer.h index b6f936c3a9..6da6eb5d41 100644 --- a/lib/pcap/backend_pcap_writer.h +++ b/lib/pcap/backend_pcap_writer.h @@ -76,6 +76,8 @@ class backend_pcap_writer backend_pcap_writer(backend_pcap_writer&& other) = delete; backend_pcap_writer& operator=(backend_pcap_writer&& other) = delete; + void flush(); + void close(); bool is_write_enabled() const { return is_open.load(std::memory_order_relaxed); } @@ -85,6 +87,8 @@ class backend_pcap_writer void write_pdu(pcap_pdu_data pdu); private: + void flush_impl(); + void write_pdu_impl(const byte_buffer& pdu); void write_context_pdu_impl(const pcap_pdu_data& pdu); diff --git a/lib/pcap/dlt_pcap_impl.cpp b/lib/pcap/dlt_pcap_impl.cpp index 9cbb9a4b0e..1f6f713184 100644 --- a/lib/pcap/dlt_pcap_impl.cpp +++ b/lib/pcap/dlt_pcap_impl.cpp @@ -34,6 +34,11 @@ dlt_pcap_impl::~dlt_pcap_impl() close(); } +void dlt_pcap_impl::flush() +{ + writer.flush(); +} + void dlt_pcap_impl::close() { writer.close(); diff --git a/lib/pcap/dlt_pcap_impl.h b/lib/pcap/dlt_pcap_impl.h index d6a299ab28..609826e914 100644 --- a/lib/pcap/dlt_pcap_impl.h +++ b/lib/pcap/dlt_pcap_impl.h @@ -32,6 +32,8 @@ class dlt_pcap_impl final : public dlt_pcap dlt_pcap_impl(dlt_pcap_impl&& other) = delete; dlt_pcap_impl& operator=(dlt_pcap_impl&& other) = delete; + void flush() override; + void close() override; bool is_write_enabled() const override { return writer.is_write_enabled(); } diff --git a/lib/pcap/mac_pcap_impl.cpp b/lib/pcap/mac_pcap_impl.cpp index 8ffb97be30..97155f9c40 100644 --- a/lib/pcap/mac_pcap_impl.cpp +++ b/lib/pcap/mac_pcap_impl.cpp @@ -30,6 +30,11 @@ mac_pcap_impl::~mac_pcap_impl() close(); } +void mac_pcap_impl::flush() +{ + writer.flush(); +} + void mac_pcap_impl::close() { writer.close(); diff --git a/lib/pcap/mac_pcap_impl.h b/lib/pcap/mac_pcap_impl.h index 53a97d328c..d3eb86e8ec 100644 --- a/lib/pcap/mac_pcap_impl.h +++ b/lib/pcap/mac_pcap_impl.h @@ -37,6 +37,8 @@ class mac_pcap_impl final : public mac_pcap mac_pcap_impl(mac_pcap_impl&& other) = delete; mac_pcap_impl& operator=(mac_pcap_impl&& other) = delete; + void flush() override; + void close() override; bool is_write_enabled() const override { return writer.is_write_enabled(); } diff --git a/lib/pcap/pcap_file_writer.cpp b/lib/pcap/pcap_file_writer.cpp index 7d7fc5ed90..f0a154b67b 100644 --- a/lib/pcap/pcap_file_writer.cpp +++ b/lib/pcap/pcap_file_writer.cpp @@ -49,21 +49,32 @@ bool pcap_file_writer::open(uint32_t dlt_, const std::string& filename_) pcap_fstream.write((char*)&file_header, sizeof(file_header)); if (pcap_fstream.fail()) { - logger.error("Failed to write to pcap: {}", strerror(errno)); + logger.error("Failed to write to PCAP: {}", strerror(errno)); return false; } return true; } +void pcap_file_writer::flush() +{ + if (pcap_fstream.is_open()) { + logger.debug("Flushing PCAP (DLT={})", dlt); + pcap_fstream.flush(); + return; + } + logger.info("Failed to flush closed PCAP (DLT={})", dlt); +} + void pcap_file_writer::close() { + flush(); if (pcap_fstream.is_open()) { logger.debug("Saving PCAP (DLT={}) to \"{}\"", dlt, filename); - pcap_fstream.flush(); pcap_fstream.close(); - logger.info("PCAP (DLT={}) successfully written to \"{}\" and closed.", dlt, filename); + logger.info("Saved PCAP (DLT={}) to \"{}\" and closed", dlt, filename); } + logger.info("Failed to close already closed PCAP (DLT={})", dlt); } void pcap_file_writer::write_pdu_header(uint32_t length) @@ -80,7 +91,7 @@ void pcap_file_writer::write_pdu_header(uint32_t length) pcap_fstream.write((char*)&packet_header, sizeof(packet_header)); if (pcap_fstream.fail()) { - logger.error("Failed to write to pcap: {}", strerror(errno)); + logger.error("Failed to write to PCAP: {}", strerror(errno)); return; } } @@ -93,7 +104,7 @@ void pcap_file_writer::write_pdu(srsran::const_span pdu) pcap_fstream.write((char*)pdu.data(), pdu.size()); if (pcap_fstream.fail()) { - logger.error("Failed to write to pcap: {}", strerror(errno)); + logger.error("Failed to write to PCAP: {}", strerror(errno)); return; } } @@ -107,7 +118,7 @@ void pcap_file_writer::write_pdu(const byte_buffer& pdu) for (span seg : pdu.segments()) { pcap_fstream.write((char*)seg.data(), seg.size()); if (pcap_fstream.fail()) { - logger.error("Failed to write to pcap: {}", strerror(errno)); + logger.error("Failed to write to PCAP: {}", strerror(errno)); return; } } diff --git a/lib/pcap/pcap_file_writer.h b/lib/pcap/pcap_file_writer.h index 29979a72f5..835015c0fc 100644 --- a/lib/pcap/pcap_file_writer.h +++ b/lib/pcap/pcap_file_writer.h @@ -55,6 +55,7 @@ class pcap_file_writer bool is_write_enabled() const { return pcap_fstream.is_open(); } bool open(uint32_t dlt, const std::string& filename); + void flush(); void close(); void write_pdu_header(uint32_t length); void write_pdu(srsran::const_span pdu); diff --git a/lib/pcap/rlc_pcap_impl.cpp b/lib/pcap/rlc_pcap_impl.cpp index 7ac208a7b5..292d319930 100644 --- a/lib/pcap/rlc_pcap_impl.cpp +++ b/lib/pcap/rlc_pcap_impl.cpp @@ -44,6 +44,11 @@ rlc_pcap_impl::~rlc_pcap_impl() close(); } +void rlc_pcap_impl::flush() +{ + writer.flush(); +} + void rlc_pcap_impl::close() { writer.close(); diff --git a/lib/pcap/rlc_pcap_impl.h b/lib/pcap/rlc_pcap_impl.h index 4e3d12f066..fcfe4776e6 100644 --- a/lib/pcap/rlc_pcap_impl.h +++ b/lib/pcap/rlc_pcap_impl.h @@ -28,6 +28,8 @@ class rlc_pcap_impl final : public rlc_pcap rlc_pcap_impl(rlc_pcap_impl&& other) = delete; rlc_pcap_impl& operator=(rlc_pcap_impl&& other) = delete; + void flush() override; + void close() override; bool is_write_enabled() const override { return writer.is_write_enabled(); } diff --git a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp index f5a45912f9..2fecaa1dda 100644 --- a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp +++ b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp @@ -29,6 +29,7 @@ class dummy_dlt_pcap final : public dlt_pcap bool closed = false; blocking_queue last_sdus{16}; + void flush() override {} void close() override { closed = true; } bool is_write_enabled() const override { return enabled; } void push_pdu(const_span pdu) override { last_sdus.push_blocking(byte_buffer::create(pdu).value()); } diff --git a/tests/unittests/e2/common/e2_test_helpers.h b/tests/unittests/e2/common/e2_test_helpers.h index 1ec54c809d..d72c61831c 100644 --- a/tests/unittests/e2/common/e2_test_helpers.h +++ b/tests/unittests/e2/common/e2_test_helpers.h @@ -100,6 +100,7 @@ class dummy_e2_pdu_notifier : public e2_message_notifier class dummy_e2ap_pcap : public dlt_pcap { public: + void flush() override {} void close() override {} bool is_write_enabled() const override { return false; } void push_pdu(const_span pdu) override {} diff --git a/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp b/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp index b949ab5a05..be7f946c77 100644 --- a/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp +++ b/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp @@ -30,6 +30,7 @@ class dummy_dlt_pcap final : public dlt_pcap bool closed = false; blocking_queue last_sdus{16}; + void flush() override {} void close() override { closed = true; } bool is_write_enabled() const override { return enabled; } void push_pdu(const_span pdu) override { last_sdus.push_blocking(byte_buffer::create(pdu).value()); } From 86f3266ae0fb419f1de5ebc4c94c3ee8c924a8f9 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Tue, 17 Dec 2024 14:58:27 +0100 Subject: [PATCH 025/107] pcaps: use flush instead of close when cleanup_signal_handler is called --- lib/pcap/dlt_pcap_impl.h | 2 +- lib/pcap/mac_pcap_impl.h | 2 +- lib/pcap/rlc_pcap_impl.h | 2 +- tests/unittests/e1ap/gateways/e1_gateway_test.cpp | 2 +- 4 files changed, 4 insertions(+), 4 deletions(-) diff --git a/lib/pcap/dlt_pcap_impl.h b/lib/pcap/dlt_pcap_impl.h index 609826e914..6a90c0d0fc 100644 --- a/lib/pcap/dlt_pcap_impl.h +++ b/lib/pcap/dlt_pcap_impl.h @@ -42,7 +42,7 @@ class dlt_pcap_impl final : public dlt_pcap void push_pdu(const_span pdu) override; - void handle_signal(int signal) override { close(); } + void handle_signal(int signal) override { flush(); } private: srslog::basic_logger& logger; diff --git a/lib/pcap/mac_pcap_impl.h b/lib/pcap/mac_pcap_impl.h index d3eb86e8ec..0bd6d127ec 100644 --- a/lib/pcap/mac_pcap_impl.h +++ b/lib/pcap/mac_pcap_impl.h @@ -47,7 +47,7 @@ class mac_pcap_impl final : public mac_pcap void push_pdu(const mac_nr_context_info& context, byte_buffer pdu) override; - void handle_signal(int signal) override { close(); } + void handle_signal(int signal) override { flush(); } private: srslog::basic_logger& logger; diff --git a/lib/pcap/rlc_pcap_impl.h b/lib/pcap/rlc_pcap_impl.h index fcfe4776e6..f95f2ee624 100644 --- a/lib/pcap/rlc_pcap_impl.h +++ b/lib/pcap/rlc_pcap_impl.h @@ -38,7 +38,7 @@ class rlc_pcap_impl final : public rlc_pcap void push_pdu(const pcap_rlc_pdu_context& context, const byte_buffer_slice& pdu) override; - void handle_signal(int signal) override { close(); } + void handle_signal(int signal) override { flush(); } private: srslog::basic_logger& logger; diff --git a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp index 2fecaa1dda..616796ba9b 100644 --- a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp +++ b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp @@ -34,7 +34,7 @@ class dummy_dlt_pcap final : public dlt_pcap bool is_write_enabled() const override { return enabled; } void push_pdu(const_span pdu) override { last_sdus.push_blocking(byte_buffer::create(pdu).value()); } virtual void push_pdu(byte_buffer pdu) override { last_sdus.push_blocking(std::move(pdu)); } - void handle_signal(int signal) override { close(); } + void handle_signal(int signal) override { flush(); } }; class e1_link : public srs_cu_cp::cu_cp_e1_handler From 52a37ff55657951315738c57e81aeb5e5feb5121 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Wed, 18 Dec 2024 13:24:32 +0100 Subject: [PATCH 026/107] pcaps: refactor signal subscription --- apps/cu/cu.cpp | 2 +- apps/du/du.cpp | 2 +- apps/gnb/gnb.cpp | 2 +- .../o_du_high/o_du_high_unit_pcap_factory.h | 47 +++++++++++++------ apps/units/o_cu_cp/pcap_factory.h | 31 ++++++++---- apps/units/o_cu_up/pcap_factory.h | 37 ++++++++++----- include/srsran/pcap/dlt_pcap.h | 4 +- include/srsran/pcap/mac_pcap.h | 4 +- include/srsran/pcap/rlc_pcap.h | 4 +- include/srsran/support/signal_observer.h | 28 ++++++++--- include/srsran/support/signal_observer_impl.h | 8 ++-- lib/pcap/dlt_pcap_impl.h | 3 -- lib/pcap/mac_pcap_impl.h | 2 - lib/pcap/rlc_pcap_impl.h | 2 - .../e1ap/gateways/e1_gateway_test.cpp | 1 - tests/unittests/e2/common/e2_test_helpers.h | 1 - .../f1ap/gateways/f1c_gateway_test.cpp | 1 - 17 files changed, 113 insertions(+), 66 deletions(-) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 19417a47d7..23fb2158f5 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -84,7 +84,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_observable_impl cleanup_signal_observable; +static signal_subject_impl cleanup_signal_observable; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/apps/du/du.cpp b/apps/du/du.cpp index d72bc1d8c5..bfde8ef5d0 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -75,7 +75,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_observable_impl cleanup_signal_observable; +static signal_subject_impl cleanup_signal_observable; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index 0d8cb86ecf..b22e6b3683 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -86,7 +86,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_observable_impl cleanup_signal_observable; +static signal_subject_impl cleanup_signal_observable; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h b/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h index 763852025b..6c316af215 100644 --- a/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h +++ b/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h @@ -15,6 +15,7 @@ #include "srsran/pcap/dlt_pcap.h" #include "srsran/pcap/mac_pcap.h" #include "srsran/pcap/rlc_pcap.h" +#include "srsran/support/signal_observer.h" namespace srsran { @@ -24,10 +25,17 @@ struct flexible_o_du_pcaps { std::unique_ptr f1u; std::unique_ptr e2ap; + std::unique_ptr f1ap_sig_handler; + std::unique_ptr f1u_sig_handler; + std::unique_ptr e2ap_sig_handler; + // MAC and RLC PCAPs std::unique_ptr mac; std::unique_ptr rlc; + std::unique_ptr mac_sig_handler; + std::unique_ptr rlc_sig_handler; + /// \brief Close (and flush) the PCAPs without destroying the objects. void close() { @@ -51,6 +59,12 @@ struct flexible_o_du_pcaps { /// \brief Destroy (close and flush) the PCAPs. void reset() { + f1ap_sig_handler.reset(); + f1u_sig_handler.reset(); + e2ap_sig_handler.reset(); + mac_sig_handler.reset(); + rlc_sig_handler.reset(); + f1ap.reset(); f1u.reset(); e2ap.reset(); @@ -61,44 +75,49 @@ struct flexible_o_du_pcaps { /// Creates the PCAPs of the O-RAN DU. inline flexible_o_du_pcaps -create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers, signal_observable& signal_source) +create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers, signal_subject& signal_source) { flexible_o_du_pcaps pcaps; const auto& pcap_cfg = config.du_high_cfg.config.pcaps; pcaps.f1ap = pcap_cfg.f1ap.enabled ? create_f1ap_pcap(pcap_cfg.f1ap.filename, workers.get_executor("pcap_exec")) : create_null_dlt_pcap(); - signal_source.attach(pcaps.f1ap.get()); + pcaps.f1ap_sig_handler = std::make_unique([&pcaps]() { pcaps.f1ap->flush(); }); + signal_source.attach(pcaps.f1ap_sig_handler.get()); pcaps.f1u = pcap_cfg.f1u.enabled ? create_gtpu_pcap(pcap_cfg.f1u.filename, workers.get_executor("f1u_pcap_exec")) : create_null_dlt_pcap(); - signal_source.attach(pcaps.f1u.get()); + pcaps.f1u_sig_handler = std::make_unique([&pcaps]() { pcaps.f1u->flush(); }); + signal_source.attach(pcaps.f1u_sig_handler.get()); - pcaps.e2ap = config.e2_cfg.pcaps.enabled - ? create_e2ap_pcap(config.e2_cfg.pcaps.filename, workers.get_executor("pcap_exec")) - : create_null_dlt_pcap(); - signal_source.attach(pcaps.e2ap.get()); + pcaps.e2ap = config.e2_cfg.pcaps.enabled + ? create_e2ap_pcap(config.e2_cfg.pcaps.filename, workers.get_executor("pcap_exec")) + : create_null_dlt_pcap(); + pcaps.e2ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e2ap->flush(); }); + signal_source.attach(pcaps.e2ap_sig_handler.get()); if (pcap_cfg.mac.type != "dlt" && pcap_cfg.mac.type != "udp") { report_error("Invalid type for MAC PCAP. type={}\n", pcap_cfg.mac.type); } - pcaps.mac = pcap_cfg.mac.enabled - ? create_mac_pcap(pcap_cfg.mac.filename, + pcaps.mac = pcap_cfg.mac.enabled + ? create_mac_pcap(pcap_cfg.mac.filename, pcap_cfg.mac.type == "dlt" ? mac_pcap_type::dlt : mac_pcap_type::udp, workers.get_executor("mac_pcap_exec")) - : create_null_mac_pcap(); - signal_source.attach(pcaps.mac.get()); + : create_null_mac_pcap(); + pcaps.mac_sig_handler = std::make_unique([&pcaps]() { pcaps.mac->flush(); }); + signal_source.attach(pcaps.mac_sig_handler.get()); if (pcap_cfg.rlc.rb_type != "all" && pcap_cfg.rlc.rb_type != "srb" && pcap_cfg.rlc.rb_type != "drb") { report_error("Invalid rb_type for RLC PCAP. rb_type={}\n", pcap_cfg.rlc.rb_type); } - pcaps.rlc = pcap_cfg.rlc.enabled ? create_rlc_pcap(pcap_cfg.rlc.filename, + pcaps.rlc = pcap_cfg.rlc.enabled ? create_rlc_pcap(pcap_cfg.rlc.filename, workers.get_executor("rlc_pcap_exec"), pcap_cfg.rlc.rb_type != "drb", pcap_cfg.rlc.rb_type != "srb") - : create_null_rlc_pcap(); - signal_source.attach(pcaps.rlc.get()); + : create_null_rlc_pcap(); + pcaps.rlc_sig_handler = std::make_unique([&pcaps]() { pcaps.rlc->flush(); }); + signal_source.attach(pcaps.rlc_sig_handler.get()); return pcaps; } diff --git a/apps/units/o_cu_cp/pcap_factory.h b/apps/units/o_cu_cp/pcap_factory.h index a42bbfd662..2330e6e8f3 100644 --- a/apps/units/o_cu_cp/pcap_factory.h +++ b/apps/units/o_cu_cp/pcap_factory.h @@ -13,6 +13,7 @@ #include "apps/services/worker_manager/worker_manager_worker_getter.h" #include "apps/units/o_cu_cp/o_cu_cp_unit_config.h" #include "srsran/pcap/dlt_pcap.h" +#include "srsran/support/signal_observer.h" namespace srsran { @@ -22,6 +23,11 @@ struct o_cu_cp_dlt_pcaps { std::unique_ptr e1ap; std::unique_ptr e2ap; + std::unique_ptr ngap_sig_handler; + std::unique_ptr f1ap_sig_handler; + std::unique_ptr e1ap_sig_handler; + std::unique_ptr e2ap_sig_handler; + /// \brief Close (and flush) the PCAPs without destroying the objects. void close() { @@ -42,6 +48,11 @@ struct o_cu_cp_dlt_pcaps { /// \brief Destroy (close and flush) the PCAPs. void reset() { + ngap_sig_handler.reset(); + f1ap_sig_handler.reset(); + e1ap_sig_handler.reset(); + e2ap_sig_handler.reset(); + ngap.reset(); f1ap.reset(); e1ap.reset(); @@ -52,26 +63,30 @@ struct o_cu_cp_dlt_pcaps { /// Creates the DLT PCAPs of the O-RAN CU-CP. inline o_cu_cp_dlt_pcaps create_o_cu_cp_dlt_pcap(const o_cu_cp_unit_config& config, worker_manager_executor_getter& exec_getter, - signal_observable& signal_source) + signal_subject& signal_source) { o_cu_cp_dlt_pcaps pcaps; const cu_cp_unit_pcap_config& pcap_cfg = config.cucp_cfg.pcap_cfg; pcaps.ngap = pcap_cfg.ngap.enabled ? create_ngap_pcap(pcap_cfg.ngap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - signal_source.attach(pcaps.ngap.get()); + pcaps.ngap_sig_handler = std::make_unique([&pcaps]() { pcaps.ngap->flush(); }); + signal_source.attach(pcaps.ngap_sig_handler.get()); pcaps.f1ap = pcap_cfg.f1ap.enabled ? create_f1ap_pcap(pcap_cfg.f1ap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - signal_source.attach(pcaps.f1ap.get()); + pcaps.f1ap_sig_handler = std::make_unique([&pcaps]() { pcaps.f1ap->flush(); }); + signal_source.attach(pcaps.f1ap_sig_handler.get()); pcaps.e1ap = pcap_cfg.e1ap.enabled ? create_e1ap_pcap(pcap_cfg.e1ap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - signal_source.attach(pcaps.e1ap.get()); + pcaps.e1ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e1ap->flush(); }); + signal_source.attach(pcaps.e1ap_sig_handler.get()); - pcaps.e2ap = config.e2_cfg.pcaps.enabled - ? create_e2ap_pcap(config.e2_cfg.pcaps.filename, exec_getter.get_executor("pcap_exec")) - : create_null_dlt_pcap(); - signal_source.attach(pcaps.e2ap.get()); + pcaps.e2ap = config.e2_cfg.pcaps.enabled + ? create_e2ap_pcap(config.e2_cfg.pcaps.filename, exec_getter.get_executor("pcap_exec")) + : create_null_dlt_pcap(); + pcaps.e2ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e2ap->flush(); }); + signal_source.attach(pcaps.e2ap_sig_handler.get()); return pcaps; } diff --git a/apps/units/o_cu_up/pcap_factory.h b/apps/units/o_cu_up/pcap_factory.h index 2337550362..0e9fbc6dee 100644 --- a/apps/units/o_cu_up/pcap_factory.h +++ b/apps/units/o_cu_up/pcap_factory.h @@ -13,6 +13,7 @@ #include "apps/services/worker_manager/worker_manager_worker_getter.h" #include "apps/units/o_cu_up/o_cu_up_unit_config.h" #include "srsran/pcap/dlt_pcap.h" +#include "srsran/support/signal_observer.h" namespace srsran { @@ -22,6 +23,11 @@ struct o_cu_up_dlt_pcaps { std::unique_ptr e1ap; std::unique_ptr e2ap; + std::unique_ptr n3_sig_handler; + std::unique_ptr f1u_sig_handler; + std::unique_ptr e1ap_sig_handler; + std::unique_ptr e2ap_sig_handler; + /// \brief Close (and flush) the PCAPs without destroying the objects. void close() { @@ -39,6 +45,11 @@ struct o_cu_up_dlt_pcaps { /// \brief Destroy (close and flush) the PCAPs. void reset() { + n3_sig_handler.reset(); + f1u_sig_handler.reset(); + e1ap_sig_handler.reset(); + e2ap_sig_handler.reset(); + n3.reset(); f1u.reset(); e1ap.reset(); @@ -49,27 +60,31 @@ struct o_cu_up_dlt_pcaps { /// Creates the DLT PCAPs of the O-RAN CU-UP. inline o_cu_up_dlt_pcaps create_o_cu_up_dlt_pcaps(const o_cu_up_unit_config& unit_cfg, worker_manager_executor_getter& exec_getter, - signal_observable& signal_source) + signal_subject& signal_source) { o_cu_up_dlt_pcaps pcaps; const auto& cu_pcaps = unit_cfg.cu_up_cfg.pcap_cfg; - pcaps.e1ap = cu_pcaps.e1ap.enabled ? create_e1ap_pcap(cu_pcaps.e1ap.filename, exec_getter.get_executor("pcap_exec")) - : create_null_dlt_pcap(); - signal_source.attach(pcaps.e1ap.get()); - pcaps.n3 = cu_pcaps.n3.enabled ? create_gtpu_pcap(cu_pcaps.n3.filename, exec_getter.get_executor("n3_pcap_exec")) : create_null_dlt_pcap(); - signal_source.attach(pcaps.n3.get()); + pcaps.n3_sig_handler = std::make_unique([&pcaps]() { pcaps.n3->flush(); }); + signal_source.attach(pcaps.n3_sig_handler.get()); pcaps.f1u = cu_pcaps.f1u.enabled ? create_gtpu_pcap(cu_pcaps.f1u.filename, exec_getter.get_executor("f1u_pcap_exec")) : create_null_dlt_pcap(); - signal_source.attach(pcaps.f1u.get()); + pcaps.f1u_sig_handler = std::make_unique([&pcaps]() { pcaps.f1u->flush(); }); + signal_source.attach(pcaps.f1u_sig_handler.get()); + + pcaps.e1ap = cu_pcaps.e1ap.enabled ? create_e1ap_pcap(cu_pcaps.e1ap.filename, exec_getter.get_executor("pcap_exec")) + : create_null_dlt_pcap(); + pcaps.e1ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e1ap->flush(); }); + signal_source.attach(pcaps.e1ap_sig_handler.get()); - pcaps.e2ap = unit_cfg.e2_cfg.pcaps.enabled - ? create_e2ap_pcap(unit_cfg.e2_cfg.pcaps.filename, exec_getter.get_executor("pcap_exec")) - : create_null_dlt_pcap(); - signal_source.attach(pcaps.e2ap.get()); + pcaps.e2ap = unit_cfg.e2_cfg.pcaps.enabled + ? create_e2ap_pcap(unit_cfg.e2_cfg.pcaps.filename, exec_getter.get_executor("pcap_exec")) + : create_null_dlt_pcap(); + pcaps.e2ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e2ap->flush(); }); + signal_source.attach(pcaps.e2ap_sig_handler.get()); return pcaps; } diff --git a/include/srsran/pcap/dlt_pcap.h b/include/srsran/pcap/dlt_pcap.h index 7d413c560d..4d477c4b7a 100644 --- a/include/srsran/pcap/dlt_pcap.h +++ b/include/srsran/pcap/dlt_pcap.h @@ -13,14 +13,13 @@ #include "srsran/adt/byte_buffer.h" #include "srsran/adt/span.h" #include -#include namespace srsran { class task_executor; /// \brief Interface class for writing a DLT PCAP to a file. -class dlt_pcap : public signal_observer +class dlt_pcap { public: virtual ~dlt_pcap() = default; @@ -49,7 +48,6 @@ class null_dlt_pcap : public dlt_pcap bool is_write_enabled() const override { return false; } void push_pdu(const_span pdu) override {} void push_pdu(byte_buffer pdu) override {} - void handle_signal(int signal) override {} }; } // namespace srsran diff --git a/include/srsran/pcap/mac_pcap.h b/include/srsran/pcap/mac_pcap.h index c5f6d4d4d7..b80aedf8e4 100644 --- a/include/srsran/pcap/mac_pcap.h +++ b/include/srsran/pcap/mac_pcap.h @@ -12,7 +12,6 @@ #include "srsran/adt/byte_buffer.h" #include "srsran/adt/span.h" -#include "srsran/support/signal_observer.h" #include namespace srsran { @@ -59,7 +58,7 @@ struct mac_nr_context_info { enum class mac_pcap_type { udp, dlt }; /// \brief Interface class for writing a MAC PCAP to a file. -class mac_pcap : public signal_observer +class mac_pcap { public: virtual ~mac_pcap() = default; @@ -85,7 +84,6 @@ class null_mac_pcap : public mac_pcap bool is_write_enabled() const override { return false; } void push_pdu(const mac_nr_context_info& context, const_span pdu) override {} void push_pdu(const mac_nr_context_info& context, byte_buffer pdu) override {} - void handle_signal(int signal) override {} }; } // namespace srsran diff --git a/include/srsran/pcap/rlc_pcap.h b/include/srsran/pcap/rlc_pcap.h index 4c8432264e..3323b1a67e 100644 --- a/include/srsran/pcap/rlc_pcap.h +++ b/include/srsran/pcap/rlc_pcap.h @@ -14,7 +14,6 @@ #include "srsran/ran/du_types.h" #include "srsran/ran/rb_id.h" #include "srsran/rlc/rlc_config.h" -#include "srsran/support/signal_observer.h" #include "fmt/format.h" #include @@ -23,7 +22,7 @@ namespace srsran { struct pcap_rlc_pdu_context; /// \brief Interface class for writing a RLC PCAP to a file. -class rlc_pcap : public signal_observer +class rlc_pcap { public: virtual ~rlc_pcap() = default; @@ -101,7 +100,6 @@ class null_rlc_pcap : public rlc_pcap bool is_write_enabled() const override { return false; } void push_pdu(const pcap_rlc_pdu_context& context, const span pdu) override {} void push_pdu(const pcap_rlc_pdu_context& context, const byte_buffer_slice& pdu) override {} - void handle_signal(int signal) override {} }; // Pre-defined values for data fields of the PCAP PDU context as defined in Wireshark's "packet-rlc-nr.h" diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h index 0a212d1039..82fdd4ad16 100644 --- a/include/srsran/support/signal_observer.h +++ b/include/srsran/support/signal_observer.h @@ -10,33 +10,47 @@ #pragma once +#include namespace srsran { class signal_observer; -class signal_observable +class signal_subject { public: - virtual ~signal_observable(){}; + virtual ~signal_subject() {}; virtual void attach(signal_observer* observer) = 0; virtual void detach(signal_observer* observer) = 0; virtual void notify_signal(int signal) = 0; }; +using signal_callback = std::function; + class signal_observer { public: + signal_observer(signal_callback callback_) : callback(callback_) {} + virtual ~signal_observer() { - if (current_observable) { - current_observable->detach(this); + if (current_subject) { + current_subject->detach(this); } } - void set_current_observable(signal_observable* observable) { current_observable = observable; } - virtual void handle_signal(int signal) = 0; + + void set_current_subject(signal_subject* subject) + { + if (current_subject) { + current_subject->detach(this); + } + current_subject = subject; + } + + void handle_signal(int signal) { callback(); }; private: - signal_observable* current_observable = nullptr; + signal_callback callback; + signal_subject* current_subject = nullptr; }; } // namespace srsran diff --git a/include/srsran/support/signal_observer_impl.h b/include/srsran/support/signal_observer_impl.h index 7491dc8a5a..af682d8bbc 100644 --- a/include/srsran/support/signal_observer_impl.h +++ b/include/srsran/support/signal_observer_impl.h @@ -14,21 +14,21 @@ #include namespace srsran { -class signal_observable_impl : public signal_observable +class signal_subject_impl : public signal_subject { public: - virtual ~signal_observable_impl() {} + virtual ~signal_subject_impl() {} void attach(signal_observer* observer) override { observers.push_back(observer); - observer->set_current_observable(this); + observer->set_current_subject(this); } void detach(signal_observer* observer) override { observers.remove(observer); - observer->set_current_observable(nullptr); + observer->set_current_subject(nullptr); } void notify_signal(int signal) override diff --git a/lib/pcap/dlt_pcap_impl.h b/lib/pcap/dlt_pcap_impl.h index 6a90c0d0fc..eda4395d51 100644 --- a/lib/pcap/dlt_pcap_impl.h +++ b/lib/pcap/dlt_pcap_impl.h @@ -11,7 +11,6 @@ #pragma once #include "backend_pcap_writer.h" -#include "pcap_file_writer.h" #include "srsran/adt/byte_buffer.h" #include "srsran/pcap/dlt_pcap.h" @@ -42,8 +41,6 @@ class dlt_pcap_impl final : public dlt_pcap void push_pdu(const_span pdu) override; - void handle_signal(int signal) override { flush(); } - private: srslog::basic_logger& logger; backend_pcap_writer writer; diff --git a/lib/pcap/mac_pcap_impl.h b/lib/pcap/mac_pcap_impl.h index 0bd6d127ec..6133d109be 100644 --- a/lib/pcap/mac_pcap_impl.h +++ b/lib/pcap/mac_pcap_impl.h @@ -47,8 +47,6 @@ class mac_pcap_impl final : public mac_pcap void push_pdu(const mac_nr_context_info& context, byte_buffer pdu) override; - void handle_signal(int signal) override { flush(); } - private: srslog::basic_logger& logger; mac_pcap_type type; diff --git a/lib/pcap/rlc_pcap_impl.h b/lib/pcap/rlc_pcap_impl.h index f95f2ee624..04329cccad 100644 --- a/lib/pcap/rlc_pcap_impl.h +++ b/lib/pcap/rlc_pcap_impl.h @@ -38,8 +38,6 @@ class rlc_pcap_impl final : public rlc_pcap void push_pdu(const pcap_rlc_pdu_context& context, const byte_buffer_slice& pdu) override; - void handle_signal(int signal) override { flush(); } - private: srslog::basic_logger& logger; bool srb_enabled = true; diff --git a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp index 616796ba9b..f9b7d0e04d 100644 --- a/tests/unittests/e1ap/gateways/e1_gateway_test.cpp +++ b/tests/unittests/e1ap/gateways/e1_gateway_test.cpp @@ -34,7 +34,6 @@ class dummy_dlt_pcap final : public dlt_pcap bool is_write_enabled() const override { return enabled; } void push_pdu(const_span pdu) override { last_sdus.push_blocking(byte_buffer::create(pdu).value()); } virtual void push_pdu(byte_buffer pdu) override { last_sdus.push_blocking(std::move(pdu)); } - void handle_signal(int signal) override { flush(); } }; class e1_link : public srs_cu_cp::cu_cp_e1_handler diff --git a/tests/unittests/e2/common/e2_test_helpers.h b/tests/unittests/e2/common/e2_test_helpers.h index d72c61831c..9b3c1f65e9 100644 --- a/tests/unittests/e2/common/e2_test_helpers.h +++ b/tests/unittests/e2/common/e2_test_helpers.h @@ -105,7 +105,6 @@ class dummy_e2ap_pcap : public dlt_pcap bool is_write_enabled() const override { return false; } void push_pdu(const_span pdu) override {} void push_pdu(byte_buffer pdu) override {} - void handle_signal(int signal) override {} }; inline e2_message generate_e2_setup_request(std::string oid) diff --git a/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp b/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp index be7f946c77..b974748e63 100644 --- a/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp +++ b/tests/unittests/f1ap/gateways/f1c_gateway_test.cpp @@ -35,7 +35,6 @@ class dummy_dlt_pcap final : public dlt_pcap bool is_write_enabled() const override { return enabled; } void push_pdu(const_span pdu) override { last_sdus.push_blocking(byte_buffer::create(pdu).value()); } void push_pdu(byte_buffer pdu) override { last_sdus.push_blocking(std::move(pdu)); } - void handle_signal(int signal) override { close(); } }; class f1c_link : public srs_cu_cp::cu_cp_f1c_handler From fec67ecdcc455bffaa504fc496b94770371b936c Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Wed, 18 Dec 2024 15:25:20 +0100 Subject: [PATCH 027/107] support: replace list of signal observers with vector --- include/srsran/support/signal_observer_impl.h | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/include/srsran/support/signal_observer_impl.h b/include/srsran/support/signal_observer_impl.h index af682d8bbc..51c91aec07 100644 --- a/include/srsran/support/signal_observer_impl.h +++ b/include/srsran/support/signal_observer_impl.h @@ -11,7 +11,7 @@ #pragma once #include "srsran/support/signal_observer.h" -#include +#include namespace srsran { class signal_subject_impl : public signal_subject @@ -27,7 +27,13 @@ class signal_subject_impl : public signal_subject void detach(signal_observer* observer) override { - observers.remove(observer); + for (auto it = observers.begin(); it != observers.end();) { + if (*it == observer) { + it = observers.erase(it); + continue; + } + ++it; + } observer->set_current_subject(nullptr); } @@ -39,7 +45,7 @@ class signal_subject_impl : public signal_subject } private: - std::list observers; + std::vector observers; }; } // namespace srsran From c8cf616fcf260108d66bc65b2baf769258191aa0 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Wed, 18 Dec 2024 17:44:25 +0100 Subject: [PATCH 028/107] support: merge signal subject impl into header to avoid virtual functionss --- apps/cu/cu.cpp | 4 +- apps/du/du.cpp | 4 +- apps/gnb/gnb.cpp | 4 +- include/srsran/support/signal_observer.h | 37 ++++++++++++-- include/srsran/support/signal_observer_impl.h | 51 ------------------- 5 files changed, 39 insertions(+), 61 deletions(-) delete mode 100644 include/srsran/support/signal_observer_impl.h diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 23fb2158f5..203852402e 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -43,7 +43,7 @@ #include "srsran/support/io/io_broker_factory.h" #include "srsran/support/io/io_timer_source.h" #include "srsran/support/signal_handling.h" -#include "srsran/support/signal_observer_impl.h" +#include "srsran/support/signal_observer.h" #include "srsran/support/sysinfo.h" #include "srsran/support/timers.h" #include "srsran/support/tracing/event_tracing.h" @@ -84,7 +84,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject_impl cleanup_signal_observable; +static signal_subject cleanup_signal_observable; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/apps/du/du.cpp b/apps/du/du.cpp index bfde8ef5d0..c499ddb6c3 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -37,7 +37,7 @@ #include "srsran/support/cpu_features.h" #include "srsran/support/io/io_broker_factory.h" #include "srsran/support/signal_handling.h" -#include "srsran/support/signal_observer_impl.h" +#include "srsran/support/signal_observer.h" #include "srsran/support/tracing/event_tracing.h" #include "srsran/support/versioning/build_info.h" #include "srsran/support/versioning/version.h" @@ -75,7 +75,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject_impl cleanup_signal_observable; +static signal_subject cleanup_signal_observable; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index b22e6b3683..ae27ab1e42 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -43,7 +43,7 @@ #include "srsran/support/cpu_features.h" #include "srsran/support/io/io_broker_factory.h" #include "srsran/support/signal_handling.h" -#include "srsran/support/signal_observer_impl.h" +#include "srsran/support/signal_observer.h" #include "srsran/support/tracing/event_tracing.h" #include "srsran/support/versioning/build_info.h" #include "srsran/support/versioning/version.h" @@ -86,7 +86,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject_impl cleanup_signal_observable; +static signal_subject cleanup_signal_observable; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h index 82fdd4ad16..de672da2e7 100644 --- a/include/srsran/support/signal_observer.h +++ b/include/srsran/support/signal_observer.h @@ -11,6 +11,8 @@ #pragma once #include +#include + namespace srsran { class signal_observer; @@ -18,10 +20,12 @@ class signal_observer; class signal_subject { public: - virtual ~signal_subject() {}; - virtual void attach(signal_observer* observer) = 0; - virtual void detach(signal_observer* observer) = 0; - virtual void notify_signal(int signal) = 0; + void attach(signal_observer* observer); + void detach(signal_observer* observer); + void notify_signal(int signal); + +private: + std::vector observers; }; using signal_callback = std::function; @@ -53,4 +57,29 @@ class signal_observer signal_subject* current_subject = nullptr; }; +void signal_subject::attach(signal_observer* observer) +{ + observers.push_back(observer); + observer->set_current_subject(this); +} + +void signal_subject::detach(signal_observer* observer) +{ + for (auto it = observers.begin(); it != observers.end();) { + if (*it == observer) { + it = observers.erase(it); + continue; + } + ++it; + } + observer->set_current_subject(nullptr); +} + +void signal_subject::notify_signal(int signal) +{ + for (auto observer : observers) { + observer->handle_signal(signal); + } +} + } // namespace srsran diff --git a/include/srsran/support/signal_observer_impl.h b/include/srsran/support/signal_observer_impl.h deleted file mode 100644 index 51c91aec07..0000000000 --- a/include/srsran/support/signal_observer_impl.h +++ /dev/null @@ -1,51 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the distribution. - * - */ - -#pragma once - -#include "srsran/support/signal_observer.h" -#include -namespace srsran { - -class signal_subject_impl : public signal_subject -{ -public: - virtual ~signal_subject_impl() {} - - void attach(signal_observer* observer) override - { - observers.push_back(observer); - observer->set_current_subject(this); - } - - void detach(signal_observer* observer) override - { - for (auto it = observers.begin(); it != observers.end();) { - if (*it == observer) { - it = observers.erase(it); - continue; - } - ++it; - } - observer->set_current_subject(nullptr); - } - - void notify_signal(int signal) override - { - for (auto observer : observers) { - observer->handle_signal(signal); - } - } - -private: - std::vector observers; -}; - -} // namespace srsran From 5fc6382e236949dbf91c583783ca716df9624e9e Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 19 Dec 2024 09:11:02 +0100 Subject: [PATCH 029/107] support: break call cycle in signal_observer::set_current_subject --- include/srsran/support/signal_observer.h | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h index de672da2e7..4e1acdd1bb 100644 --- a/include/srsran/support/signal_observer.h +++ b/include/srsran/support/signal_observer.h @@ -42,13 +42,7 @@ class signal_observer } } - void set_current_subject(signal_subject* subject) - { - if (current_subject) { - current_subject->detach(this); - } - current_subject = subject; - } + void set_current_subject(signal_subject* subject) { current_subject = subject; } void handle_signal(int signal) { callback(); }; From ec3d956c8cffc83c164bdce856cf7edc88ca2fe0 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 19 Dec 2024 14:52:00 +0100 Subject: [PATCH 030/107] support: exlicit ctor for signal_observer, move callback --- include/srsran/support/signal_observer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h index 4e1acdd1bb..fe69333c96 100644 --- a/include/srsran/support/signal_observer.h +++ b/include/srsran/support/signal_observer.h @@ -33,7 +33,7 @@ using signal_callback = std::function; class signal_observer { public: - signal_observer(signal_callback callback_) : callback(callback_) {} + explicit signal_observer(signal_callback callback_) : callback(std::move(callback_)) {} virtual ~signal_observer() { From 9c4efd12b5e09915757c293aa27967e86c0eee2e Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Thu, 19 Dec 2024 14:55:44 +0100 Subject: [PATCH 031/107] apps: rename cleanup_signal_observable to /dispatcher --- apps/cu/cu.cpp | 8 ++++---- apps/du/du.cpp | 6 +++--- apps/gnb/gnb.cpp | 10 +++++----- 3 files changed, 12 insertions(+), 12 deletions(-) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 203852402e..bb64f358fc 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -84,12 +84,12 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject cleanup_signal_observable; +static signal_subject cleanup_signal_dispatcher; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) { - cleanup_signal_observable.notify_signal(signal); + cleanup_signal_dispatcher.notify_signal(signal); srslog::flush(); } @@ -269,9 +269,9 @@ int main(int argc, char** argv) // Create layer specific PCAPs. o_cu_cp_dlt_pcaps cu_cp_dlt_pcaps = create_o_cu_cp_dlt_pcap( - o_cu_cp_app_unit->get_o_cu_cp_unit_config(), *workers.get_executor_getter(), cleanup_signal_observable); + o_cu_cp_app_unit->get_o_cu_cp_unit_config(), *workers.get_executor_getter(), cleanup_signal_dispatcher); o_cu_up_dlt_pcaps cu_up_dlt_pcaps = create_o_cu_up_dlt_pcaps( - o_cu_up_app_unit->get_o_cu_up_unit_config(), *workers.get_executor_getter(), cleanup_signal_observable); + o_cu_up_app_unit->get_o_cu_up_unit_config(), *workers.get_executor_getter(), cleanup_signal_dispatcher); // Create IO broker. const auto& low_prio_cpu_mask = cu_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask; diff --git a/apps/du/du.cpp b/apps/du/du.cpp index c499ddb6c3..1948031c76 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -75,12 +75,12 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject cleanup_signal_observable; +static signal_subject cleanup_signal_dispatcher; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) { - cleanup_signal_observable.notify_signal(signal); + cleanup_signal_dispatcher.notify_signal(signal); srslog::flush(); } @@ -250,7 +250,7 @@ int main(int argc, char** argv) std::unique_ptr epoll_broker = create_io_broker(io_broker_type::epoll, io_broker_cfg); flexible_o_du_pcaps du_pcaps = - create_o_du_pcaps(o_du_app_unit->get_o_du_high_unit_config(), workers, cleanup_signal_observable); + create_o_du_pcaps(o_du_app_unit->get_o_du_high_unit_config(), workers, cleanup_signal_dispatcher); // Instantiate F1-C client gateway. std::unique_ptr f1c_gw = create_f1c_client_gateway(du_cfg.f1ap_cfg.cu_cp_address, diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index ae27ab1e42..256e9cf5fb 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -86,12 +86,12 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject cleanup_signal_observable; +static signal_subject cleanup_signal_dispatcher; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) { - cleanup_signal_observable.notify_signal(signal); + cleanup_signal_dispatcher.notify_signal(signal); srslog::flush(); } @@ -330,11 +330,11 @@ int main(int argc, char** argv) o_cu_up_app_unit->get_o_cu_up_unit_config().cu_up_cfg.pcap_cfg.disable_e1_pcaps(); o_du_app_unit->get_o_du_high_unit_config().du_high_cfg.config.pcaps.disable_f1_pcaps(); o_cu_cp_dlt_pcaps cu_cp_dlt_pcaps = create_o_cu_cp_dlt_pcap( - o_cu_cp_app_unit->get_o_cu_cp_unit_config(), *workers.get_executor_getter(), cleanup_signal_observable); + o_cu_cp_app_unit->get_o_cu_cp_unit_config(), *workers.get_executor_getter(), cleanup_signal_dispatcher); o_cu_up_dlt_pcaps cu_up_dlt_pcaps = create_o_cu_up_dlt_pcaps( - o_cu_up_app_unit->get_o_cu_up_unit_config(), *workers.get_executor_getter(), cleanup_signal_observable); + o_cu_up_app_unit->get_o_cu_up_unit_config(), *workers.get_executor_getter(), cleanup_signal_dispatcher); flexible_o_du_pcaps du_pcaps = - create_o_du_pcaps(o_du_app_unit->get_o_du_high_unit_config(), workers, cleanup_signal_observable); + create_o_du_pcaps(o_du_app_unit->get_o_du_high_unit_config(), workers, cleanup_signal_dispatcher); std::unique_ptr f1c_gw = create_f1c_local_connector(f1c_local_connector_config{*cu_cp_dlt_pcaps.f1ap}); From 4cb759ab17bcaf7f21287f5ffc8add8c7f4413a1 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 20 Dec 2024 11:47:41 +0100 Subject: [PATCH 032/107] support: rename signal_subject to signal_dispatcher --- apps/cu/cu.cpp | 2 +- apps/du/du.cpp | 2 +- apps/gnb/gnb.cpp | 2 +- .../o_du_high/o_du_high_unit_pcap_factory.h | 2 +- apps/units/o_cu_cp/pcap_factory.h | 2 +- apps/units/o_cu_up/pcap_factory.h | 2 +- include/srsran/support/signal_observer.h | 14 +++++++------- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index bb64f358fc..55ce114f12 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -84,7 +84,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject cleanup_signal_dispatcher; +static signal_dispatcher cleanup_signal_dispatcher; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/apps/du/du.cpp b/apps/du/du.cpp index 1948031c76..cc981f2858 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -75,7 +75,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject cleanup_signal_dispatcher; +static signal_dispatcher cleanup_signal_dispatcher; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index 256e9cf5fb..ed62ecb07d 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -86,7 +86,7 @@ static void interrupt_signal_handler(int signal) is_app_running = false; } -static signal_subject cleanup_signal_dispatcher; +static signal_dispatcher cleanup_signal_dispatcher; /// Function to call when the application is going to be forcefully shutdown. static void cleanup_signal_handler(int signal) diff --git a/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h b/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h index 6c316af215..948025c6bf 100644 --- a/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h +++ b/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h @@ -75,7 +75,7 @@ struct flexible_o_du_pcaps { /// Creates the PCAPs of the O-RAN DU. inline flexible_o_du_pcaps -create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers, signal_subject& signal_source) +create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers, signal_dispatcher& signal_source) { flexible_o_du_pcaps pcaps; diff --git a/apps/units/o_cu_cp/pcap_factory.h b/apps/units/o_cu_cp/pcap_factory.h index 2330e6e8f3..9d417b599d 100644 --- a/apps/units/o_cu_cp/pcap_factory.h +++ b/apps/units/o_cu_cp/pcap_factory.h @@ -63,7 +63,7 @@ struct o_cu_cp_dlt_pcaps { /// Creates the DLT PCAPs of the O-RAN CU-CP. inline o_cu_cp_dlt_pcaps create_o_cu_cp_dlt_pcap(const o_cu_cp_unit_config& config, worker_manager_executor_getter& exec_getter, - signal_subject& signal_source) + signal_dispatcher& signal_source) { o_cu_cp_dlt_pcaps pcaps; const cu_cp_unit_pcap_config& pcap_cfg = config.cucp_cfg.pcap_cfg; diff --git a/apps/units/o_cu_up/pcap_factory.h b/apps/units/o_cu_up/pcap_factory.h index 0e9fbc6dee..83d66f4b8d 100644 --- a/apps/units/o_cu_up/pcap_factory.h +++ b/apps/units/o_cu_up/pcap_factory.h @@ -60,7 +60,7 @@ struct o_cu_up_dlt_pcaps { /// Creates the DLT PCAPs of the O-RAN CU-UP. inline o_cu_up_dlt_pcaps create_o_cu_up_dlt_pcaps(const o_cu_up_unit_config& unit_cfg, worker_manager_executor_getter& exec_getter, - signal_subject& signal_source) + signal_dispatcher& signal_source) { o_cu_up_dlt_pcaps pcaps; diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h index fe69333c96..a174bebfb9 100644 --- a/include/srsran/support/signal_observer.h +++ b/include/srsran/support/signal_observer.h @@ -17,7 +17,7 @@ namespace srsran { class signal_observer; -class signal_subject +class signal_dispatcher { public: void attach(signal_observer* observer); @@ -42,22 +42,22 @@ class signal_observer } } - void set_current_subject(signal_subject* subject) { current_subject = subject; } + void set_current_subject(signal_dispatcher* subject) { current_subject = subject; } void handle_signal(int signal) { callback(); }; private: - signal_callback callback; - signal_subject* current_subject = nullptr; + signal_callback callback; + signal_dispatcher* current_subject = nullptr; }; -void signal_subject::attach(signal_observer* observer) +void signal_dispatcher::attach(signal_observer* observer) { observers.push_back(observer); observer->set_current_subject(this); } -void signal_subject::detach(signal_observer* observer) +void signal_dispatcher::detach(signal_observer* observer) { for (auto it = observers.begin(); it != observers.end();) { if (*it == observer) { @@ -69,7 +69,7 @@ void signal_subject::detach(signal_observer* observer) observer->set_current_subject(nullptr); } -void signal_subject::notify_signal(int signal) +void signal_dispatcher::notify_signal(int signal) { for (auto observer : observers) { observer->handle_signal(signal); From 6cf9f3571633b9ee4ff98140a446c739f9adad6b Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 20 Dec 2024 13:04:18 +0100 Subject: [PATCH 033/107] support: simplify API and document signal_observer/signal_dispatcher --- .../o_du_high/o_du_high_unit_pcap_factory.h | 16 +++---- apps/units/o_cu_cp/pcap_factory.h | 12 ++--- apps/units/o_cu_up/pcap_factory.h | 12 ++--- include/srsran/support/signal_observer.h | 44 ++++++++++++++----- 4 files changed, 46 insertions(+), 38 deletions(-) diff --git a/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h b/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h index 948025c6bf..e2a64d6b86 100644 --- a/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h +++ b/apps/units/flexible_o_du/o_du_high/o_du_high_unit_pcap_factory.h @@ -82,20 +82,16 @@ create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers, const auto& pcap_cfg = config.du_high_cfg.config.pcaps; pcaps.f1ap = pcap_cfg.f1ap.enabled ? create_f1ap_pcap(pcap_cfg.f1ap.filename, workers.get_executor("pcap_exec")) : create_null_dlt_pcap(); - pcaps.f1ap_sig_handler = std::make_unique([&pcaps]() { pcaps.f1ap->flush(); }); - signal_source.attach(pcaps.f1ap_sig_handler.get()); + pcaps.f1ap_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.f1ap->flush(); }); pcaps.f1u = pcap_cfg.f1u.enabled ? create_gtpu_pcap(pcap_cfg.f1u.filename, workers.get_executor("f1u_pcap_exec")) : create_null_dlt_pcap(); - pcaps.f1u_sig_handler = std::make_unique([&pcaps]() { pcaps.f1u->flush(); }); - signal_source.attach(pcaps.f1u_sig_handler.get()); + pcaps.f1u_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.f1u->flush(); }); pcaps.e2ap = config.e2_cfg.pcaps.enabled ? create_e2ap_pcap(config.e2_cfg.pcaps.filename, workers.get_executor("pcap_exec")) : create_null_dlt_pcap(); - pcaps.e2ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e2ap->flush(); }); - signal_source.attach(pcaps.e2ap_sig_handler.get()); - + pcaps.e2ap_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.e2ap->flush(); }); if (pcap_cfg.mac.type != "dlt" && pcap_cfg.mac.type != "udp") { report_error("Invalid type for MAC PCAP. type={}\n", pcap_cfg.mac.type); } @@ -104,8 +100,7 @@ create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers, pcap_cfg.mac.type == "dlt" ? mac_pcap_type::dlt : mac_pcap_type::udp, workers.get_executor("mac_pcap_exec")) : create_null_mac_pcap(); - pcaps.mac_sig_handler = std::make_unique([&pcaps]() { pcaps.mac->flush(); }); - signal_source.attach(pcaps.mac_sig_handler.get()); + pcaps.mac_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.mac->flush(); }); if (pcap_cfg.rlc.rb_type != "all" && pcap_cfg.rlc.rb_type != "srb" && pcap_cfg.rlc.rb_type != "drb") { report_error("Invalid rb_type for RLC PCAP. rb_type={}\n", pcap_cfg.rlc.rb_type); @@ -116,8 +111,7 @@ create_o_du_pcaps(const o_du_high_unit_config& config, worker_manager& workers, pcap_cfg.rlc.rb_type != "drb", pcap_cfg.rlc.rb_type != "srb") : create_null_rlc_pcap(); - pcaps.rlc_sig_handler = std::make_unique([&pcaps]() { pcaps.rlc->flush(); }); - signal_source.attach(pcaps.rlc_sig_handler.get()); + pcaps.rlc_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.rlc->flush(); }); return pcaps; } diff --git a/apps/units/o_cu_cp/pcap_factory.h b/apps/units/o_cu_cp/pcap_factory.h index 9d417b599d..e79b7edede 100644 --- a/apps/units/o_cu_cp/pcap_factory.h +++ b/apps/units/o_cu_cp/pcap_factory.h @@ -69,24 +69,20 @@ inline o_cu_cp_dlt_pcaps create_o_cu_cp_dlt_pcap(const o_cu_cp_unit_config& const cu_cp_unit_pcap_config& pcap_cfg = config.cucp_cfg.pcap_cfg; pcaps.ngap = pcap_cfg.ngap.enabled ? create_ngap_pcap(pcap_cfg.ngap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - pcaps.ngap_sig_handler = std::make_unique([&pcaps]() { pcaps.ngap->flush(); }); - signal_source.attach(pcaps.ngap_sig_handler.get()); + pcaps.ngap_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.ngap->flush(); }); pcaps.f1ap = pcap_cfg.f1ap.enabled ? create_f1ap_pcap(pcap_cfg.f1ap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - pcaps.f1ap_sig_handler = std::make_unique([&pcaps]() { pcaps.f1ap->flush(); }); - signal_source.attach(pcaps.f1ap_sig_handler.get()); + pcaps.f1ap_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.f1ap->flush(); }); pcaps.e1ap = pcap_cfg.e1ap.enabled ? create_e1ap_pcap(pcap_cfg.e1ap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - pcaps.e1ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e1ap->flush(); }); - signal_source.attach(pcaps.e1ap_sig_handler.get()); + pcaps.e1ap_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.e1ap->flush(); }); pcaps.e2ap = config.e2_cfg.pcaps.enabled ? create_e2ap_pcap(config.e2_cfg.pcaps.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - pcaps.e2ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e2ap->flush(); }); - signal_source.attach(pcaps.e2ap_sig_handler.get()); + pcaps.e2ap_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.e2ap->flush(); }); return pcaps; } diff --git a/apps/units/o_cu_up/pcap_factory.h b/apps/units/o_cu_up/pcap_factory.h index 83d66f4b8d..7a71fa65b2 100644 --- a/apps/units/o_cu_up/pcap_factory.h +++ b/apps/units/o_cu_up/pcap_factory.h @@ -67,24 +67,20 @@ inline o_cu_up_dlt_pcaps create_o_cu_up_dlt_pcaps(const o_cu_up_unit_config& const auto& cu_pcaps = unit_cfg.cu_up_cfg.pcap_cfg; pcaps.n3 = cu_pcaps.n3.enabled ? create_gtpu_pcap(cu_pcaps.n3.filename, exec_getter.get_executor("n3_pcap_exec")) : create_null_dlt_pcap(); - pcaps.n3_sig_handler = std::make_unique([&pcaps]() { pcaps.n3->flush(); }); - signal_source.attach(pcaps.n3_sig_handler.get()); + pcaps.n3_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.n3->flush(); }); pcaps.f1u = cu_pcaps.f1u.enabled ? create_gtpu_pcap(cu_pcaps.f1u.filename, exec_getter.get_executor("f1u_pcap_exec")) : create_null_dlt_pcap(); - pcaps.f1u_sig_handler = std::make_unique([&pcaps]() { pcaps.f1u->flush(); }); - signal_source.attach(pcaps.f1u_sig_handler.get()); + pcaps.f1u_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.f1u->flush(); }); pcaps.e1ap = cu_pcaps.e1ap.enabled ? create_e1ap_pcap(cu_pcaps.e1ap.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - pcaps.e1ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e1ap->flush(); }); - signal_source.attach(pcaps.e1ap_sig_handler.get()); + pcaps.e1ap_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.e1ap->flush(); }); pcaps.e2ap = unit_cfg.e2_cfg.pcaps.enabled ? create_e2ap_pcap(unit_cfg.e2_cfg.pcaps.filename, exec_getter.get_executor("pcap_exec")) : create_null_dlt_pcap(); - pcaps.e2ap_sig_handler = std::make_unique([&pcaps]() { pcaps.e2ap->flush(); }); - signal_source.attach(pcaps.e2ap_sig_handler.get()); + pcaps.e2ap_sig_handler = std::make_unique(signal_source, [&pcaps]() { pcaps.e2ap->flush(); }); return pcaps; } diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h index a174bebfb9..aaea8228f6 100644 --- a/include/srsran/support/signal_observer.h +++ b/include/srsran/support/signal_observer.h @@ -17,44 +17,67 @@ namespace srsran { class signal_observer; +/// This class is a dispatcher for [posix] signals (e.g. SIGTERM,...) to which the signal observer can attach/detach in +/// order to subscribe for signal events. +/// +/// The signal dispatcher must outlive all its subscribers, because the signal observer automatically unregister from +/// the signal dispatcher upon destruction. class signal_dispatcher { public: + /// \brief Attach a new observer to this signal dispatcher. + /// \param observer The observer that subscribes for receiving signals. void attach(signal_observer* observer); + /// \brief Detach an observer, i.e. end its subscription for receiving signals. + /// \param observer The observer that unsubscribers from receiving signals. void detach(signal_observer* observer); + /// \brief Notifies all its attached observers (i.e. subscribers) of the given signal. This function is to be called + /// from the actual signal source. + /// \param signal The signal that occured. void notify_signal(int signal); private: + /// Collection of subscribers attached to this dispatcher. std::vector observers; }; using signal_callback = std::function; +/// This class is an observer that may be used to subscribe for [posix] signals (e.g. SIGTERM,...) from a signal +/// dispatcher. The dispatcher calls handle_signal to notify the observer of a signal event; the observer then invokes +/// the callback that was set upon construction. +/// +/// Upon destruction, the observer automatically unsubscribes from the signal dispatcher. class signal_observer { public: - explicit signal_observer(signal_callback callback_) : callback(std::move(callback_)) {} - - virtual ~signal_observer() + /// \brief Creates a signal observer with a given callback and attaches the observer to a signal dispatcher. The + /// signal dispatcher must outlive the observer due to automatic detach on destruction of the observer. + /// \param dispatcher_ The signal dispatcher to which this observer shall be attached. + /// \param callback_ The callback to be invoked on a signal event. + explicit signal_observer(signal_dispatcher& dispatcher_, signal_callback callback_) : + dispatcher(dispatcher_), callback(std::move(callback_)) { - if (current_subject) { - current_subject->detach(this); - } + dispatcher.attach(this); } - void set_current_subject(signal_dispatcher* subject) { current_subject = subject; } + /// \brief Automatically unsubscribe from the current_subject (i.e. the signal dispatcher). + virtual ~signal_observer() { dispatcher.detach(this); } + /// \brief Handles a signal that was dispatched by the signal_dispatcher. + /// \param signal The signal that occured. void handle_signal(int signal) { callback(); }; private: - signal_callback callback; - signal_dispatcher* current_subject = nullptr; + /// The signal dispatcher to which this observer is attached. + signal_dispatcher& dispatcher; + /// The callback to be invoked on a signal event. + signal_callback callback; }; void signal_dispatcher::attach(signal_observer* observer) { observers.push_back(observer); - observer->set_current_subject(this); } void signal_dispatcher::detach(signal_observer* observer) @@ -66,7 +89,6 @@ void signal_dispatcher::detach(signal_observer* observer) } ++it; } - observer->set_current_subject(nullptr); } void signal_dispatcher::notify_signal(int signal) From 7de19c2f3d2c9d8d0f290699b6428cdfabdbe324 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 20 Dec 2024 13:48:40 +0100 Subject: [PATCH 034/107] support: signal_dispatcher attach/detach reference instead of pointer --- include/srsran/support/signal_observer.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h index aaea8228f6..16ce9d86f8 100644 --- a/include/srsran/support/signal_observer.h +++ b/include/srsran/support/signal_observer.h @@ -27,10 +27,10 @@ class signal_dispatcher public: /// \brief Attach a new observer to this signal dispatcher. /// \param observer The observer that subscribes for receiving signals. - void attach(signal_observer* observer); + void attach(signal_observer& observer); /// \brief Detach an observer, i.e. end its subscription for receiving signals. /// \param observer The observer that unsubscribers from receiving signals. - void detach(signal_observer* observer); + void detach(signal_observer& observer); /// \brief Notifies all its attached observers (i.e. subscribers) of the given signal. This function is to be called /// from the actual signal source. /// \param signal The signal that occured. @@ -58,11 +58,11 @@ class signal_observer explicit signal_observer(signal_dispatcher& dispatcher_, signal_callback callback_) : dispatcher(dispatcher_), callback(std::move(callback_)) { - dispatcher.attach(this); + dispatcher.attach(*this); } /// \brief Automatically unsubscribe from the current_subject (i.e. the signal dispatcher). - virtual ~signal_observer() { dispatcher.detach(this); } + virtual ~signal_observer() { dispatcher.detach(*this); } /// \brief Handles a signal that was dispatched by the signal_dispatcher. /// \param signal The signal that occured. @@ -75,15 +75,15 @@ class signal_observer signal_callback callback; }; -void signal_dispatcher::attach(signal_observer* observer) +void signal_dispatcher::attach(signal_observer& observer) { - observers.push_back(observer); + observers.push_back(&observer); } -void signal_dispatcher::detach(signal_observer* observer) +void signal_dispatcher::detach(signal_observer& observer) { for (auto it = observers.begin(); it != observers.end();) { - if (*it == observer) { + if (*it == &observer) { it = observers.erase(it); continue; } From 988cfb0b4d9ee20b0790bbfae02786fddcc6b265 Mon Sep 17 00:00:00 2001 From: Robert Falkenberg Date: Fri, 20 Dec 2024 13:57:15 +0100 Subject: [PATCH 035/107] support: unique observers allow for early return on detach. --- include/srsran/support/signal_observer.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/srsran/support/signal_observer.h b/include/srsran/support/signal_observer.h index 16ce9d86f8..7ee0cb77ba 100644 --- a/include/srsran/support/signal_observer.h +++ b/include/srsran/support/signal_observer.h @@ -85,7 +85,7 @@ void signal_dispatcher::detach(signal_observer& observer) for (auto it = observers.begin(); it != observers.end();) { if (*it == &observer) { it = observers.erase(it); - continue; + return; // Finish here because observers are unique and attach only once upon their construction } ++it; } From 093e28d10252f66a44626f69ccf60594e5f8a749 Mon Sep 17 00:00:00 2001 From: faluco Date: Thu, 19 Dec 2024 11:30:39 +0100 Subject: [PATCH 036/107] [SRSLOG] Use a lockfree MPMC queue for the backend and for the arg store pool --- .../detail/support/dyn_arg_store_pool.h | 20 +++---- .../srsran/srslog/detail/support/work_queue.h | 58 +++++-------------- 2 files changed, 21 insertions(+), 57 deletions(-) diff --git a/include/srsran/srslog/detail/support/dyn_arg_store_pool.h b/include/srsran/srslog/detail/support/dyn_arg_store_pool.h index 9c3ce5200f..1225644c8b 100644 --- a/include/srsran/srslog/detail/support/dyn_arg_store_pool.h +++ b/include/srsran/srslog/detail/support/dyn_arg_store_pool.h @@ -10,8 +10,8 @@ #pragma once +#include "rigtorp/MPMCQueue.h" #include "srsran/srslog/detail/support/backend_capacity.h" -#include "fmt/core.h" namespace srslog { @@ -23,29 +23,27 @@ namespace detail { class dyn_arg_store_pool { public: - dyn_arg_store_pool() + dyn_arg_store_pool() : free_list(SRSLOG_QUEUE_CAPACITY) { pool.resize(SRSLOG_QUEUE_CAPACITY); for (auto& elem : pool) { // Reserve for 10 normal and 2 named arguments. elem.reserve(10, 2); } - free_list.reserve(SRSLOG_QUEUE_CAPACITY); for (auto& elem : pool) { - free_list.push_back(&elem); + free_list.push(&elem); } } /// Returns a pointer to a free dyn arg store object, otherwise returns nullptr. fmt::dynamic_format_arg_store* alloc() { - scoped_lock lock(m); if (free_list.empty()) { return nullptr; } - auto* p = free_list.back(); - free_list.pop_back(); + fmt::dynamic_format_arg_store* p = nullptr; + free_list.try_pop(p); return p; } @@ -58,14 +56,12 @@ class dyn_arg_store_pool } p->clear(); - scoped_lock lock(m); - free_list.push_back(p); + free_list.push(p); } private: - std::vector> pool; - std::vector*> free_list; - mutable mutex m; + std::vector> pool; + rigtorp::MPMCQueue*> free_list; }; } // namespace detail diff --git a/include/srsran/srslog/detail/support/work_queue.h b/include/srsran/srslog/detail/support/work_queue.h index 5f8ce57257..6075370e76 100644 --- a/include/srsran/srslog/detail/support/work_queue.h +++ b/include/srsran/srslog/detail/support/work_queue.h @@ -10,9 +10,8 @@ #pragma once -#include "srsran/adt/ring_buffer.h" +#include "rigtorp/MPMCQueue.h" #include "srsran/srslog/detail/support/backend_capacity.h" -#include "srsran/srslog/detail/support/thread_utils.h" namespace srslog { @@ -24,8 +23,7 @@ namespace detail { template class work_queue { - srsran::ring_buffer queue; - mutable mutex m; + rigtorp::MPMCQueue queue; static constexpr size_t threshold = capacity * 0.98; public: @@ -36,54 +34,21 @@ class work_queue /// Inserts a new element into the back of the queue. Returns false when the /// queue is full, otherwise true. - bool push(const T& value) - { - m.lock(); - // Discard the new element if we reach the maximum capacity. - if (queue.full()) { - m.unlock(); - return false; - } - queue.push(value); - m.unlock(); - - return true; - } + bool push(const T& value) { return queue.try_push(value); } /// Inserts a new element into the back of the queue. Returns false when the /// queue is full, otherwise true. - bool push(T&& value) - { - m.lock(); - // Discard the new element if we reach the maximum capacity. - if (queue.full()) { - m.unlock(); - return false; - } - queue.push(std::move(value)); - m.unlock(); - - return true; - } + bool push(T&& value) { return queue.try_push(std::move(value)); } /// Extracts the top most element from the queue if it exists. /// Returns a pair with a bool indicating if the pop has been successful. std::pair try_pop() { - m.lock(); - - if (queue.empty()) { - m.unlock(); + T item; + if (!queue.try_pop(item)) { return {false, T()}; } - - // Here we have been woken up normally. - T Item = std::move(queue.top()); - queue.pop(); - - m.unlock(); - - return {true, std::move(Item)}; + return {true, std::move(item)}; } /// Capacity of the queue. @@ -92,9 +57,12 @@ class work_queue /// Returns true when the queue is almost full, otherwise returns false. bool is_almost_full() const { - scoped_lock lock(m); - - return queue.size() > threshold; + auto s = queue.size(); + // size() may return a negative value for an empty queue. + if (s < 0) { + return false; + } + return static_cast(s) > threshold; } }; From 867955052f011c76f1a0b5d7fdd0ef69bd53c232 Mon Sep 17 00:00:00 2001 From: Alejandro Leal Date: Thu, 19 Dec 2024 13:24:21 +0100 Subject: [PATCH 037/107] phy: changed the interface for the resource_grid_pool so it only needs the slot point to allocate a resource grid. --- apps/examples/phy/upper_phy_ssb_example.cpp | 12 +++++------- include/srsran/phy/support/resource_grid_pool.h | 4 ++-- lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp | 10 ++-------- lib/phy/support/resource_grid_pool_impl.cpp | 7 ++----- lib/phy/support/resource_grid_pool_impl.h | 2 +- tests/integrationtests/ofh/ofh_integration_test.cpp | 4 ++-- .../fapi_adaptor/phy/fapi_to_phy_translator_test.cpp | 2 +- .../phy/support/resource_grid_pool_test.cpp | 7 +------ 8 files changed, 16 insertions(+), 32 deletions(-) diff --git a/apps/examples/phy/upper_phy_ssb_example.cpp b/apps/examples/phy/upper_phy_ssb_example.cpp index ee151c2b69..dbf26e7f52 100644 --- a/apps/examples/phy/upper_phy_ssb_example.cpp +++ b/apps/examples/phy/upper_phy_ssb_example.cpp @@ -126,7 +126,7 @@ class upper_phy_example_sw : public upper_phy_ssb_example rx_symb_context.slot = context.slot; // Try to allocate a resource grid. - shared_resource_grid rg = ul_rg_pool->allocate_resource_grid(rx_symb_context); + shared_resource_grid rg = ul_rg_pool->allocate_resource_grid(context.slot); // If the resource grid allocation fails, it aborts the slot request. if (rg) { @@ -155,13 +155,8 @@ class upper_phy_example_sw : public upper_phy_ssb_example rx_symb_req_notifier->on_prach_capture_request(prach_context, *prach_buf); } - // Prepare resource grid context. - resource_grid_context rg_context; - rg_context.sector = 0; - rg_context.slot = context.slot; - // Get a resource grid from the pool. - shared_resource_grid rg = dl_rg_pool->allocate_resource_grid(rg_context); + shared_resource_grid rg = dl_rg_pool->allocate_resource_grid(context.slot); // Abort slot processing if the grid is not valid. if (!rg) { @@ -237,6 +232,9 @@ class upper_phy_example_sw : public upper_phy_ssb_example mapper->map(rg.get_writer(), data_symbols, grid_allocation, precoding_config); } + resource_grid_context rg_context; + rg_context.sector = 0; + rg_context.slot = context.slot; gateway->send(rg_context, std::move(rg)); // Raise TTI boundary and notify. diff --git a/include/srsran/phy/support/resource_grid_pool.h b/include/srsran/phy/support/resource_grid_pool.h index fe1fbdb938..dac18b89db 100644 --- a/include/srsran/phy/support/resource_grid_pool.h +++ b/include/srsran/phy/support/resource_grid_pool.h @@ -30,9 +30,9 @@ class resource_grid_pool /// the allocation might fail. When allocation fails, the reason for the failure is logged to the \e PHY logger /// channel. /// - /// \param [in] context The context for resource grid allocation. + /// \param [in] slot The slot for resource grid allocation. /// \return A valid shared resource grid if the allocation is successful; otherwise, an invalid shared resource grid. - virtual shared_resource_grid allocate_resource_grid(const resource_grid_context& context) = 0; + virtual shared_resource_grid allocate_resource_grid(slot_point slot) = 0; }; } // namespace srsran diff --git a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp index cb79232ebb..5bbe8afdf1 100644 --- a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp +++ b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp @@ -118,10 +118,7 @@ fapi_to_phy_translator::slot_based_upper_phy_controller::slot_based_upper_phy_co { resource_grid_context context = {slot_, sector_id}; // Grab the resource grid. - // FIXME: 0 is hardcoded as the sector as in this implementation there is one DU per sector, so each DU have its own - // resource grid pool and downlink processor pool. It is also in the previous get processor call of the downlink - // processor pool - shared_resource_grid grid = rg_pool.allocate_resource_grid({slot_, 0}); + shared_resource_grid grid = rg_pool.allocate_resource_grid(slot_); // If the resource grid is not valid, all DL transmissions for this slot shall be discarded. if (!grid) { @@ -532,10 +529,7 @@ void fapi_to_phy_translator::ul_tti_request(const fapi::ul_tti_request_message& rg_context.slot = slot; rg_context.sector = sector_id; - // Get ul_resource_grid. - resource_grid_context pool_context = rg_context; - pool_context.sector = 0; - shared_resource_grid ul_rg = ul_rg_pool.allocate_resource_grid(pool_context); + shared_resource_grid ul_rg = ul_rg_pool.allocate_resource_grid(slot); // Abort UL processing for this slot if the resource grid is not available. if (!ul_rg) { diff --git a/lib/phy/support/resource_grid_pool_impl.cpp b/lib/phy/support/resource_grid_pool_impl.cpp index f2223173ff..ff8bf4b9d6 100644 --- a/lib/phy/support/resource_grid_pool_impl.cpp +++ b/lib/phy/support/resource_grid_pool_impl.cpp @@ -46,7 +46,7 @@ resource_grid_pool_impl::~resource_grid_pool_impl() "Not all the resource grids have returned to the pool."); } -shared_resource_grid resource_grid_pool_impl::allocate_resource_grid(const resource_grid_context& context) +shared_resource_grid resource_grid_pool_impl::allocate_resource_grid(slot_point slot) { // Trace point for grid reservation. trace_point tp = l1_tracer.now(); @@ -66,10 +66,7 @@ shared_resource_grid resource_grid_pool_impl::allocate_resource_grid(const resou // Return an invalid grid if not available. if (!available) { - logger.warning(context.slot.sfn(), - context.slot.slot_index(), - "Resource grid with identifier {} is not available.", - identifier); + logger.warning(slot.sfn(), slot.slot_index(), "Resource grid with identifier {} is not available.", identifier); return {}; } diff --git a/lib/phy/support/resource_grid_pool_impl.h b/lib/phy/support/resource_grid_pool_impl.h index b5267e5da9..def38cf3a9 100644 --- a/lib/phy/support/resource_grid_pool_impl.h +++ b/lib/phy/support/resource_grid_pool_impl.h @@ -46,7 +46,7 @@ class resource_grid_pool_impl : public resource_grid_pool, private shared_resour ~resource_grid_pool_impl() override; // See resource_grid_pool interface for documentation. - shared_resource_grid allocate_resource_grid(const resource_grid_context& context) override; + shared_resource_grid allocate_resource_grid(slot_point slot) override; private: /// Reference counter value to indicate the availability of a resource grid. diff --git a/tests/integrationtests/ofh/ofh_integration_test.cpp b/tests/integrationtests/ofh/ofh_integration_test.cpp index c6192d2f90..7e4ef38a5a 100644 --- a/tests/integrationtests/ofh/ofh_integration_test.cpp +++ b/tests/integrationtests/ofh/ofh_integration_test.cpp @@ -689,7 +689,7 @@ class test_du_emulator resource_grid_context context{slot, 0}; shared_resource_grid dl_grid; while (!dl_grid && alloc_attempts--) { - dl_grid = dl_rg_pool.allocate_resource_grid(context); + dl_grid = dl_rg_pool.allocate_resource_grid(slot); if (!dl_grid) { std::this_thread::sleep_for(std::chrono::microseconds(10)); } @@ -709,7 +709,7 @@ class test_du_emulator resource_grid_context context{slot, 0}; shared_resource_grid ul_grid; while (!ul_grid && alloc_attempts--) { - ul_grid = ul_rg_pool.allocate_resource_grid(context); + ul_grid = ul_rg_pool.allocate_resource_grid(slot); if (!ul_grid) { std::this_thread::sleep_for(std::chrono::microseconds(10)); } diff --git a/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp b/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp index 23b83557f9..e79e4a59e3 100644 --- a/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp +++ b/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp @@ -94,7 +94,7 @@ class resource_grid_pool_dummy : public resource_grid_pool, private shared_resou public: explicit resource_grid_pool_dummy() {} - shared_resource_grid allocate_resource_grid(const resource_grid_context& context) override + shared_resource_grid allocate_resource_grid(slot_point slot) override { unsigned expected_available_ref_count = 0; bool available = ref_count.compare_exchange_strong(expected_available_ref_count, 1); diff --git a/tests/unittests/phy/support/resource_grid_pool_test.cpp b/tests/unittests/phy/support/resource_grid_pool_test.cpp index d58b95a937..94e62dfe0c 100644 --- a/tests/unittests/phy/support/resource_grid_pool_test.cpp +++ b/tests/unittests/phy/support/resource_grid_pool_test.cpp @@ -34,13 +34,8 @@ void test(unsigned nof_slots) // Iterate all parameters and assert grid reference std::vector reserved_grids; for (unsigned slot = 0; slot != nof_slots; ++slot) { - // Create context - resource_grid_context context = {}; - context.slot = {0, slot}; - context.sector = 0; - // Get grid. - shared_resource_grid grid = pool->allocate_resource_grid(context); + shared_resource_grid grid = pool->allocate_resource_grid({0, slot}); TESTASSERT(grid.is_valid()); // Verify grid reference match From af37f4bfb1c0aa5ab45856417916e4048253001f Mon Sep 17 00:00:00 2001 From: qarlosalberto Date: Mon, 23 Dec 2024 19:11:37 +0100 Subject: [PATCH 038/107] ci: add dummy test with 16 cells and 50 ues --- .gitlab/ci/e2e/retina_request_viavi.yml | 4 +- tests/e2e/tests/test_mode.py | 37 +++++++++ .../tests/test_mode/config_ru_16cell_50ue.yml | 76 +++++++++++++++++++ 3 files changed, 115 insertions(+), 2 deletions(-) create mode 100644 tests/e2e/tests/test_mode/config_ru_16cell_50ue.yml diff --git a/.gitlab/ci/e2e/retina_request_viavi.yml b/.gitlab/ci/e2e/retina_request_viavi.yml index a4cc08df76..98e7a37eb6 100644 --- a/.gitlab/ci/e2e/retina_request_viavi.yml +++ b/.gitlab/ci/e2e/retina_request_viavi.yml @@ -23,8 +23,8 @@ requests: 2Gi limits: 2Gi ephemeral-storage: - requests: "50G" - limits: "50G" + requests: "150G" + limits: "150G" resources: - type: emulator model: viavi diff --git a/tests/e2e/tests/test_mode.py b/tests/e2e/tests/test_mode.py index 426dba1054..acad668ee2 100644 --- a/tests/e2e/tests/test_mode.py +++ b/tests/e2e/tests/test_mode.py @@ -178,6 +178,24 @@ def test_ru( _test_ru(retina_manager, retina_data, gnb, ru_config="config_ru.yml") +@mark.test_mode +@mark.flaky( + reruns=2, + only_rerun=[_POD_ERROR], +) +def test_ru_16cell_50ue( + # Retina + retina_manager: RetinaTestManager, + retina_data: RetinaTestData, + # Clients + gnb: GNBStub, +): + """ + Run gnb in test mode ru dummy. + """ + _test_ru(retina_manager, retina_data, gnb, ru_config="config_ru_16cell_50ue.yml", warning_as_errors=False) + + @mark.test_mode_not_crash @mark.flaky( reruns=2, @@ -205,6 +223,25 @@ def test_ru_not_crash( ) +@mark.test_mode_not_crash +@mark.flaky( + reruns=2, + only_rerun=[_POD_ERROR], +) +def test_ru_16cell_50ue_not_crash( + # Retina + retina_manager: RetinaTestManager, + retina_data: RetinaTestData, + # Clients + gnb: GNBStub, +): + """ + Run gnb with sanitizers in test mode ru dummy. + It ignores warnings and KOs, so it will fail if the gnb+sanitizer fails + """ + _test_ru(retina_manager, retina_data, gnb, ru_config="config_ru_16cell_50ue.yml") + + # pylint: disable=too-many-arguments,too-many-positional-arguments def _test_ru( # Retina diff --git a/tests/e2e/tests/test_mode/config_ru_16cell_50ue.yml b/tests/e2e/tests/test_mode/config_ru_16cell_50ue.yml new file mode 100644 index 0000000000..6eb9187d96 --- /dev/null +++ b/tests/e2e/tests/test_mode/config_ru_16cell_50ue.yml @@ -0,0 +1,76 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the distribution. +# + +cu_up: + warn_on_drop: False + +buffer_pool: + nof_segments: 1048576 + +cell_cfg: + dl_arfcn: 437000 + band: 256 + channel_bandwidth_MHz: 5 + common_scs: 15 + tac: 7 + pucch: + pucch_resource_common: 11 + sr_period_ms: 80 + use_format_0: true + nof_ue_res_harq_per_set: 8 + f0_or_f1_nof_cell_res_sr: 2 + f0_intraslot_freq_hop: false + f1_enable_occ: true + f1_nof_cyclic_shifts: 12 + ul_common: + max_pucchs_per_slot: 31 + max_ul_grants_per_slot: 32 + ssb: + ssb_period: 20 + pusch: + enable_transform_precoding: true + +cells: + - pci: 1 + - pci: 2 + - pci: 3 + - pci: 4 + - pci: 5 + - pci: 6 + - pci: 7 + - pci: 8 + - pci: 9 + - pci: 10 + - pci: 11 + - pci: 12 + - pci: 13 + - pci: 14 + - pci: 15 + - pci: 16 + +ru_dummy: + time_scaling: 10 + +test_mode: + test_ue: + rnti: 0x1234 + ri: 1 + cqi: 15 + pdsch_active: true + pusch_active: true + nof_ues: 50 + +expert_execution: + threads: + upper_phy: + nof_dl_threads: 2 + nof_ul_threads: 1 + nof_pusch_decoder_threads: 2 + +expert_phy: + max_proc_delay: 6 \ No newline at end of file From 7ec794c460b86a4973b9bce9d2b6111b90c62f27 Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 20 Dec 2024 11:49:44 +0100 Subject: [PATCH 039/107] sched: avoid allocating CEs in slice with no pending bytes when there is already a slice with pending bytes for the same UE --- lib/scheduler/slicing/slice_ue_repository.cpp | 30 ++++++++++++++++--- 1 file changed, 26 insertions(+), 4 deletions(-) diff --git a/lib/scheduler/slicing/slice_ue_repository.cpp b/lib/scheduler/slicing/slice_ue_repository.cpp index 8abca711ec..2fd59d7364 100644 --- a/lib/scheduler/slicing/slice_ue_repository.cpp +++ b/lib/scheduler/slicing/slice_ue_repository.cpp @@ -52,25 +52,47 @@ void slice_ue::rem_logical_channel(lcid_t lcid) bool slice_ue::has_pending_dl_newtx_bytes() const { - if (u.has_pending_ce_bytes()) { - return true; - } + // If at least one slice bearer has pending data, return true. for (unsigned lcid = 0, e = bearers.size(); lcid != e; ++lcid) { if (bearers.test(lcid) and u.has_pending_dl_newtx_bytes(uint_to_lcid(lcid))) { return true; } } + + // In case of SRB slice and there are pending CE bytes and no pending newtx data + // Note: We only report pending newtx bytes when there are pending CEs in the case of SRB slice and if there are no + // pending data in other slices. Otherwise, we let other slices allocate the CEs. + if (bearers.size() > 1 and bearers.test(LCID_SRB1) and u.has_pending_ce_bytes()) { + // Check if there aren't pending bytes in other slices. + return not u.has_pending_dl_newtx_bytes(); + } + return false; } unsigned slice_ue::pending_dl_newtx_bytes() const { - unsigned pending_bytes = u.pending_ce_bytes(); + // Compute pending bytes in LC CHs. + unsigned pending_bytes = 0; for (unsigned lcid = 0, e = bearers.size(); lcid != e; ++lcid) { if (bearers.test(lcid)) { pending_bytes += u.pending_dl_newtx_bytes(uint_to_lcid(lcid)); } } + + unsigned pending_ce_bytes = u.pending_ce_bytes(); + if (pending_ce_bytes > 0) { + // In case the slice UE has pending bytes, we also include the CE bytes. + if (pending_bytes > 0) { + pending_bytes += pending_ce_bytes; + } else if (bearers.size() > 1 and bearers.test(LCID_SRB1)) { + // In case of SRB slice, we also include the CE bytes if the UE has no pending data in other slices. + if (not u.has_pending_dl_newtx_bytes()) { + pending_bytes += pending_ce_bytes; + } + } + } + return pending_bytes; } From 337f45a85d64586ad245d9d0aef93f5292ab004c Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 26 Dec 2024 01:31:02 +0100 Subject: [PATCH 040/107] sched: fix computation of pending bytes for a subset of logical channels --- lib/scheduler/slicing/slice_ue_repository.cpp | 40 +------ .../ue_context/dl_logical_channel_manager.cpp | 108 ++++++++++++++++++ .../ue_context/dl_logical_channel_manager.h | 46 +++----- lib/scheduler/ue_context/ue.cpp | 14 +-- lib/scheduler/ue_context/ue.h | 22 +++- .../ue_scheduling/fallback_scheduler_test.cpp | 11 +- 6 files changed, 152 insertions(+), 89 deletions(-) diff --git a/lib/scheduler/slicing/slice_ue_repository.cpp b/lib/scheduler/slicing/slice_ue_repository.cpp index 2fd59d7364..93fbe7a2eb 100644 --- a/lib/scheduler/slicing/slice_ue_repository.cpp +++ b/lib/scheduler/slicing/slice_ue_repository.cpp @@ -52,48 +52,12 @@ void slice_ue::rem_logical_channel(lcid_t lcid) bool slice_ue::has_pending_dl_newtx_bytes() const { - // If at least one slice bearer has pending data, return true. - for (unsigned lcid = 0, e = bearers.size(); lcid != e; ++lcid) { - if (bearers.test(lcid) and u.has_pending_dl_newtx_bytes(uint_to_lcid(lcid))) { - return true; - } - } - - // In case of SRB slice and there are pending CE bytes and no pending newtx data - // Note: We only report pending newtx bytes when there are pending CEs in the case of SRB slice and if there are no - // pending data in other slices. Otherwise, we let other slices allocate the CEs. - if (bearers.size() > 1 and bearers.test(LCID_SRB1) and u.has_pending_ce_bytes()) { - // Check if there aren't pending bytes in other slices. - return not u.has_pending_dl_newtx_bytes(); - } - - return false; + return u.has_pending_dl_newtx_bytes(bearers); } unsigned slice_ue::pending_dl_newtx_bytes() const { - // Compute pending bytes in LC CHs. - unsigned pending_bytes = 0; - for (unsigned lcid = 0, e = bearers.size(); lcid != e; ++lcid) { - if (bearers.test(lcid)) { - pending_bytes += u.pending_dl_newtx_bytes(uint_to_lcid(lcid)); - } - } - - unsigned pending_ce_bytes = u.pending_ce_bytes(); - if (pending_ce_bytes > 0) { - // In case the slice UE has pending bytes, we also include the CE bytes. - if (pending_bytes > 0) { - pending_bytes += pending_ce_bytes; - } else if (bearers.size() > 1 and bearers.test(LCID_SRB1)) { - // In case of SRB slice, we also include the CE bytes if the UE has no pending data in other slices. - if (not u.has_pending_dl_newtx_bytes()) { - pending_bytes += pending_ce_bytes; - } - } - } - - return pending_bytes; + return u.pending_dl_newtx_bytes(bearers); } unsigned slice_ue::pending_ul_newtx_bytes() const diff --git a/lib/scheduler/ue_context/dl_logical_channel_manager.cpp b/lib/scheduler/ue_context/dl_logical_channel_manager.cpp index 382493655d..8697418aa8 100644 --- a/lib/scheduler/ue_context/dl_logical_channel_manager.cpp +++ b/lib/scheduler/ue_context/dl_logical_channel_manager.cpp @@ -61,6 +61,16 @@ void dl_logical_channel_manager::deactivate() sorted_channels.clear(); } +void dl_logical_channel_manager::set_fallback_state(bool enter_fallback) +{ + if (fallback_state == enter_fallback) { + // no-op. + return; + } + + fallback_state = enter_fallback; +} + void dl_logical_channel_manager::set_status(lcid_t lcid, bool active) { srsran_sanity_check(lcid < MAX_NOF_RB_LCIDS, "Max LCID value 32 exceeded"); @@ -139,6 +149,104 @@ void dl_logical_channel_manager::configure(span lo }); } +bool dl_logical_channel_manager::has_pending_bytes(const bounded_bitset& bearers) const +{ + if (fallback_state) { + bool srb0_chosen = bearers.size() > 0 and bearers.test(LCID_SRB0); + bool srb1_chosen = bearers.size() > 1 and bearers.test(LCID_SRB1); + if (not srb0_chosen and not srb1_chosen) { + return false; + } + return is_con_res_id_pending() or (srb0_chosen and has_pending_bytes(LCID_SRB0)) or + (srb1_chosen and has_pending_bytes(LCID_SRB1)); + } + + // If at least one slice bearer has pending data, return true. + for (lcid_t lcid : sorted_channels) { + if (lcid < bearers.size() and bearers.test(lcid) and has_pending_bytes(lcid)) { + return true; + } + } + + // In case SRB1 was selected (but with no data) and there are pending CE bytes. + if (bearers.size() > 1 and bearers.test(LCID_SRB1) and has_pending_ces()) { + // Check if any other bearer has pending data. If they do, CE is not considered. + // Note: This extra check is to avoid multiple slices report pending data. + for (lcid_t lcid : sorted_channels) { + if (lcid > LCID_SRB1 and has_pending_bytes(lcid)) { + return false; + } + } + return true; + } + + return false; +} + +unsigned dl_logical_channel_manager::total_pending_bytes() const +{ + unsigned bytes = pending_ce_bytes(); + for (lcid_t lcid : sorted_channels) { + bytes += pending_bytes(lcid); + } + return bytes; +} + +unsigned dl_logical_channel_manager::pending_bytes() const +{ + if (fallback_state) { + return pending_con_res_ce_bytes() + pending_bytes(LCID_SRB0) + pending_bytes(LCID_SRB1); + } + unsigned bytes = pending_ce_bytes(); + for (lcid_t lcid : sorted_channels) { + bytes += lcid != LCID_SRB0 ? pending_bytes(lcid) : 0; + } + return bytes; +} + +unsigned dl_logical_channel_manager::pending_bytes(const bounded_bitset& bearers) const +{ + if (fallback_state) { + bool srb0_chosen = bearers.size() > 0 and bearers.test(LCID_SRB0); + bool srb1_chosen = bearers.size() > 1 and bearers.test(LCID_SRB1); + if (not srb0_chosen and not srb1_chosen) { + return 0; + } + return pending_con_res_ce_bytes() + (srb0_chosen ? pending_bytes(LCID_SRB0) : 0) + + (srb1_chosen ? pending_bytes(LCID_SRB1) : 0); + } + + // Compute pending bytes for the given bearers. + unsigned total_bytes = 0; + for (lcid_t lcid : sorted_channels) { + if (lcid < bearers.size() and bearers.test(lcid)) { + total_bytes += pending_bytes(lcid); + } + } + + unsigned ce_bytes = pending_ce_bytes(); + if (ce_bytes > 0) { + // There are also pending CE bytes. + if (total_bytes > 0) { + // In case the UE has pending bearer bytes, we also include the CE bytes. + total_bytes += ce_bytes; + } else if (bearers.size() > 1 and bearers.test(LCID_SRB1)) { + // In case SRB1 was selected, and there are no pending bytes in the selected bearers, we return the pending CE + // bytes iff the UE has no pending data on the remaining, non-selected bearers. + // This is to avoid the situation where a UE, for instance, has DRB data to transmit, but the CE is allocated in + // the SRB slice instead. + for (lcid_t lcid : sorted_channels) { + if (lcid > LCID_SRB1 and pending_bytes(lcid) > 0) { + return 0; + } + } + return ce_bytes; + } + } + + return total_bytes; +} + void dl_logical_channel_manager::handle_mac_ce_indication(const mac_ce_info& ce) { if (ce.ce_lcid == lcid_dl_sch_t::UE_CON_RES_ID) { diff --git a/lib/scheduler/ue_context/dl_logical_channel_manager.h b/lib/scheduler/ue_context/dl_logical_channel_manager.h index 8303ac8350..cae937e06c 100644 --- a/lib/scheduler/ue_context/dl_logical_channel_manager.h +++ b/lib/scheduler/ue_context/dl_logical_channel_manager.h @@ -35,6 +35,9 @@ class dl_logical_channel_manager /// \brief Deactivate all bearers. void deactivate(); + /// Set UE fallback state. + void set_fallback_state(bool enter_fallback); + /// \brief Activate/Deactivate Bearer. void set_status(lcid_t lcid, bool active); @@ -44,18 +47,10 @@ class dl_logical_channel_manager /// \brief Verifies if logical channel is activated for DL. bool is_active(lcid_t lcid) const { return lcid <= LCID_MAX_DRB and channels[lcid].active; } - /// \brief Checks whether the UE has pending data, regardless of the state it is in. - bool has_pending_bytes() const - { - return has_pending_ces() or std::any_of(sorted_channels.begin(), sorted_channels.end(), [this](lcid_t lcid) { - return channels[lcid].active and channels[lcid].buf_st > 0; - }); - } - /// \brief Check whether the UE has pending data, given its current state. - bool has_pending_bytes(bool fallback_enabled) const + bool has_pending_bytes() const { - if (fallback_enabled) { + if (fallback_state) { return is_con_res_id_pending() or has_pending_bytes(LCID_SRB0) or has_pending_bytes(LCID_SRB1); } return has_pending_ces() or std::any_of(sorted_channels.begin(), sorted_channels.end(), [this](lcid_t lcid) { @@ -63,6 +58,9 @@ class dl_logical_channel_manager }); } + /// \brief Check whether the UE has pending data in the provided bearers, given its current state. + bool has_pending_bytes(const bounded_bitset& bearers) const; + /// \brief Checks whether a logical channel has pending data. bool has_pending_bytes(lcid_t lcid) const { return channels[lcid].active and channels[lcid].buf_st > 0; } @@ -74,27 +72,13 @@ class dl_logical_channel_manager /// \brief Calculates total number of DL bytes, including MAC header overhead, and without taking into account /// the UE state. - unsigned total_pending_bytes() const - { - unsigned bytes = pending_ce_bytes(); - for (lcid_t lcid : sorted_channels) { - bytes += pending_bytes(lcid); - } - return bytes; - } + unsigned total_pending_bytes() const; /// \brief Calculates number of DL pending bytes, including MAC header overhead, and taking UE state into account. - unsigned pending_bytes(bool fallback_enabled) const - { - if (fallback_enabled) { - return pending_con_res_ce_bytes() + pending_bytes(LCID_SRB0) + pending_bytes(LCID_SRB1); - } - unsigned bytes = pending_ce_bytes(); - for (lcid_t lcid : sorted_channels) { - bytes += lcid != LCID_SRB0 ? pending_bytes(lcid) : 0; - } - return bytes; - } + unsigned pending_bytes() const; + + /// Calculates the number of DL pending bytes, including MAC header overhead, for a subset of LCIDs. + unsigned pending_bytes(const bounded_bitset& lcids) const; /// \brief Returns the UE pending CEs' bytes to be scheduled, if any. unsigned pending_ce_bytes() const @@ -178,6 +162,10 @@ class dl_logical_channel_manager // List of logical channel IDs sorted in decreasing order of priority. i.e. first element has the highest priority. std::vector sorted_channels; + // Whether the UE is in fallback (no DRB tx). + bool fallback_state = false; + + // Whether a CON RES CE needs to be sent. bool pending_con_res_id{false}; // List of pending CEs except UE Contention Resolution Identity. diff --git a/lib/scheduler/ue_context/ue.cpp b/lib/scheduler/ue_context/ue.cpp index 35a18a9149..f4bdb53c39 100644 --- a/lib/scheduler/ue_context/ue.cpp +++ b/lib/scheduler/ue_context/ue.cpp @@ -39,6 +39,7 @@ ue::ue(const ue_creation_command& cmd) : cell->set_fallback_state(cmd.starts_in_fallback); } } + dl_lc_ch_mgr.set_fallback_state(cmd.starts_in_fallback); } void ue::slot_indication(slot_point sl_tx) @@ -80,6 +81,7 @@ void ue::handle_reconfiguration_request(const ue_reconf_command& cmd) // UE enters fallback mode when a Reconfiguration takes place. reconf_ongoing = true; get_pcell().set_fallback_state(true); + dl_lc_ch_mgr.set_fallback_state(true); // Update UE config. set_config(cmd.cfg); @@ -88,6 +90,7 @@ void ue::handle_reconfiguration_request(const ue_reconf_command& cmd) void ue::handle_config_applied() { get_pcell().set_fallback_state(false); + dl_lc_ch_mgr.set_fallback_state(false); reconf_ongoing = false; } @@ -177,17 +180,6 @@ void ue::handle_dl_buffer_state_indication(const dl_buffer_state_indication_mess dl_lc_ch_mgr.handle_dl_buffer_status_indication(msg.lcid, pending_bytes); } -bool ue::has_pending_dl_newtx_bytes() const -{ - return dl_lc_ch_mgr.has_pending_bytes(get_pcell().is_in_fallback_mode()); -} - -unsigned ue::pending_dl_newtx_bytes(lcid_t lcid) const -{ - return lcid != INVALID_LCID ? dl_lc_ch_mgr.pending_bytes(lcid) - : dl_lc_ch_mgr.pending_bytes(get_pcell().is_in_fallback_mode()); -} - unsigned ue::pending_ul_newtx_bytes() const { static constexpr unsigned SR_GRANT_BYTES = 512; diff --git a/lib/scheduler/ue_context/ue.h b/lib/scheduler/ue_context/ue.h index 4bbfd0a8d7..129682c5de 100644 --- a/lib/scheduler/ue_context/ue.h +++ b/lib/scheduler/ue_context/ue.h @@ -124,7 +124,11 @@ class ue /// \brief Checks if there are DL pending bytes that are yet to be allocated in a DL HARQ. /// This method is faster than computing \c pending_dl_newtx_bytes() > 0. - bool has_pending_dl_newtx_bytes() const; + bool has_pending_dl_newtx_bytes() const { return dl_lc_ch_mgr.has_pending_bytes(); } + bool has_pending_dl_newtx_bytes(const bounded_bitset& bearers) const + { + return dl_lc_ch_mgr.has_pending_bytes(bearers); + } /// \brief Checks if there are DL pending bytes for a specific LCID that are yet to be allocated in a DL HARQ. bool has_pending_dl_newtx_bytes(lcid_t lcid) const { return dl_lc_ch_mgr.has_pending_bytes(lcid); } @@ -141,12 +145,18 @@ class ue /// \brief Returns whether the UE has pending CEs' bytes to be scheduled, if any. bool has_pending_ce_bytes() const { return dl_lc_ch_mgr.has_pending_ces(); } - /// \brief Computes the number of DL pending bytes that are not already allocated in a DL HARQ. The value is used - /// to derive the required transport block size for an DL grant. - /// param[in] lcid If the LCID is provided, the method will return the number of pending bytes for that LCID. - /// Otherwise it will return the sum of all LCIDs pending bytes, excluding SRB0. + /// \brief Computes the number of DL pending bytes that are not already allocated in a DL HARQ. + /// \param[in] lcid If the LCID is provided, the method will return the number of pending bytes for that LCID. + /// Otherwise it will return the sum of all LCIDs pending bytes, considering the UE current state. /// \return The number of DL pending bytes that are not already allocated in a DL HARQ. - unsigned pending_dl_newtx_bytes(lcid_t lcid = lcid_t::INVALID_LCID) const; + unsigned pending_dl_newtx_bytes(lcid_t lcid = lcid_t::INVALID_LCID) const + { + return lcid != INVALID_LCID ? dl_lc_ch_mgr.pending_bytes(lcid) : dl_lc_ch_mgr.pending_bytes(); + } + unsigned pending_dl_newtx_bytes(const bounded_bitset& bearers) const + { + return dl_lc_ch_mgr.pending_bytes(bearers); + } /// \brief Computes the number of UL pending bytes that are not already allocated in a UL HARQ. The value is used /// to derive the required transport block size for an UL grant. diff --git a/tests/unittests/scheduler/ue_scheduling/fallback_scheduler_test.cpp b/tests/unittests/scheduler/ue_scheduling/fallback_scheduler_test.cpp index 0e62be5903..3ae088ec95 100644 --- a/tests/unittests/scheduler/ue_scheduling/fallback_scheduler_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/fallback_scheduler_test.cpp @@ -287,11 +287,12 @@ class base_fallback_tester bool add_ue(rnti_t tc_rnti, du_ue_index_t ue_index, bool remove_ded_cfg = false) { // Add cell to UE cell grid allocator. - auto ue_create_req = remove_ded_cfg - ? sched_config_helper::create_empty_spcell_cfg_sched_ue_creation_request() - : sched_config_helper::create_default_sched_ue_creation_request(bench->builder_params); - ue_create_req.crnti = tc_rnti; - ue_create_req.ue_index = ue_index; + auto ue_create_req = remove_ded_cfg + ? sched_config_helper::create_empty_spcell_cfg_sched_ue_creation_request() + : sched_config_helper::create_default_sched_ue_creation_request(bench->builder_params); + ue_create_req.crnti = tc_rnti; + ue_create_req.ue_index = ue_index; + ue_create_req.starts_in_fallback = true; return bench->add_ue(ue_create_req); } From e75c65c3e659217c6bb4765f76215d470d5430ed Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 20 Dec 2024 17:41:16 +0100 Subject: [PATCH 041/107] cu-up: redesign cu-up executor mapper to avoid deadlocks --- apps/cu/cu.cpp | 9 +- apps/du/du.cpp | 7 +- apps/gnb/gnb.cpp | 5 +- .../worker_manager/worker_manager.cpp | 7 +- apps/services/worker_manager/worker_manager.h | 2 +- .../worker_manager/worker_manager_config.h | 4 + include/srsran/cu_up/cu_up_executor_mapper.h | 4 +- lib/cu_up/cu_up_executor_mapper.cpp | 90 ++++++++++--------- 8 files changed, 72 insertions(+), 56 deletions(-) diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp index 55ce114f12..db9422cf4d 100644 --- a/apps/cu/cu.cpp +++ b/apps/cu/cu.cpp @@ -260,11 +260,16 @@ int main(int argc, char** argv) check_cpu_governor(cu_logger); check_drm_kms_polling(cu_logger); + // Create manager of timers for CU-CP and CU-UP, which will be driven by the system timer slot ticks. + timer_manager app_timers{256}; + timer_manager* cu_timers = &app_timers; + // Create worker manager. worker_manager_config worker_manager_cfg; fill_cu_worker_manager_config(worker_manager_cfg, cu_cfg); o_cu_cp_app_unit->fill_worker_manager_config(worker_manager_cfg); o_cu_up_app_unit->fill_worker_manager_config(worker_manager_cfg); + worker_manager_cfg.app_timers = &app_timers; worker_manager workers{worker_manager_cfg}; // Create layer specific PCAPs. @@ -315,10 +320,6 @@ int main(int argc, char** argv) std::unique_ptr e1_gw = create_e1_local_connector(e1_local_connector_config{*cu_up_dlt_pcaps.e1ap}); - // Create manager of timers for CU-CP and CU-UP, which will be driven by the system timer slot ticks. - timer_manager app_timers{256}; - timer_manager* cu_timers = &app_timers; - // Create time source that ticks the timers. std::optional time_source( std::in_place_t{}, app_timers, *epoll_broker, *workers.non_rt_hi_prio_exec, std::chrono::milliseconds{1}); diff --git a/apps/du/du.cpp b/apps/du/du.cpp index cc981f2858..457fc69988 100644 --- a/apps/du/du.cpp +++ b/apps/du/du.cpp @@ -235,10 +235,14 @@ int main(int argc, char** argv) check_cpu_governor(du_logger); check_drm_kms_polling(du_logger); + // Create manager of timers for DU, which will be driven by the PHY slot ticks. + timer_manager app_timers{256}; + // Instantiate worker manager. worker_manager_config worker_manager_cfg; o_du_app_unit->fill_worker_manager_config(worker_manager_cfg); fill_du_worker_manager_config(worker_manager_cfg, du_cfg); + worker_manager_cfg.app_timers = &app_timers; worker_manager workers{worker_manager_cfg}; @@ -259,9 +263,6 @@ int main(int argc, char** argv) *workers.non_rt_hi_prio_exec, *du_pcaps.f1ap); - // Create manager of timers for DU, which will be driven by the PHY slot ticks. - timer_manager app_timers{256}; - // Create F1-U connector. // TODO: Simplify this and use factory. gtpu_demux_creation_request du_f1u_gtpu_msg = {}; diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index ed62ecb07d..4404cd9a65 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -308,12 +308,16 @@ int main(int argc, char** argv) check_cpu_governor(gnb_logger); check_drm_kms_polling(gnb_logger); + // Setup application timers. + timer_manager app_timers{1024}; + // Instantiate worker manager. worker_manager_config worker_manager_cfg; o_cu_cp_app_unit->fill_worker_manager_config(worker_manager_cfg); o_cu_up_app_unit->fill_worker_manager_config(worker_manager_cfg); o_du_app_unit->fill_worker_manager_config(worker_manager_cfg); fill_gnb_worker_manager_config(worker_manager_cfg, gnb_cfg); + worker_manager_cfg.app_timers = &app_timers; worker_manager workers{worker_manager_cfg}; @@ -342,7 +346,6 @@ int main(int argc, char** argv) create_e1_local_connector(e1_local_connector_config{*cu_cp_dlt_pcaps.e1ap}); // Create manager of timers for DU, CU-CP and CU-UP, which will be driven by the PHY slot ticks. - timer_manager app_timers{256}; timer_manager* cu_timers = &app_timers; std::unique_ptr dummy_timers; if (o_du_app_unit->get_o_du_high_unit_config().du_high_cfg.config.is_testmode_enabled()) { diff --git a/apps/services/worker_manager/worker_manager.cpp b/apps/services/worker_manager/worker_manager.cpp index 319436599c..ff95d7bff0 100644 --- a/apps/services/worker_manager/worker_manager.cpp +++ b/apps/services/worker_manager/worker_manager.cpp @@ -42,7 +42,7 @@ worker_manager::worker_manager(const worker_manager_config& worker_cfg) : associate_low_prio_executors(worker_cfg); if (worker_cfg.cu_up_cfg) { - create_cu_up_executors(worker_cfg.cu_up_cfg.value()); + create_cu_up_executors(worker_cfg.cu_up_cfg.value(), *worker_cfg.app_timers); } if (worker_cfg.du_hi_cfg) { @@ -187,7 +187,7 @@ worker_manager::create_du_hi_slot_workers(unsigned nof_cells, bool rt_mode) return workers; } -void worker_manager::create_cu_up_executors(const worker_manager_config::cu_up_config& config) +void worker_manager::create_cu_up_executors(const worker_manager_config::cu_up_config& config, timer_manager& timers) { using namespace execution_config_helper; const auto& exec_map = exec_mng.executors(); @@ -197,7 +197,8 @@ void worker_manager::create_cu_up_executors(const worker_manager_config::cu_up_c task_worker_queue_size, config.gtpu_queue_size, *exec_map.at("low_prio_exec"), - config.dedicated_io_ul_strand}); + config.dedicated_io_ul_strand, + &timers}); } void worker_manager::create_du_executors(const worker_manager_config::du_high_config& du_hi, diff --git a/apps/services/worker_manager/worker_manager.h b/apps/services/worker_manager/worker_manager.h index 345d818e68..e134c16645 100644 --- a/apps/services/worker_manager/worker_manager.h +++ b/apps/services/worker_manager/worker_manager.h @@ -123,7 +123,7 @@ struct worker_manager : public worker_manager_executor_getter { bool rt_mode); /// Helper method that creates the CU-UP executors. - void create_cu_up_executors(const worker_manager_config::cu_up_config& config); + void create_cu_up_executors(const worker_manager_config::cu_up_config& config, timer_manager& timers); /// Helper method that creates the Distributed Unit executors. void create_du_executors(const worker_manager_config::du_high_config& du_hi, diff --git a/apps/services/worker_manager/worker_manager_config.h b/apps/services/worker_manager/worker_manager_config.h index 8795043fe8..c676d04ac3 100644 --- a/apps/services/worker_manager/worker_manager_config.h +++ b/apps/services/worker_manager/worker_manager_config.h @@ -14,6 +14,8 @@ namespace srsran { +class timer_manager; + /// Worker manager configuration. struct worker_manager_config { /// RU OFH worker configuration. @@ -101,6 +103,8 @@ struct worker_manager_config { os_sched_affinity_config low_prio_sched_config; /// PCAP configuration. pcap_config pcap_cfg; + /// Timer config. + timer_manager* app_timers = nullptr; /// Vector of affinities mask indexed by cell. std::vector> config_affinities; /// CU-UP configuration. diff --git a/include/srsran/cu_up/cu_up_executor_mapper.h b/include/srsran/cu_up/cu_up_executor_mapper.h index 497c1997bc..ace1a83826 100644 --- a/include/srsran/cu_up/cu_up_executor_mapper.h +++ b/include/srsran/cu_up/cu_up_executor_mapper.h @@ -10,9 +10,9 @@ #pragma once -#include "srsran/adt/span.h" #include "srsran/support/async/async_task.h" #include "srsran/support/executors/task_executor.h" +#include "srsran/support/timers.h" namespace srsran { namespace srs_cu_up { @@ -83,6 +83,8 @@ struct strand_based_executor_config { task_executor& worker_pool_executor; /// \brief Whether to instantiate a dedicated strand for sending UL PDUs to the IO. bool dedicated_io_strand; + /// \brief Timers used by the application. + timer_manager* timers; }; /// \brief Creates an executor mapper for the CU-UP that is based on strands of a worker pool. diff --git a/lib/cu_up/cu_up_executor_mapper.cpp b/lib/cu_up/cu_up_executor_mapper.cpp index 7dec7ac13b..1cf56f894b 100644 --- a/lib/cu_up/cu_up_executor_mapper.cpp +++ b/lib/cu_up/cu_up_executor_mapper.cpp @@ -11,7 +11,9 @@ #include "srsran/cu_up/cu_up_executor_mapper.h" #include "srsran/adt/mpmc_queue.h" #include "srsran/srslog/srslog.h" +#include "srsran/support/async/async_no_op_task.h" #include "srsran/support/async/execute_on.h" +#include "srsran/support/async/execute_on_blocking.h" #include "srsran/support/executors/inline_task_executor.h" #include "srsran/support/executors/strand_executor.h" #include @@ -25,28 +27,20 @@ namespace { class cancellable_task_executor final : public task_executor { public: - cancellable_task_executor(task_executor& exec_) : exec(&exec_) {} + cancellable_task_executor(task_executor& exec_, const std::atomic& cancelled_flag, timer_manager& timers_) : + exec(&exec_), cancelled(cancelled_flag), timers(&timers_) + { + } - /// Note: This needs to be called from within the executor's context. ~cancellable_task_executor() override { - // cancel pending tasks. - *cancelled = true; - - // extend life-time of cancelled flag by copying it (ref count) into an executor. - while (not exec->defer([flag_moved = cancelled]() mutable { - // flag is finally destroyed. - flag_moved.reset(); - })) { - srslog::fetch_basic_logger("ALL").warning("Unable to dispatch UE task executor deletion. Retrying..."); - std::this_thread::sleep_for(std::chrono::microseconds{100}); - } + srsran_assert(cancelled, "cancellable_task_executor destroyed before tasks being cancelled"); } [[nodiscard]] bool execute(unique_task task) override { - return exec->execute([&cancelled_ref = *cancelled, task = std::move(task)]() { - if (cancelled_ref) { + return exec->execute([this, task = std::move(task)]() { + if (cancelled.load(std::memory_order_acquire)) { return; } task(); @@ -55,8 +49,8 @@ class cancellable_task_executor final : public task_executor [[nodiscard]] bool defer(unique_task task) override { - return exec->defer([&cancelled_ref = *cancelled, task = std::move(task)]() { - if (cancelled_ref) { + return exec->defer([this, task = std::move(task)]() { + if (cancelled.load(std::memory_order_acquire)) { return; } task(); @@ -64,8 +58,9 @@ class cancellable_task_executor final : public task_executor } private: - task_executor* exec; - std::shared_ptr cancelled = std::make_shared(false); + task_executor* exec; + const std::atomic& cancelled; + timer_manager* timers; }; /// Implementation of the UE executor mapper. @@ -76,8 +71,14 @@ class ue_executor_mapper_impl final : public ue_executor_mapper task_executor& ul_exec_, task_executor& dl_exec_, task_executor& crypto_exec_, - task_executor& main_exec_) : - ctrl_exec(ctrl_exec_), ul_exec(ul_exec_), dl_exec(dl_exec_), crypto_exec(crypto_exec_), main_exec(main_exec_) + task_executor& main_exec_, + timer_manager& timers_) : + timers(timers_), + ctrl_exec(ctrl_exec_, cancelled_flag, timers), + ul_exec(ul_exec_, cancelled_flag, timers), + dl_exec(dl_exec_, cancelled_flag, timers), + crypto_exec(crypto_exec_), + main_exec(main_exec_) { } @@ -88,33 +89,33 @@ class ue_executor_mapper_impl final : public ue_executor_mapper return launch_async([this](coro_context>& ctx) mutable { CORO_BEGIN(ctx); - // Switch to the UE execution context. - CORO_AWAIT(defer_to_blocking(*ctrl_exec)); - - // Cancel all pending tasks. - // Note: this operation is only thread-safe because we are calling it from the UE executor's context. - ctrl_exec.reset(); - ul_exec.reset(); - dl_exec.reset(); - - // Return back to main control execution context. - CORO_AWAIT(defer_to_blocking(main_exec)); + if (cancel_tasks()) { + // Await for tasks for the given UE to be completely flushed, before proceeding. + // TODO: Use when_all. + CORO_AWAIT(defer_on_blocking(dl_exec, timers)); + CORO_AWAIT(defer_on_blocking(ul_exec, timers)); + CORO_AWAIT(defer_on_blocking(ctrl_exec, timers)); + } CORO_RETURN(); }); } - task_executor& ctrl_executor() override { return *ctrl_exec; } - task_executor& ul_pdu_executor() override { return *ul_exec; } - task_executor& dl_pdu_executor() override { return *dl_exec; } + task_executor& ctrl_executor() override { return ctrl_exec; } + task_executor& ul_pdu_executor() override { return ul_exec; } + task_executor& dl_pdu_executor() override { return dl_exec; } task_executor& crypto_executor() override { return crypto_exec; } private: - std::optional ctrl_exec; - std::optional ul_exec; - std::optional dl_exec; - task_executor& crypto_exec; - task_executor& main_exec; + bool cancel_tasks() { return not cancelled_flag.exchange(true, std::memory_order_acq_rel); } + + std::atomic cancelled_flag{false}; + timer_manager& timers; + cancellable_task_executor ctrl_exec; + cancellable_task_executor ul_exec; + cancellable_task_executor dl_exec; + task_executor& crypto_exec; + task_executor& main_exec; }; struct base_cu_up_executor_pool_config { @@ -123,12 +124,14 @@ struct base_cu_up_executor_pool_config { span ul_executors; span ctrl_executors; task_executor& crypto_exec; + timer_manager& timers; }; class round_robin_cu_up_exec_pool { public: - round_robin_cu_up_exec_pool(base_cu_up_executor_pool_config config) : main_exec(config.main_exec) + round_robin_cu_up_exec_pool(base_cu_up_executor_pool_config config) : + main_exec(config.main_exec), timers(config.timers) { srsran_assert(config.ctrl_executors.size() > 0, "At least one DL executor must be specified"); if (config.dl_executors.empty()) { @@ -154,7 +157,7 @@ class round_robin_cu_up_exec_pool { auto& ctxt = execs[round_robin_index.fetch_add(1, std::memory_order_relaxed) % execs.size()]; return std::make_unique( - ctxt.ctrl_exec, ctxt.ul_exec, ctxt.dl_exec, ctxt.crypto_exec, main_exec); + ctxt.ctrl_exec, ctxt.ul_exec, ctxt.dl_exec, ctxt.crypto_exec, main_exec, timers); } private: @@ -175,6 +178,7 @@ class round_robin_cu_up_exec_pool // Main executor of the CU-UP. task_executor& main_exec; + timer_manager& timers; // List of UE executor mapper contexts created. std::vector execs; @@ -248,7 +252,7 @@ class strand_based_cu_up_executor_mapper final : public cu_up_executor_mapper } return base_cu_up_executor_pool_config{ - cu_up_strand, ue_dl_execs, ue_ul_execs, ue_ctrl_execs, config.worker_pool_executor}; + cu_up_strand, ue_dl_execs, ue_ul_execs, ue_ctrl_execs, config.worker_pool_executor, *config.timers}; } // Base strand that sequentializes accesses to the worker pool executor. From a252ba282875511b3e0c58b4a876c6f3edbc0f4c Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 20 Dec 2024 17:46:58 +0100 Subject: [PATCH 042/107] support: remove blocking defer_to call --- include/srsran/support/async/execute_on.h | 8 -------- lib/cu_up/cu_up_executor_mapper.cpp | 10 +++------- 2 files changed, 3 insertions(+), 15 deletions(-) diff --git a/include/srsran/support/async/execute_on.h b/include/srsran/support/async/execute_on.h index 050d2c75a1..f51ae878ac 100644 --- a/include/srsran/support/async/execute_on.h +++ b/include/srsran/support/async/execute_on.h @@ -88,14 +88,6 @@ auto try_defer_to(TaskExecutor& exec) return detail::try_execute_on_awaiter{exec}; } -/// \brief Returns an awaitable that resumes the suspended coroutine in a different execution context. If the call -/// to defer fails, the awaitable will retry it until it succeeds. -template -auto defer_to_blocking(TaskExecutor& exec) -{ - return detail::blocking_execute_on_awaiter(exec); -} - /// Returns an awaitable that performs a task in a "dispatch_exec" executor, and resumes in "resume_exec" executor. /// Note: This is a specialization for tasks with non-void return values. /// \param dispatch_exec task executor where given task is going to run. diff --git a/lib/cu_up/cu_up_executor_mapper.cpp b/lib/cu_up/cu_up_executor_mapper.cpp index 1cf56f894b..8aae93aa94 100644 --- a/lib/cu_up/cu_up_executor_mapper.cpp +++ b/lib/cu_up/cu_up_executor_mapper.cpp @@ -28,7 +28,7 @@ class cancellable_task_executor final : public task_executor { public: cancellable_task_executor(task_executor& exec_, const std::atomic& cancelled_flag, timer_manager& timers_) : - exec(&exec_), cancelled(cancelled_flag), timers(&timers_) + exec(&exec_), cancelled(cancelled_flag) { } @@ -60,7 +60,6 @@ class cancellable_task_executor final : public task_executor private: task_executor* exec; const std::atomic& cancelled; - timer_manager* timers; }; /// Implementation of the UE executor mapper. @@ -71,14 +70,12 @@ class ue_executor_mapper_impl final : public ue_executor_mapper task_executor& ul_exec_, task_executor& dl_exec_, task_executor& crypto_exec_, - task_executor& main_exec_, timer_manager& timers_) : timers(timers_), ctrl_exec(ctrl_exec_, cancelled_flag, timers), ul_exec(ul_exec_, cancelled_flag, timers), dl_exec(dl_exec_, cancelled_flag, timers), - crypto_exec(crypto_exec_), - main_exec(main_exec_) + crypto_exec(crypto_exec_) { } @@ -115,7 +112,6 @@ class ue_executor_mapper_impl final : public ue_executor_mapper cancellable_task_executor ul_exec; cancellable_task_executor dl_exec; task_executor& crypto_exec; - task_executor& main_exec; }; struct base_cu_up_executor_pool_config { @@ -157,7 +153,7 @@ class round_robin_cu_up_exec_pool { auto& ctxt = execs[round_robin_index.fetch_add(1, std::memory_order_relaxed) % execs.size()]; return std::make_unique( - ctxt.ctrl_exec, ctxt.ul_exec, ctxt.dl_exec, ctxt.crypto_exec, main_exec, timers); + ctxt.ctrl_exec, ctxt.ul_exec, ctxt.dl_exec, ctxt.crypto_exec, timers); } private: From 34f384c4b3f2b631ce0d66d015b21c9bd45fe219 Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 2 Jan 2025 10:12:53 +0100 Subject: [PATCH 043/107] cu-up: remove unused member variable --- lib/cu_up/cu_up_executor_mapper.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/lib/cu_up/cu_up_executor_mapper.cpp b/lib/cu_up/cu_up_executor_mapper.cpp index 8aae93aa94..275a8c51b8 100644 --- a/lib/cu_up/cu_up_executor_mapper.cpp +++ b/lib/cu_up/cu_up_executor_mapper.cpp @@ -10,7 +10,6 @@ #include "srsran/cu_up/cu_up_executor_mapper.h" #include "srsran/adt/mpmc_queue.h" -#include "srsran/srslog/srslog.h" #include "srsran/support/async/async_no_op_task.h" #include "srsran/support/async/execute_on.h" #include "srsran/support/async/execute_on_blocking.h" @@ -27,7 +26,7 @@ namespace { class cancellable_task_executor final : public task_executor { public: - cancellable_task_executor(task_executor& exec_, const std::atomic& cancelled_flag, timer_manager& timers_) : + cancellable_task_executor(task_executor& exec_, const std::atomic& cancelled_flag) : exec(&exec_), cancelled(cancelled_flag) { } @@ -72,14 +71,18 @@ class ue_executor_mapper_impl final : public ue_executor_mapper task_executor& crypto_exec_, timer_manager& timers_) : timers(timers_), - ctrl_exec(ctrl_exec_, cancelled_flag, timers), - ul_exec(ul_exec_, cancelled_flag, timers), - dl_exec(dl_exec_, cancelled_flag, timers), + ctrl_exec(ctrl_exec_, cancelled_flag), + ul_exec(ul_exec_, cancelled_flag), + dl_exec(dl_exec_, cancelled_flag), crypto_exec(crypto_exec_) { } - ~ue_executor_mapper_impl() override = default; + ~ue_executor_mapper_impl() override + { + srsran_assert(cancelled_flag.load(std::memory_order_relaxed), + "cancellable_task_executor destroyed before tasks being cancelled"); + } async_task stop() override { @@ -126,8 +129,7 @@ struct base_cu_up_executor_pool_config { class round_robin_cu_up_exec_pool { public: - round_robin_cu_up_exec_pool(base_cu_up_executor_pool_config config) : - main_exec(config.main_exec), timers(config.timers) + round_robin_cu_up_exec_pool(base_cu_up_executor_pool_config config) : timers(config.timers) { srsran_assert(config.ctrl_executors.size() > 0, "At least one DL executor must be specified"); if (config.dl_executors.empty()) { @@ -173,7 +175,6 @@ class round_robin_cu_up_exec_pool }; // Main executor of the CU-UP. - task_executor& main_exec; timer_manager& timers; // List of UE executor mapper contexts created. From bc3533abf1d1fc6146b5fea7434e56b7204ad069 Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 2 Jan 2025 11:45:19 +0100 Subject: [PATCH 044/107] cu-up: redesign cu-up stop method --- .../support/executors/execute_until_success.h | 52 +++++++++++++++ .../srsran/support/executors/task_executor.h | 1 - lib/cu_up/cu_up_impl.cpp | 65 ++++++++++++------- lib/cu_up/cu_up_impl.h | 3 +- 4 files changed, 94 insertions(+), 27 deletions(-) create mode 100644 include/srsran/support/executors/execute_until_success.h diff --git a/include/srsran/support/executors/execute_until_success.h b/include/srsran/support/executors/execute_until_success.h new file mode 100644 index 0000000000..afec499720 --- /dev/null +++ b/include/srsran/support/executors/execute_until_success.h @@ -0,0 +1,52 @@ +/* + * + * Copyright 2021-2025 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/support/executors/task_executor.h" +#include "srsran/support/timers.h" + +namespace srsran { + +/// Execute task in the given task executor. If dispatch fails, postpone execution via timers. +/// +/// This function is useful for tasks whose dispatch can never fail. +template +void execute_until_success(TaskExecutor& exec, timer_manager& timers, CopyableTask&& task) +{ + static_assert(std::is_copy_constructible_v, "CopyableTask must be copyable"); + + // Try to dispatch right away. + if (not exec.execute(task)) { + // If it fails, resort to timers. + auto timer = timers.create_unique_timer(exec); + timer.set(timer_duration{0}, [task](timer_id_t tid) mutable { task(); }); + timer.run(); + } +} + +/// Defer task in the given task executor. If dispatch fails, postpone defer. +/// +/// This function is useful for tasks whose dispatch can never fail. +template +void defer_until_success(TaskExecutor& exec, timer_manager& timers, CopyableTask&& task) +{ + static_assert(std::is_copy_constructible_v, "CopyableTask must be copyable"); + + // Try to dispatch right away. + if (not exec.defer(task)) { + // If it fails, resort to timers. + auto timer = timers.create_unique_timer(exec); + timer.set(timer_duration{0}, [task](timer_id_t tid) mutable { task(); }); + timer.run(); + } +} + +} // namespace srsran diff --git a/include/srsran/support/executors/task_executor.h b/include/srsran/support/executors/task_executor.h index 527d8317c2..b38613e5c6 100644 --- a/include/srsran/support/executors/task_executor.h +++ b/include/srsran/support/executors/task_executor.h @@ -11,7 +11,6 @@ #pragma once #include "srsran/adt/unique_function.h" -#include "srsran/support/compiler.h" namespace srsran { diff --git a/lib/cu_up/cu_up_impl.cpp b/lib/cu_up/cu_up_impl.cpp index e5b882a1c4..aabbc347b1 100644 --- a/lib/cu_up/cu_up_impl.cpp +++ b/lib/cu_up/cu_up_impl.cpp @@ -16,6 +16,7 @@ #include "srsran/gtpu/gtpu_demux_factory.h" #include "srsran/gtpu/gtpu_echo_factory.h" #include "srsran/gtpu/gtpu_teid_pool_factory.h" +#include "srsran/support/executors/execute_until_success.h" #include using namespace srsran; @@ -57,7 +58,10 @@ static cu_up_manager_impl_dependencies generate_cu_up_manager_impl_dependencies( } cu_up::cu_up(const cu_up_config& config_, const cu_up_dependencies& dependencies) : - cfg(config_), ctrl_executor(dependencies.exec_mapper->ctrl_executor()), main_ctrl_loop(128) + cfg(config_), + ctrl_executor(dependencies.exec_mapper->ctrl_executor()), + timers(*dependencies.timers), + main_ctrl_loop(128) { assert_cu_up_dependencies_valid(dependencies); @@ -187,37 +191,35 @@ void cu_up::stop() logger.debug("CU-UP stopping..."); - eager_async_task main_loop; - std::atomic main_loop_stopped{false}; + std::condition_variable cvar; - auto stop_cu_up_main_loop = [this, &main_loop, &main_loop_stopped]() mutable { - if (main_loop.empty()) { - // First call. Initiate shutdown operations. - - // Stop statistics report timer. - statistics_report_timer.stop(); + auto stop_cu_up_main_loop = [this, &cvar]() mutable { + // Dispatch coroutine to stop CU-UP. + launch_async([this, &cvar](coro_context>& ctx) mutable { + CORO_BEGIN(ctx); // CU-UP stops listening to new GTPU Rx PDUs and stops pushing UL PDUs. - disconnect(); + CORO_AWAIT(handle_stop_command()); - // Stop main control loop and communicate back with the caller thread. - main_loop = main_ctrl_loop.request_stop(); - } + // We defer main ctrl loop stop to let the current coroutine complete successfully. + defer_until_success(ctrl_executor, timers, [this, &cvar]() { + // Stop main control loop and communicate back with the caller thread. + auto main_loop = main_ctrl_loop.request_stop(); - if (main_loop.ready()) { - // If the main loop finished, return back to the caller. - main_loop_stopped = true; - } + std::lock_guard lock2(mutex); + running = false; + cvar.notify_all(); + }); + + CORO_RETURN(); + }); }; + // Dispatch task to stop CU-UP main loop. + defer_until_success(ctrl_executor, timers, stop_cu_up_main_loop); + // Wait until the all tasks of the main loop are completed and main loop has stopped. - while (not main_loop_stopped) { - if (not ctrl_executor.execute(stop_cu_up_main_loop)) { - logger.error("Unable to stop CU-UP"); - return; - } - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } + cvar.wait(lock, [this]() { return not running; }); // CU-UP stops listening to new GTPU Rx PDUs. ngu_sessions.clear(); @@ -225,11 +227,24 @@ void cu_up::stop() logger.info("CU-UP stopped successfully"); } -void cu_up::disconnect() +async_task cu_up::handle_stop_command() { + // Stop statistics report timer. + statistics_report_timer.stop(); + gw_data_gtpu_demux_adapter.disconnect(); gtpu_gw_adapter.disconnect(); e1ap_cu_up_mng_adapter.disconnect(); + + return launch_async([this](coro_context>& ctx) { + CORO_BEGIN(ctx); + + // Stop main executor. + CORO_AWAIT(ctrl_exec_mapper->stop()); + ctrl_exec_mapper = nullptr; + + CORO_RETURN(); + }); } void cu_up::on_statistics_report_timer_expired() diff --git a/lib/cu_up/cu_up_impl.h b/lib/cu_up/cu_up_impl.h index dce4302d9e..18d8b1584d 100644 --- a/lib/cu_up/cu_up_impl.h +++ b/lib/cu_up/cu_up_impl.h @@ -44,7 +44,7 @@ class cu_up final : public cu_up_interface cu_up_manager* get_cu_up_manager() { return cu_up_mng.get(); } private: - void disconnect(); + async_task handle_stop_command(); void on_statistics_report_timer_expired(); @@ -54,6 +54,7 @@ class cu_up final : public cu_up_interface cu_up_config cfg; task_executor& ctrl_executor; + timer_manager& timers; // logger srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-UP", false); From 500397ba95579974ead3b19e0a4fbc4abe20c81e Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 2 Jan 2025 12:47:24 +0100 Subject: [PATCH 045/107] cu-up: fix cu-up stop method --- lib/cu_up/cu_up_executor_mapper.cpp | 24 ++++++++++++++++-------- lib/cu_up/cu_up_impl.cpp | 6 +++--- 2 files changed, 19 insertions(+), 11 deletions(-) diff --git a/lib/cu_up/cu_up_executor_mapper.cpp b/lib/cu_up/cu_up_executor_mapper.cpp index 275a8c51b8..702882d5b3 100644 --- a/lib/cu_up/cu_up_executor_mapper.cpp +++ b/lib/cu_up/cu_up_executor_mapper.cpp @@ -26,8 +26,8 @@ namespace { class cancellable_task_executor final : public task_executor { public: - cancellable_task_executor(task_executor& exec_, const std::atomic& cancelled_flag) : - exec(&exec_), cancelled(cancelled_flag) + cancellable_task_executor(task_executor& exec_, const std::atomic& cancelled_flag, timer_manager& timers_) : + exec(&exec_), cancelled(cancelled_flag), timers(timers_) { } @@ -56,9 +56,16 @@ class cancellable_task_executor final : public task_executor }); } + auto defer_on() + { + // We use the underlying executor to ignore cancelled flag. + return defer_on_blocking(*exec, timers); + } + private: task_executor* exec; const std::atomic& cancelled; + timer_manager& timers; }; /// Implementation of the UE executor mapper. @@ -71,9 +78,9 @@ class ue_executor_mapper_impl final : public ue_executor_mapper task_executor& crypto_exec_, timer_manager& timers_) : timers(timers_), - ctrl_exec(ctrl_exec_, cancelled_flag), - ul_exec(ul_exec_, cancelled_flag), - dl_exec(dl_exec_, cancelled_flag), + ctrl_exec(ctrl_exec_, cancelled_flag, timers), + ul_exec(ul_exec_, cancelled_flag, timers), + dl_exec(dl_exec_, cancelled_flag, timers), crypto_exec(crypto_exec_) { } @@ -92,9 +99,10 @@ class ue_executor_mapper_impl final : public ue_executor_mapper if (cancel_tasks()) { // Await for tasks for the given UE to be completely flushed, before proceeding. // TODO: Use when_all. - CORO_AWAIT(defer_on_blocking(dl_exec, timers)); - CORO_AWAIT(defer_on_blocking(ul_exec, timers)); - CORO_AWAIT(defer_on_blocking(ctrl_exec, timers)); + CORO_AWAIT(dl_exec.defer_on()); + CORO_AWAIT(ul_exec.defer_on()); + // Revert back to ctrl exec. + CORO_AWAIT(ctrl_exec.defer_on()); } CORO_RETURN(); diff --git a/lib/cu_up/cu_up_impl.cpp b/lib/cu_up/cu_up_impl.cpp index aabbc347b1..9a0ad3b4bf 100644 --- a/lib/cu_up/cu_up_impl.cpp +++ b/lib/cu_up/cu_up_impl.cpp @@ -185,7 +185,7 @@ void cu_up::start() void cu_up::stop() { std::unique_lock lock(mutex); - if (not std::exchange(running, false)) { + if (not running) { return; } @@ -195,7 +195,7 @@ void cu_up::stop() auto stop_cu_up_main_loop = [this, &cvar]() mutable { // Dispatch coroutine to stop CU-UP. - launch_async([this, &cvar](coro_context>& ctx) mutable { + main_ctrl_loop.schedule(launch_async([this, &cvar](coro_context>& ctx) mutable { CORO_BEGIN(ctx); // CU-UP stops listening to new GTPU Rx PDUs and stops pushing UL PDUs. @@ -212,7 +212,7 @@ void cu_up::stop() }); CORO_RETURN(); - }); + })); }; // Dispatch task to stop CU-UP main loop. From 29513121912bddbc98966912398ade2e7d753d55 Mon Sep 17 00:00:00 2001 From: Pedro Alvarez Date: Thu, 2 Jan 2025 12:53:31 +0000 Subject: [PATCH 046/107] cu_up: add a stop to cu_up_manager and ue_manager --- include/srsran/cu_up/cu_up_manager.h | 1 + lib/cu_up/cu_up_impl.cpp | 5 ++++- lib/cu_up/cu_up_manager_impl.cpp | 5 +++++ lib/cu_up/cu_up_manager_impl.h | 1 + lib/cu_up/ue_manager.cpp | 16 ++++++++++++++++ lib/cu_up/ue_manager.h | 1 + lib/cu_up/ue_manager_interfaces.h | 1 + 7 files changed, 29 insertions(+), 1 deletion(-) diff --git a/include/srsran/cu_up/cu_up_manager.h b/include/srsran/cu_up/cu_up_manager.h index 51a987ce60..301e9345ce 100644 --- a/include/srsran/cu_up/cu_up_manager.h +++ b/include/srsran/cu_up/cu_up_manager.h @@ -61,6 +61,7 @@ class cu_up_manager : public cu_up_manager_e1ap_connection_notifier, public cu_u public: ~cu_up_manager() override = default; + virtual async_task stop() = 0; virtual async_task enable_test_mode() = 0; virtual size_t get_nof_ues() = 0; }; diff --git a/lib/cu_up/cu_up_impl.cpp b/lib/cu_up/cu_up_impl.cpp index 9a0ad3b4bf..7f138816ad 100644 --- a/lib/cu_up/cu_up_impl.cpp +++ b/lib/cu_up/cu_up_impl.cpp @@ -239,10 +239,13 @@ async_task cu_up::handle_stop_command() return launch_async([this](coro_context>& ctx) { CORO_BEGIN(ctx); - // Stop main executor. + // Stop dedicated executor for control TEID. CORO_AWAIT(ctrl_exec_mapper->stop()); ctrl_exec_mapper = nullptr; + // Stop CU-UP manager and remove all UEs. + CORO_AWAIT(cu_up_mng->stop()); + CORO_RETURN(); }); } diff --git a/lib/cu_up/cu_up_manager_impl.cpp b/lib/cu_up/cu_up_manager_impl.cpp index 2b047f64cd..4df2ce46b7 100644 --- a/lib/cu_up/cu_up_manager_impl.cpp +++ b/lib/cu_up/cu_up_manager_impl.cpp @@ -61,6 +61,11 @@ cu_up_manager_impl::cu_up_manager_impl(const cu_up_manager_impl_config& co generate_ue_manager_dependencies(dependencies, logger)); } +async_task cu_up_manager_impl::stop() +{ + return ue_mng->stop(); +} + void cu_up_manager_impl::schedule_ue_async_task(ue_index_t ue_index, async_task task) { ue_mng->schedule_ue_async_task(ue_index, std::move(task)); diff --git a/lib/cu_up/cu_up_manager_impl.h b/lib/cu_up/cu_up_manager_impl.h index 8e388dab77..cabc8f4d90 100644 --- a/lib/cu_up/cu_up_manager_impl.h +++ b/lib/cu_up/cu_up_manager_impl.h @@ -46,6 +46,7 @@ class cu_up_manager_impl final : public cu_up_manager public: cu_up_manager_impl(const cu_up_manager_impl_config& config, const cu_up_manager_impl_dependencies& dependencies); + async_task stop() override; e1ap_bearer_context_setup_response handle_bearer_context_setup_request(const e1ap_bearer_context_setup_request& msg) override; diff --git a/lib/cu_up/ue_manager.cpp b/lib/cu_up/ue_manager.cpp index 5ee81e7afc..3b25dd240c 100644 --- a/lib/cu_up/ue_manager.cpp +++ b/lib/cu_up/ue_manager.cpp @@ -35,6 +35,22 @@ ue_manager::ue_manager(const ue_manager_config& config, const ue_manager_depende } } +async_task ue_manager::stop() +{ + // Routine to stop all UEs + auto ue_it = ue_db.begin(); + return launch_async([this, ue_it](coro_context>& ctx) mutable { + CORO_BEGIN(ctx); + + // Remove all UEs. + while (ue_it != ue_db.end()) { + CORO_AWAIT(remove_ue((ue_it++)->first)); + } + + CORO_RETURN(); + }); +} + ue_context* ue_manager::find_ue(ue_index_t ue_index) { srsran_assert(ue_index < MAX_NOF_UES, "Invalid ue_index={}", fmt::underlying(ue_index)); diff --git a/lib/cu_up/ue_manager.h b/lib/cu_up/ue_manager.h index 80ae26404a..320f8a848f 100644 --- a/lib/cu_up/ue_manager.h +++ b/lib/cu_up/ue_manager.h @@ -51,6 +51,7 @@ class ue_manager : public ue_manager_ctrl using ue_task_schedulers_t = slotted_array; const ue_db_t& get_ues() const { return ue_db; } + async_task stop() override; ue_context* add_ue(const ue_context_cfg& cfg) override; async_task remove_ue(ue_index_t ue_index) override; ue_context* find_ue(ue_index_t ue_index) override; diff --git a/lib/cu_up/ue_manager_interfaces.h b/lib/cu_up/ue_manager_interfaces.h index 5a614c3104..55f41883b1 100644 --- a/lib/cu_up/ue_manager_interfaces.h +++ b/lib/cu_up/ue_manager_interfaces.h @@ -22,6 +22,7 @@ class ue_manager_ctrl public: virtual ~ue_manager_ctrl() = default; + virtual async_task stop() = 0; virtual ue_context* add_ue(const ue_context_cfg& ue_cfg) = 0; virtual async_task remove_ue(ue_index_t ue_index) = 0; virtual ue_context* find_ue(ue_index_t ue_index) = 0; From 102ee09950fed7ab098456df7c3f6ef53de04e54 Mon Sep 17 00:00:00 2001 From: qarlosalberto Date: Tue, 31 Dec 2024 08:27:54 +0100 Subject: [PATCH 047/107] ci: update retina --- .gitlab/ci/e2e/.env | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env index cd72961fa8..3fd65401a4 100644 --- a/.gitlab/ci/e2e/.env +++ b/.gitlab/ci/e2e/.env @@ -1,6 +1,6 @@ SRSGNB_REGISTRY_URI=registry.gitlab.com/softwareradiosystems/srsgnb RETINA_REGISTRY_PREFIX=registry.gitlab.com/softwareradiosystems/ci/retina -RETINA_VERSION=0.59.3 +RETINA_VERSION=0.59.5 UBUNTU_VERSION=24.04 AMARISOFT_VERSION=2023-09-08 SRSUE_VERSION=23.11 From 576f40b7daa08db77560edea4b56a80fc8c4ee2e Mon Sep 17 00:00:00 2001 From: qarlosalberto Date: Tue, 31 Dec 2024 08:33:19 +0100 Subject: [PATCH 048/107] ci: increase ping interval --- tests/e2e/tests/ping.py | 55 +++++++++++++++++++++++++++++++++-- tests/e2e/tests/steps/stub.py | 18 +++++++++--- 2 files changed, 67 insertions(+), 6 deletions(-) diff --git a/tests/e2e/tests/ping.py b/tests/e2e/tests/ping.py index 1aefba999f..d314e18c57 100644 --- a/tests/e2e/tests/ping.py +++ b/tests/e2e/tests/ping.py @@ -231,6 +231,56 @@ def test_android_drx( always_download_artifacts=True, reattach_count=reattach_count, enable_drx=True, + ping_interval=0.1, + ) + + +@mark.parametrize( + "reattach_count", + (param(0, id="reattach:%s"),), +) +@mark.parametrize( + "band, common_scs, bandwidth", + (param(3, 15, 10, id="band:%s-scs:%s-bandwidth:%s"),), +) +@mark.android_drx +@mark.flaky( + reruns=2, + only_rerun=["failed to start", "Exception calling application", "Attach timeout reached", "Some packages got lost"], +) +# pylint: disable=too-many-arguments,too-many-positional-arguments +def test_android_no_drx( + retina_manager: RetinaTestManager, + retina_data: RetinaTestData, + ue: UEStub, # pylint: disable=invalid-name + fivegc: FiveGCStub, + gnb: GNBStub, + band: int, + common_scs: int, + bandwidth: int, + reattach_count: int, +): + """ + Android high performance Pings + """ + + _ping( + retina_manager=retina_manager, + retina_data=retina_data, + ue_array=(ue,), + gnb=gnb, + fivegc=fivegc, + band=band, + common_scs=common_scs, + bandwidth=bandwidth, + sample_rate=None, + global_timing_advance=-1, + time_alignment_calibration="auto", + warning_as_errors=False, + always_download_artifacts=True, + reattach_count=reattach_count, + enable_drx=False, + ping_interval=0.1, ) @@ -578,6 +628,7 @@ def _ping( prach_config_index=-1, pdsch_mcs_table: str = "qam256", pusch_mcs_table: str = "qam256", + ping_interval: float = 1.0, ): logging.info("Ping Test") @@ -609,13 +660,13 @@ def _ping( ue_attach_info_dict = ue_start_and_attach(ue_array, gnb, fivegc) try: - ping(ue_attach_info_dict, fivegc, ping_count) + ping(ue_attach_info_dict, fivegc, ping_count, ping_interval=ping_interval) # reattach and repeat if requested for _ in range(reattach_count): ue_stop(ue_array, retina_data) ue_attach_info_dict = ue_start_and_attach(ue_array, gnb, fivegc) - ping(ue_attach_info_dict, fivegc, ping_count) + ping(ue_attach_info_dict, fivegc, ping_count, ping_interval=ping_interval) except Failed as err: if not ims_mode or ims_mode == "enabled": raise err from None diff --git a/tests/e2e/tests/steps/stub.py b/tests/e2e/tests/steps/stub.py index c44576d9c3..dd82e0c04c 100644 --- a/tests/e2e/tests/steps/stub.py +++ b/tests/e2e/tests/steps/stub.py @@ -319,16 +319,26 @@ def _log_attached_ue(future: grpc.Future, ue_stub: UEStub): ) -def ping(ue_attach_info_dict: Dict[UEStub, UEAttachedInfo], fivegc: FiveGCStub, ping_count, time_step: int = 0): +def ping( + ue_attach_info_dict: Dict[UEStub, UEAttachedInfo], + fivegc: FiveGCStub, + ping_count, + time_step: int = 0, + ping_interval: float = 1.0, +): """ Ping command between an UE and a 5GC """ - ping_task_array = ping_start(ue_attach_info_dict, fivegc, ping_count, time_step) + ping_task_array = ping_start(ue_attach_info_dict, fivegc, ping_count, time_step, ping_interval) ping_wait_until_finish(ping_task_array) def ping_start( - ue_attach_info_dict: Dict[UEStub, UEAttachedInfo], fivegc: FiveGCStub, ping_count, time_step: float = 0 + ue_attach_info_dict: Dict[UEStub, UEAttachedInfo], + fivegc: FiveGCStub, + ping_count, + time_step: float = 0, + ping_interval: float = 1.0, ) -> List[grpc.Future]: """ Ping command between an UE and a 5GC @@ -339,7 +349,7 @@ def ping_start( ping_task_array: List[grpc.Future] = [] for ue_stub, ue_attached_info in ue_attach_info_dict.items(): ue_to_fivegc: grpc.Future = ue_stub.Ping.future( - PingRequest(address=ue_attached_info.ipv4_gateway, count=ping_count) + PingRequest(address=ue_attached_info.ipv4_gateway, count=ping_count, interval=ping_interval) ) ue_to_fivegc.add_done_callback( lambda _task, _msg=f"[{ue_attached_info.ipv4}] UE -> 5GC": _print_ping_result(_msg, _task) From 6b3d4df15887a3eeb600385a59f4f84e378c2911 Mon Sep 17 00:00:00 2001 From: frankist Date: Sat, 4 Jan 2025 22:53:40 +0100 Subject: [PATCH 049/107] timer lib that does not get affected by event reordering --- include/srsran/support/timers.h | 145 +++++++++-------- lib/support/timers.cpp | 278 ++++++++++++++++++++------------ 2 files changed, 256 insertions(+), 167 deletions(-) diff --git a/include/srsran/support/timers.h b/include/srsran/support/timers.h index 1956726763..82ea868514 100644 --- a/include/srsran/support/timers.h +++ b/include/srsran/support/timers.h @@ -54,6 +54,67 @@ class timer_manager /// Possible states for a timer. enum class state_t { stopped, running, expired }; + class timer_frontend; + + /// Command sent by the unique_timer (front-end) to the timer manager (back-end). + struct cmd_t { + /// action type used when timer is started. + struct start { + /// Duration for the new run. + timer_duration duration; + }; + /// action type used when timer is stopped. + struct stop {}; + /// action type used when timer is created. + struct create {}; + /// action type used when timer is destroyed. + struct destroy {}; + + /// Unique identity of the timer. + timer_id_t id; + /// Identifier associated with this particular command. + cmd_id_t cmd_id; + /// Action Request type sent by the unique_timer. + std::variant action; + }; + + class timer_command_buffer + { + public: + timer_command_buffer() + { + for (auto& cmd : buffers) { + cmd.id = timer_id_t::invalid; + } + } + + bool push(const cmd_t& cmd) + { + srsran_sanity_check(cmd.id != timer_id_t::invalid, "Invalid timer id"); + *back_buffer = cmd; + back_buffer = middle_buffer.exchange(back_buffer, std::memory_order_acq_rel); + return back_buffer->id == timer_id_t::invalid; + } + + bool pop(cmd_t& elem) + { + front_buffer = middle_buffer.exchange(front_buffer, std::memory_order_acq_rel); + if (front_buffer->id != timer_id_t::invalid) { + elem = *front_buffer; + front_buffer->id = timer_id_t::invalid; + return true; + } + return false; + } + + private: + std::array buffers; + + std::atomic middle_buffer{&buffers[1]}; + cmd_t* front_buffer{&buffers[0]}; + cmd_t* back_buffer{&buffers[2]}; + }; + /// Data relative to a timer state that is safe to access from the front-end side (the unique_timer). struct timer_frontend { /// Reference to timer manager class. @@ -73,6 +134,8 @@ class timer_manager timer_duration duration = INVALID_DURATION; /// Callback triggered when timer expires. Callback updates are protected by backend lock. unique_function timeout_callback; + /// Pending commands to be handled by the backend. + timer_command_buffer command_buffer; timer_frontend(timer_manager& parent_, timer_id_t id_) : parent(parent_), id(id_) {} @@ -87,30 +150,6 @@ class timer_manager void stop(); }; - /// Command sent by the unique_timer (front-end) to the timer manager (back-end). - struct cmd_t { - /// action type used when timer is started. - struct start { - /// Duration for the new run. - timer_duration duration; - }; - /// action type used when timer is stopped. - struct stop {}; - /// action type used when timer is created. - struct create { - std::unique_ptr frontend; - }; - /// action type used when timer is destroyed. - struct destroy {}; - - /// Unique identity of the timer. - timer_id_t id; - /// Identifier associated with this particular command. - cmd_id_t cmd_id; - /// Action Request type sent by the unique_timer. - std::variant action; - }; - /// Timer context used solely by the back-end side of the timer manager. struct timer_backend_context { /// Last command to be handled in the timer backend. @@ -153,22 +192,25 @@ class timer_manager class unique_timer_pool; class command_queue; + class timer_update_queue; /// \brief Create a new front-end context to be used by a newly created unique_timer. timer_frontend& create_frontend_timer(task_executor& exec); - /// Push a new timer command (start, stop, destroy) from the front-end execution context to the backend. - void push_timer_command(cmd_t cmd); + /// Signal a timer state update (start, stop, destroy) from the front-end execution context to the backend. + void push_timer_command(timer_id_t tid); /// Handle in the timer manager backend side the command sent by the timer frontends void handle_timer_commands(); + void handle_timer_creations(); + /// Create a new timer_handle object in the timer manager back-end side and associate it with the provided frontend /// timer. - void create_timer_handle(cmd_id_t cmd_id, std::unique_ptr timer); + void create_timer_handle(std::unique_ptr timer); /// Handle in the timer manager backend side, a single command sent by the respective timer frontend. - void handle_timer_command(timer_handle& timer, const cmd_t& cmd); + void handle_timer_updates(timer_handle& timer); /// Start the ticking of a timer with a given duration. void start_timer_backend(timer_handle& timer, timer_duration duration); @@ -222,10 +264,7 @@ class timer_manager std::deque> failed_to_trigger_timers; /// Commands sent by the timer front-end to the backend. - std::unique_ptr pending_cmds; - - /// List of commands that were not yet processed due to cmd_id reordering. - std::deque> tmp_skipped_cmds; + std::unique_ptr pending_events; }; /// \brief This class represents a timer which invokes a user-provided callback upon timer expiration. To setup a @@ -241,22 +280,11 @@ class unique_timer unique_timer& operator=(const unique_timer&) = delete; unique_timer(unique_timer&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {} - unique_timer& operator=(unique_timer&& other) noexcept - { - reset(); - handle = std::exchange(other.handle, nullptr); - return *this; - } - ~unique_timer() { reset(); } + unique_timer& operator=(unique_timer&& other) noexcept; + ~unique_timer(); /// Destroy current unique_timer context, cancelling any pending event. - void reset() - { - if (is_valid()) { - handle->destroy(); - handle = nullptr; - } - } + void reset(); /// Returns true if the timer is valid, otherwise returns false if already released. bool is_valid() const { return handle != nullptr; } @@ -277,33 +305,16 @@ class unique_timer timer_duration duration() const { return is_valid() ? handle->duration : timer_manager::INVALID_DURATION; } /// Configures the duration of the timer calling the provided callback upon timer expiration. - void set(timer_duration duration, unique_function callback) - { - srsran_assert(is_valid(), "Trying to setup empty timer pimpl"); - handle->set(duration, std::move(callback)); - } + void set(timer_duration duration, unique_function callback); /// Configures the duration of the timer. - void set(timer_duration duration) - { - srsran_assert(is_valid(), "Trying to setup empty timer pimpl"); - handle->set(duration); - } + void set(timer_duration duration); /// Activates the timer to start ticking. - void run() - { - srsran_assert(is_valid(), "Starting invalid timer"); - handle->run(); - } + void run(); /// Stops the timer from ticking. - void stop() - { - if (is_running()) { - handle->stop(); - } - } + void stop(); private: friend class timer_manager; diff --git a/lib/support/timers.cpp b/lib/support/timers.cpp index 150bdfffa1..7988ab7046 100644 --- a/lib/support/timers.cpp +++ b/lib/support/timers.cpp @@ -27,7 +27,9 @@ void timer_manager::timer_frontend::destroy() { cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; state = state_t::stopped; - parent.push_timer_command(cmd_t{id, new_cmd_id, cmd_t::destroy{}}); + if (command_buffer.push(cmd_t{id, new_cmd_id, cmd_t::destroy{}})) { + parent.push_timer_command(id); + } } void timer_manager::timer_frontend::set(timer_duration dur) @@ -38,7 +40,9 @@ void timer_manager::timer_frontend::set(timer_duration dur) if (state == state_t::running) { cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; // If we are setting the timer when it is already running, force run restart. - parent.push_timer_command(cmd_t{id, new_cmd_id, cmd_t::start{duration}}); + if (command_buffer.push(cmd_t{id, new_cmd_id, cmd_t::start{duration}})) { + parent.push_timer_command(id); + } } } @@ -55,14 +59,18 @@ void timer_manager::timer_frontend::run() srsran_assert(duration != INVALID_DURATION, "Calling timer::run with invalid duration"); cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; state = state_t::running; - parent.push_timer_command(cmd_t{id, new_cmd_id, cmd_t::start{duration}}); + if (command_buffer.push(cmd_t{id, new_cmd_id, cmd_t::start{duration}})) { + parent.push_timer_command(id); + } } void timer_manager::timer_frontend::stop() { cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; state = state_t::stopped; - parent.push_timer_command(cmd_t{id, new_cmd_id, cmd_t::stop{}}); + if (command_buffer.push(cmd_t{id, new_cmd_id, cmd_t::stop{}})) { + parent.push_timer_command(id); + } } // @@ -92,6 +100,80 @@ class timer_manager::unique_timer_pool // +class timer_manager::timer_update_queue +{ + constexpr static size_t initial_batch_size = 128; + constexpr static size_t warn_on_nof_dequeues_per_tick = 4096U; + constexpr static size_t default_queue_capacity = 16384; + +public: + explicit timer_update_queue(srslog::basic_logger& logger_, size_t initial_capacity = default_queue_capacity) : + logger(logger_), + timers_to_create(initial_capacity), + timer_creation_dequeuer(timers_to_create), + commands(initial_capacity), + cmd_dequeuer(commands), + temp_cmd_buffer(initial_batch_size) + { + } + + void push_new_timer(std::unique_ptr timer) { timers_to_create.enqueue(std::move(timer)); } + + void push_timer_update(timer_id_t tid) { commands.enqueue(tid); } + + std::unique_ptr pop_timer_to_create() + { + std::unique_ptr popped; + timers_to_create.try_dequeue(popped); + return popped; + } + + span pop_updated_timers() + { + size_t nof_popped = dequeue_events(commands, cmd_dequeuer, temp_cmd_buffer); + span list{temp_cmd_buffer.data(), nof_popped}; + return list; + } + +private: + size_t dequeue_events(moodycamel::ConcurrentQueue& q, + typename moodycamel::ConcurrentQueue::consumer_token_t& t, + std::vector& buffer) + { + size_t nread = 0; + for (size_t pos = 0; true;) { + // pop whatever space we have left in temporary buffer. + size_t max_n = buffer.size() - pos; + size_t n = q.try_dequeue_bulk(t, buffer.begin() + pos, max_n); + nread += n; + + if (n < max_n) { + // We drained the queue. + break; + } + + // Need to grow temp timer buffer and dequeue more objects. + pos = buffer.size(); + buffer.resize(buffer.size() + initial_batch_size); + } + if (nread == warn_on_nof_dequeues_per_tick) { + logger.warning("Number of timer events within one tick exceeded {}", warn_on_nof_dequeues_per_tick); + } + + return nread; + } + + srslog::basic_logger& logger; + + moodycamel::ConcurrentQueue> timers_to_create; + moodycamel::ConcurrentQueue::consumer_token_t timer_creation_dequeuer; + + // Queue of timers with pending events. + moodycamel::ConcurrentQueue commands; + moodycamel::ConcurrentQueue::consumer_token_t cmd_dequeuer; + std::vector temp_cmd_buffer; +}; + class timer_manager::command_queue { constexpr static size_t initial_batch_size = 128; @@ -162,7 +244,7 @@ timer_manager::timer_manager(size_t capacity) : logger(srslog::fetch_basic_logger("ALL")), timer_pool(std::make_unique(*this, capacity)), time_wheel(WHEEL_SIZE), - pending_cmds(std::make_unique(logger)) + pending_events(std::make_unique(logger)) { // Pre-reserve timers. while (timers.size() < capacity) { @@ -209,7 +291,7 @@ void timer_manager::tick() } } -void timer_manager::create_timer_handle(cmd_id_t cmd_id, std::unique_ptr timer) +void timer_manager::create_timer_handle(std::unique_ptr timer) { auto timer_idx = static_cast(timer->id); srsran_assert(timer_idx >= timers.size() or timers[timer_idx].frontend == nullptr, "Duplicate timer id detection"); @@ -217,113 +299,60 @@ void timer_manager::create_timer_handle(cmd_id_t cmd_id, std::unique_ptr new_cmds = pending_cmds->pop_commands(); - for (cmd_t& cmd : new_cmds) { - if (std::holds_alternative(cmd.action)) { - // Create new timer. - create_timer_handle(cmd.cmd_id, std::move(std::get(cmd.action).frontend)); - continue; - } - const auto t_idx = static_cast(cmd.id); - - if (t_idx >= timers.size()) { - // Second command ended up being processed before timer creation due to reordering. - // Grow timers list to avoid bad accesses. The cmd will be added to tmp_skipped_cmds below in this iteration. - if (t_idx > timers.size() + 1024) { - logger.error("Detected corrupted timer_id={}. Current timer list size is {}", t_idx, timers.size()); - } - timers.resize(t_idx + 1); - } - - // The timer already exists. - timer_handle& timer = timers[t_idx]; - - if (cmd.cmd_id - timer.backend.cmd_id >= std::numeric_limits::max() / 2) { - // Note: This should not happen. It means that there was some corruption of the cmd_id. - logger.warning( - "Discarding cmd_id={} for timer={}. Cause: cmd_id is below the last processed cmd_id={} by the timer", - cmd.cmd_id, - fmt::underlying(cmd.id), - timer.backend.cmd_id); - continue; - } - if (cmd.cmd_id - timer.backend.cmd_id > 1) { - // We detected a discontinuity in the cmd_id. It could be due to dequeue reordering. - // Cache the command for later processing. - tmp_skipped_cmds.emplace_back(cur_time, std::move(cmd)); - logger.debug("The processing of cmd_id={} for timer={} was postponed. Cause: There are commands in between " - "[{},{}) not yet processed", - cmd.cmd_id, - fmt::underlying(cmd.id), - timer.backend.cmd_id + 1, - cmd.cmd_id); - continue; - } - - // Handle timer command. - handle_timer_command(timer, cmd); + for (auto new_frontend = pending_events->pop_timer_to_create(); new_frontend != nullptr; + new_frontend = pending_events->pop_timer_to_create()) { + // Create new timer. + create_timer_handle(std::move(new_frontend)); } +} - constexpr static unsigned max_skip_slot_thres = 16; - // Sort skipped cmds by cmd_id. - std::sort(tmp_skipped_cmds.begin(), tmp_skipped_cmds.end(), [](const auto& lhs, const auto& rhs) { - return lhs.second.cmd_id < rhs.second.cmd_id; - }); - for (auto& p : tmp_skipped_cmds) { - cmd_t& cmd = p.second; - if (cmd.id == timer_id_t::invalid) { - // already processed. - continue; +void timer_manager::handle_timer_commands() +{ + // Dequeue timers with new updates to be processed in this tick. + span updated_timers = pending_events->pop_updated_timers(); + for (timer_id_t tid : updated_timers) { + const auto t_idx = static_cast(tid); + + if (t_idx >= timers.size() or timers[t_idx].frontend == nullptr) { + // Timer has not been created yet. + handle_timer_creations(); + + if (t_idx >= timers.size() or timers[t_idx].frontend == nullptr) { + // Unexpected - timer creation event is nowhere to be found. + logger.warning("Discarding update for timer={}. Cause: Timer hasn't been created", fmt::underlying(tid)); + continue; + } } - const auto t_idx = static_cast(cmd.id); - timer_handle& timer = timers[t_idx]; + srsran_assert(timer.frontend != nullptr, "Invalid timer frontend"); - if (cmd.cmd_id == timer.backend.cmd_id + 1) { - // Now the command can be processed in order. - handle_timer_command(timer, cmd); - cmd.id = timer_id_t::invalid; - } else if (cmd.cmd_id - timer.backend.cmd_id >= std::numeric_limits::max() / 2) { - // invalid cmd_id. - logger.warning( - "Discarding cmd_id={} for timer={}. Cause: cmd_id is below the last processed cmd_id={} by the timer", - cmd.cmd_id, - t_idx, - timer.backend.cmd_id); - cmd.id = timer_id_t::invalid; - } else if (cur_time - p.first > max_skip_slot_thres) { - logger.warning("Discarding cmd_ids=[{},{}) for timer={}. Cause: The cmd_ids went missing", - timer.backend.cmd_id + 1, - cmd.cmd_id - 1, - t_idx); - if (timer.frontend != nullptr) { - handle_timer_command(timer, cmd); - } - cmd.id = timer_id_t::invalid; - } - } - // Remove commands that were initially skipped and have been processed since then. - while (not tmp_skipped_cmds.empty() and tmp_skipped_cmds.front().second.id == timer_id_t::invalid) { - tmp_skipped_cmds.pop_front(); + handle_timer_updates(timer); } } -void timer_manager::handle_timer_command(timer_handle& timer, const cmd_t& cmd) +void timer_manager::handle_timer_updates(timer_handle& timer) { - srsran_sanity_check(not std::holds_alternative(cmd.action), "Invalid action type"); srsran_assert(timer.frontend != nullptr, "Invalid timer frontend"); + cmd_t cmd; + if (not timer.frontend->command_buffer.pop(cmd)) { + // this is unexpected. + logger.warning("New timer update signal for tid={} but no pending commands", fmt::underlying(timer.frontend->id)); + return; + } + // Update the timer backend cmd_id to match frontend. timer.backend.cmd_id = cmd.cmd_id; - // Stop timer if it is currently running, no matter which action. + if (std::holds_alternative(cmd.action)) { + return; + } + + // Stop timer if it is currently running, no matter which action to apply. try_stop_timer_backend(timer, false); if (std::holds_alternative(cmd.action)) { @@ -455,16 +484,18 @@ timer_manager::timer_frontend& timer_manager::create_frontend_timer(task_executo cached_timer = new_handle.get(); // Forward created timer handle to the backend. + cmd_id_t new_cmd_id = new_handle->cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; + new_handle->command_buffer.push(cmd_t{new_handle->id, new_cmd_id, cmd_t::create{}}); // Note: This cannot fail, otherwise the created timer "id" cannot be reused. - auto next_cmd_id = cached_timer->cmd_id.fetch_add(1, std::memory_order_relaxed) + 1; - pending_cmds->push_command(cmd_t{cached_timer->id, next_cmd_id, cmd_t::create{std::move(new_handle)}}); + pending_events->push_new_timer(std::move(new_handle)); + push_timer_command(id); return *cached_timer; } -void timer_manager::push_timer_command(cmd_t cmd) +void timer_manager::push_timer_command(timer_id_t tid) { - pending_cmds->push_command(std::move(cmd)); + pending_events->push_timer_update(tid); } unique_timer timer_manager::create_unique_timer(task_executor& exec) @@ -476,3 +507,50 @@ size_t timer_manager::nof_timers() const { return timers.size() - std::min(timers.size(), timer_pool->size_approx()); } + +// unique_timer methods + +unique_timer& unique_timer::operator=(unique_timer&& other) noexcept +{ + reset(); + handle = std::exchange(other.handle, nullptr); + return *this; +} + +unique_timer::~unique_timer() +{ + reset(); +} + +void unique_timer::reset() +{ + if (is_valid()) { + handle->destroy(); + handle = nullptr; + } +} + +void unique_timer::set(timer_duration duration, unique_function callback) +{ + srsran_assert(is_valid(), "Trying to setup empty timer pimpl"); + handle->set(duration, std::move(callback)); +} + +void unique_timer::set(timer_duration duration) +{ + srsran_assert(is_valid(), "Trying to setup empty timer pimpl"); + handle->set(duration); +} + +void unique_timer::run() +{ + srsran_assert(is_valid(), "Starting invalid timer"); + handle->run(); +} + +void unique_timer::stop() +{ + if (is_running()) { + handle->stop(); + } +} From e72484725f56f3521b04b671532d5c85dfc33b20 Mon Sep 17 00:00:00 2001 From: frankist Date: Mon, 6 Jan 2025 10:03:25 +0100 Subject: [PATCH 050/107] refactor timer lib - move implementation details to cpp file --- .../du_high/metrics/du_high_rlc_metrics.h | 1 + include/srsran/support/timers.h | 260 ++----- .../metrics_handler/metrics_handler_impl.h | 3 +- .../e2sm_kpm/e2sm_kpm_cu_meas_provider_impl.h | 1 + .../e2sm_kpm/e2sm_kpm_du_meas_provider_impl.h | 1 + lib/f1ap/du/ue_context/f1c_du_bearer_impl.h | 1 + lib/support/timers.cpp | 650 ++++++++++-------- .../mac/dl_sch_pdu_assembler_test.cpp | 1 + 8 files changed, 422 insertions(+), 496 deletions(-) diff --git a/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_rlc_metrics.h b/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_rlc_metrics.h index 997eb55525..5dfc461dc4 100644 --- a/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_rlc_metrics.h +++ b/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_rlc_metrics.h @@ -15,6 +15,7 @@ #include "apps/services/metrics/metrics_set.h" #include "srsran/adt/span.h" #include "srsran/rlc/rlc_metrics.h" +#include "srsran/srslog/logger.h" namespace srsran { diff --git a/include/srsran/support/timers.h b/include/srsran/support/timers.h index 82ea868514..7fd1159a06 100644 --- a/include/srsran/support/timers.h +++ b/include/srsran/support/timers.h @@ -10,15 +10,10 @@ #pragma once -#include "srsran/adt/intrusive_list.h" #include "srsran/adt/unique_function.h" -#include "srsran/srslog/logger.h" #include "srsran/support/executors/task_executor.h" -#include "srsran/support/srsran_assert.h" #include -#include -#include -#include +#include namespace srsran { @@ -28,6 +23,28 @@ enum class timer_id_t : unsigned { invalid = std::numeric_limits::max( /// Unit used to represent a time duration in terms of timer_manager ticks. using timer_duration = std::chrono::milliseconds; +namespace timer_detail { + +/// Possible states for a timer. +enum class state_t { stopped, running, expired }; + +/// Constant used to represent invalid timer durations. +static constexpr timer_duration INVALID_DURATION = timer_duration::max(); + +/// Data relative to a timer state that is accessed from the front-end side (the unique_timer) getters. +struct frontend_state { + /// Timer identifier. This identifier should remain constant throughout the timer lifetime. + const timer_id_t id; + /// The current state of timer (e.g. running/expired/notify_stop) from the perspective of the timer front-end. + state_t state = state_t::stopped; + /// Duration of each timer run. + timer_duration duration = INVALID_DURATION; + + explicit frontend_state(timer_id_t id_) : id(id_) {} +}; + +} // namespace timer_detail + class unique_timer; /// \brief This class implements the service associated with the management of the timers of the application, including @@ -40,137 +57,14 @@ class unique_timer; /// a running timer time-outs, the respective timer gets notified via a callback. The user can control the timer /// starting time, duration until timeout and timeout callback function definition via the \c unique_timer interface. /// -/// The implementation assumes that the "tick()" method is always called from the same thread and that an unique_timer +/// The implementation assumes that the "tick()" method is always called from the same thread and that a unique_timer /// object is not accessed concurrently by different threads. However, separate unique_timers can be safely used from -/// separate threads. An unique_timer object and the timer_manager communicate with one another via thread-safe queues. +/// separate threads. A unique_timer object and the timer_manager communicate with one another via thread-safe queues. class timer_manager { - /// Unambiguous identifier of the last command sent by a unique_timer to the timer_manager. - using cmd_id_t = uint32_t; - - /// Constant used to represent invalid timer durations. - static constexpr timer_duration INVALID_DURATION = timer_duration::max(); - - /// Possible states for a timer. - enum class state_t { stopped, running, expired }; - - class timer_frontend; - - /// Command sent by the unique_timer (front-end) to the timer manager (back-end). - struct cmd_t { - /// action type used when timer is started. - struct start { - /// Duration for the new run. - timer_duration duration; - }; - /// action type used when timer is stopped. - struct stop {}; - /// action type used when timer is created. - struct create {}; - /// action type used when timer is destroyed. - struct destroy {}; - - /// Unique identity of the timer. - timer_id_t id; - /// Identifier associated with this particular command. - cmd_id_t cmd_id; - /// Action Request type sent by the unique_timer. - std::variant action; - }; - - class timer_command_buffer - { - public: - timer_command_buffer() - { - for (auto& cmd : buffers) { - cmd.id = timer_id_t::invalid; - } - } - - bool push(const cmd_t& cmd) - { - srsran_sanity_check(cmd.id != timer_id_t::invalid, "Invalid timer id"); - *back_buffer = cmd; - back_buffer = middle_buffer.exchange(back_buffer, std::memory_order_acq_rel); - return back_buffer->id == timer_id_t::invalid; - } - - bool pop(cmd_t& elem) - { - front_buffer = middle_buffer.exchange(front_buffer, std::memory_order_acq_rel); - if (front_buffer->id != timer_id_t::invalid) { - elem = *front_buffer; - front_buffer->id = timer_id_t::invalid; - return true; - } - return false; - } - - private: - std::array buffers; - - std::atomic middle_buffer{&buffers[1]}; - cmd_t* front_buffer{&buffers[0]}; - cmd_t* back_buffer{&buffers[2]}; - }; - - /// Data relative to a timer state that is safe to access from the front-end side (the unique_timer). - struct timer_frontend { - /// Reference to timer manager class. - timer_manager& parent; - /// Timer identifier. This identifier should remain constant throughout the timer lifetime. - const timer_id_t id; - /// Task executor used to dispatch expiry callback. When set to nullptr, the timer is not allocated. - task_executor* exec = nullptr; - /// \brief Identifier of the last command sent from the front-end to the back-end side of the timer_manager. We use - /// this value to track whether a timer run has been overwritten and there is no need to trigger an timeout. - /// Atomic is used because we let the timer_manager back-end fetch this value to get a coarse estimation of the - /// last cmd_id at the front-end side, and avoid an unnecessary timeout callback trigger. - std::atomic cmd_id{0}; - /// The current state of timer (e.g. running/expired/notify_stop) from the perspective of the timer front-end. - state_t state = state_t::stopped; - /// Duration of each timer run. - timer_duration duration = INVALID_DURATION; - /// Callback triggered when timer expires. Callback updates are protected by backend lock. - unique_function timeout_callback; - /// Pending commands to be handled by the backend. - timer_command_buffer command_buffer; - - timer_frontend(timer_manager& parent_, timer_id_t id_) : parent(parent_), id(id_) {} - - void destroy(); - - void set(timer_duration duration); - - void set(timer_duration duration, unique_function callback); - - void run(); - - void stop(); - }; - - /// Timer context used solely by the back-end side of the timer manager. - struct timer_backend_context { - /// Last command to be handled in the timer backend. - cmd_id_t cmd_id = 0; - /// Current stand of the timer from the backend perspective. - state_t state = state_t::stopped; - /// Timeout set for the last timer run. - unsigned timeout = 0; - }; - - /// Object holding both the front-end and back-end contexts of the timers. - struct timer_handle : public intrusive_double_linked_list_element<>, public intrusive_forward_list_element<> { - std::unique_ptr frontend; - timer_backend_context backend; - - timer_handle() = default; - }; - public: /// Constructs a timer manager. - /// \param capacity Number of timers to pre-reserve and speed up timer construction. + /// \param pre_reserve_capacity Number of timers to pre-reserve and speed up timer construction. explicit timer_manager(size_t pre_reserve_capacity = 64); ~timer_manager(); @@ -185,101 +79,27 @@ class timer_manager size_t nof_timers() const; /// Returns the number of running timers handled by this instance. - unsigned nof_running_timers() const { return nof_timers_running; } + size_t nof_running_timers() const; private: - friend class unique_timer; - - class unique_timer_pool; - class command_queue; - class timer_update_queue; - - /// \brief Create a new front-end context to be used by a newly created unique_timer. - timer_frontend& create_frontend_timer(task_executor& exec); - - /// Signal a timer state update (start, stop, destroy) from the front-end execution context to the backend. - void push_timer_command(timer_id_t tid); - - /// Handle in the timer manager backend side the command sent by the timer frontends - void handle_timer_commands(); - - void handle_timer_creations(); - - /// Create a new timer_handle object in the timer manager back-end side and associate it with the provided frontend - /// timer. - void create_timer_handle(std::unique_ptr timer); - - /// Handle in the timer manager backend side, a single command sent by the respective timer frontend. - void handle_timer_updates(timer_handle& timer); - - /// Start the ticking of a timer with a given duration. - void start_timer_backend(timer_handle& timer, timer_duration duration); - - /// Stop a timer from ticking. If \c expiry_reason is set to true, the timer callback is dispatched to the frontend - /// execution context. - bool try_stop_timer_backend(timer_handle& timer, bool expiry_reason); - void stop_timer_backend(timer_handle& timer, bool expiry_reason); - - /// Called to destroy a timer context and return it back to the free timer pool. - void destroy_timer_backend(timer_handle& timer); - - /// Dispatch timeout handling to the front-end side. - bool trigger_timeout_handling(timer_handle& timer); - - /// Handle timers for which the timeout handling dispatch failed and was postponed. - void handle_postponed_timeouts(); - - srslog::basic_logger& logger; - - /// Counter of the number of ticks elapsed. This counter gets incremented on every \c tick call. - unsigned cur_time = 0; - - /// Number of created timer_handle objects that are currently running. - size_t nof_timers_running = 0; - - /// \brief List of timer_handle objects currently being managed by the timer_manager class. The index of the element - /// in this container matches the id of the corresponding unique_timer object. - /// This container will only grow in size. "Deleted" unique_timers in the front-end side are just tagged as empty - /// in their respective timer_handle, in the back-end side. This allows already created timer_handle objects to be - /// reused for the creation of new unique_timers in the front-end. The fact that the timer_handle is not deleted - /// also avoids potential dangling pointer when the timer expiry callback is dispatched to the front-end execution - /// context. - // Note: we use a deque to maintain reference validity. - std::deque timers; - - // Pool of timers that can be reused. - std::unique_ptr timer_pool; - std::atomic next_timer_id{0}; - - /// \brief Timer wheel, which is circularly indexed via a running timer timeout. Collisions are resolved via an - /// intrusive linked list stored in the timer_handle objects. - /// For a number of running timers N, and uniform distribution of timeout values, the tick() complexity - /// should be O(N/WHEEL_SIZE). Thus, the performance should improve with a larger WHEEL_SIZE, at the expense of more - /// used memory. - std::vector> time_wheel; - - /// \brief This is a list of timers that already timeouted, but for some unforeseen reason, - /// it was not possible to dispatch their respective timeout handling to the timer front-end. - /// The timeout handling will be re-triggered in the next slot. - std::deque> failed_to_trigger_timers; + class manager_impl; - /// Commands sent by the timer front-end to the backend. - std::unique_ptr pending_events; + /// Impl class for timer manager. + std::unique_ptr impl; }; -/// \brief This class represents a timer which invokes a user-provided callback upon timer expiration. To setup a -/// timer session it needs to connect to a \c timer_manager class. -/// This class is not thread-safe. +/// \brief This class represents a timer which invokes a user-provided callback upon timer expiration. +/// This class API is not thread-safe. class unique_timer { - using state_t = timer_manager::state_t; + using state_t = timer_detail::state_t; public: unique_timer() = default; unique_timer(const unique_timer&) = delete; unique_timer& operator=(const unique_timer&) = delete; - unique_timer(unique_timer&& other) noexcept : handle(std::exchange(other.handle, nullptr)) {} + unique_timer(unique_timer&& other) noexcept : handle(other.handle) { other.handle = nullptr; } unique_timer& operator=(unique_timer&& other) noexcept; ~unique_timer(); @@ -293,16 +113,16 @@ class unique_timer timer_id_t id() const { return is_valid() ? handle->id : timer_id_t::invalid; } /// Returns true if the timer duration has been set, otherwise returns false. - bool is_set() const { return is_valid() && handle->duration != timer_manager::INVALID_DURATION; } + bool is_set() const { return is_valid() && handle->duration != timer_detail::INVALID_DURATION; } /// Returns true if the timer is currently running, otherwise returns false. - bool is_running() const { return is_valid() && handle->state == timer_manager::state_t::running; } + bool is_running() const { return is_valid() && handle->state == state_t::running; } /// Returns true if the timer has expired, otherwise returns false. - bool has_expired() const { return is_valid() && handle->state == timer_manager::state_t::expired; } + bool has_expired() const { return is_valid() && handle->state == state_t::expired; } /// Returns the configured duration for this timer measured in ticks. - timer_duration duration() const { return is_valid() ? handle->duration : timer_manager::INVALID_DURATION; } + timer_duration duration() const { return is_valid() ? handle->duration : timer_detail::INVALID_DURATION; } /// Configures the duration of the timer calling the provided callback upon timer expiration. void set(timer_duration duration, unique_function callback); @@ -319,9 +139,9 @@ class unique_timer private: friend class timer_manager; - explicit unique_timer(timer_manager::timer_frontend& impl) : handle(&impl) {} + explicit unique_timer(timer_detail::frontend_state& impl) : handle(&impl) {} - timer_manager::timer_frontend* handle = nullptr; + timer_detail::frontend_state* handle = nullptr; }; /// Factory of timers that associates created timers to specific task executors. diff --git a/lib/cu_cp/metrics_handler/metrics_handler_impl.h b/lib/cu_cp/metrics_handler/metrics_handler_impl.h index 4e23a11761..78e157aa56 100644 --- a/lib/cu_cp/metrics_handler/metrics_handler_impl.h +++ b/lib/cu_cp/metrics_handler/metrics_handler_impl.h @@ -16,6 +16,7 @@ #include "srsran/cu_cp/cu_cp_metrics_handler.h" #include "srsran/support/executors/task_executor.h" #include "srsran/support/timers.h" +#include #include namespace srsran { @@ -61,4 +62,4 @@ class metrics_handler_impl final : public metrics_handler }; } // namespace srs_cu_cp -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_cu_meas_provider_impl.h b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_cu_meas_provider_impl.h index 17acc1bced..f549966df2 100644 --- a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_cu_meas_provider_impl.h +++ b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_cu_meas_provider_impl.h @@ -18,6 +18,7 @@ #include "srsran/e2/e2sm/e2sm.h" #include "srsran/e2/e2sm/e2sm_kpm.h" #include "srsran/pdcp/pdcp_entity.h" +#include #include #include diff --git a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.h b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.h index 59ebe74941..7369f7e0ed 100644 --- a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.h +++ b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.h @@ -18,6 +18,7 @@ #include "srsran/e2/e2sm/e2sm.h" #include "srsran/e2/e2sm/e2sm_kpm.h" #include "srsran/f1ap/du/f1ap_du.h" +#include #include #include diff --git a/lib/f1ap/du/ue_context/f1c_du_bearer_impl.h b/lib/f1ap/du/ue_context/f1c_du_bearer_impl.h index 8773c84181..4d9bbbe326 100644 --- a/lib/f1ap/du/ue_context/f1c_du_bearer_impl.h +++ b/lib/f1ap/du/ue_context/f1c_du_bearer_impl.h @@ -18,6 +18,7 @@ #include "srsran/ran/rnti.h" #include "srsran/support/async/protocol_transaction_manager.h" #include "srsran/support/memory_pool/unsync_fixed_size_memory_block_pool.h" +#include namespace srsran { namespace srs_du { diff --git a/lib/support/timers.cpp b/lib/support/timers.cpp index 7988ab7046..3b1ced7e93 100644 --- a/lib/support/timers.cpp +++ b/lib/support/timers.cpp @@ -10,10 +10,15 @@ #include "srsran/support/timers.h" #include "cameron314/concurrentqueue.h" +#include "srsran/adt/intrusive_list.h" #include "srsran/adt/span.h" #include "srsran/srslog/srslog.h" +#include +#include +#include using namespace srsran; +using namespace timer_detail; /// Timer Wheel configuration parameters. static constexpr size_t WHEEL_SHIFT = 16U; @@ -23,111 +28,50 @@ static constexpr size_t WHEEL_MASK = WHEEL_SIZE - 1U; /// Maximum timeout duration supported for a given timer in ticks. static constexpr timer_duration MAX_TIMER_DURATION = timer_duration{std::numeric_limits::max() / 2}; -void timer_manager::timer_frontend::destroy() -{ - cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; - state = state_t::stopped; - if (command_buffer.push(cmd_t{id, new_cmd_id, cmd_t::destroy{}})) { - parent.push_timer_command(id); - } -} - -void timer_manager::timer_frontend::set(timer_duration dur) -{ - srsran_assert(dur <= MAX_TIMER_DURATION, "Invalid timer duration ({}>{})", dur.count(), MAX_TIMER_DURATION.count()); - - duration = dur; - if (state == state_t::running) { - cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; - // If we are setting the timer when it is already running, force run restart. - if (command_buffer.push(cmd_t{id, new_cmd_id, cmd_t::start{duration}})) { - parent.push_timer_command(id); - } - } -} - -void timer_manager::timer_frontend::set(timer_duration dur, unique_function callback_) -{ - set(dur); - // Note: we can set the timeout_callback after pushing the cmd to the backend, because the callback is going to be - // run in the frontend (via the executor defer call). - timeout_callback = std::move(callback_); -} - -void timer_manager::timer_frontend::run() -{ - srsran_assert(duration != INVALID_DURATION, "Calling timer::run with invalid duration"); - cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; - state = state_t::running; - if (command_buffer.push(cmd_t{id, new_cmd_id, cmd_t::start{duration}})) { - parent.push_timer_command(id); - } -} - -void timer_manager::timer_frontend::stop() -{ - cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; - state = state_t::stopped; - if (command_buffer.push(cmd_t{id, new_cmd_id, cmd_t::stop{}})) { - parent.push_timer_command(id); - } -} - -// - -class timer_manager::unique_timer_pool -{ -public: - unique_timer_pool(timer_manager& parent, unsigned capacity) : free_list(capacity) {} - - void push(timer_manager::timer_frontend* obj) { free_list.enqueue(obj); } - - timer_manager::timer_frontend* pop() - { - timer_manager::timer_frontend* ret; - if (free_list.try_dequeue(ret)) { - return ret; - } - return nullptr; - } - - size_t size_approx() const { return free_list.size_approx(); } - -private: - // List of timer_handle objects in timer_list that are currently not allocated. - moodycamel::ConcurrentQueue free_list; +namespace { + +/// Unambiguous identifier of the last command sent by a unique_timer to the timer_manager. +using cmd_id_t = uint32_t; + +/// Command sent by the unique_timer (front-end) to the timer manager (back-end). +struct cmd_t { + /// action type used when timer is started. + struct start { + /// Duration for the new run. + timer_duration duration; + }; + /// action type used when timer is stopped. + struct stop {}; + /// action type used when timer is created. + struct create {}; + /// action type used when timer is destroyed. + struct destroy {}; + + /// Unique identity of the timer. + timer_id_t id; + /// Identifier associated with this particular command. + cmd_id_t cmd_id; + /// Action Request type sent by the unique_timer. + std::variant action; }; -// - -class timer_manager::timer_update_queue +/// Queue used by timer frontend to report that they have a new pending action to be processed by the backend. +class timer_update_signaller { constexpr static size_t initial_batch_size = 128; constexpr static size_t warn_on_nof_dequeues_per_tick = 4096U; constexpr static size_t default_queue_capacity = 16384; public: - explicit timer_update_queue(srslog::basic_logger& logger_, size_t initial_capacity = default_queue_capacity) : - logger(logger_), - timers_to_create(initial_capacity), - timer_creation_dequeuer(timers_to_create), - commands(initial_capacity), - cmd_dequeuer(commands), - temp_cmd_buffer(initial_batch_size) + explicit timer_update_signaller(srslog::basic_logger& logger_, size_t initial_capacity = default_queue_capacity) : + logger(logger_), commands(initial_capacity), cmd_dequeuer(commands), temp_cmd_buffer(initial_batch_size) { } - void push_new_timer(std::unique_ptr timer) { timers_to_create.enqueue(std::move(timer)); } - - void push_timer_update(timer_id_t tid) { commands.enqueue(tid); } - - std::unique_ptr pop_timer_to_create() - { - std::unique_ptr popped; - timers_to_create.try_dequeue(popped); - return popped; - } + /// Signal a timer state update (start, stop, create, destroy) from the front-end execution context to the backend. + void notify_timer_update(timer_id_t tid) { commands.enqueue(tid); } + /// Pop in batches the timers that have pending updates. span pop_updated_timers() { size_t nof_popped = dequeue_events(commands, cmd_dequeuer, temp_cmd_buffer); @@ -165,161 +109,315 @@ class timer_manager::timer_update_queue srslog::basic_logger& logger; - moodycamel::ConcurrentQueue> timers_to_create; - moodycamel::ConcurrentQueue::consumer_token_t timer_creation_dequeuer; - // Queue of timers with pending events. moodycamel::ConcurrentQueue commands; moodycamel::ConcurrentQueue::consumer_token_t cmd_dequeuer; std::vector temp_cmd_buffer; }; -class timer_manager::command_queue +/// \brief Channel used by the timer frontend to communicate to the backend and forward new events. +/// +/// This channel works as follows: +/// - it overwrites the latest timer event (e.g. stop, start, create, destroy) in an internal buffer. Older events +/// are discarded, even if they were not processed by the backend. +/// - it signals that the timer has a pending event to the backend via the \c backend_signaller IFF the timer has no +/// other events pending to be handled by the backend. This way the use of the backend_signaller is minimized. +class backend_channel { - constexpr static size_t initial_batch_size = 128; - constexpr static size_t warn_on_nof_dequeues_per_tick = 4096U; - constexpr static size_t default_queue_capacity = 16384; - public: - explicit command_queue(srslog::basic_logger& logger_, size_t initial_capacity = default_queue_capacity) : - logger(logger_), commands(initial_capacity), cmd_dequeuer(commands), temp_cmd_buffer(initial_batch_size) + backend_channel(timer_update_signaller& backend_signaller_) : backend_signaller(backend_signaller_) { + for (auto& cmd : buffers) { + cmd.id = timer_id_t::invalid; + } } - void push_command(cmd_t cmd) { commands.enqueue(std::move(cmd)); } + /// Counter of the last command sent by this timer frontend. + cmd_id_t current_cmd_id() const { return cmd_id.load(std::memory_order_relaxed); } - span pop_commands() + /// Push a new action from the frontend to the backend. + void push(timer_id_t tid, const std::variant& action) { - size_t nof_popped = dequeue_events(commands, cmd_dequeuer, temp_cmd_buffer); - span list{temp_cmd_buffer.data(), nof_popped}; + srsran_sanity_check(tid != timer_id_t::invalid, "Invalid timer id"); + // increase the "epoch", which will invalidate other events for this timer. + cmd_id_t new_cmd_id = cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; + // prepare new action. + write_buffer->id = tid; + write_buffer->cmd_id = new_cmd_id; + write_buffer->action = action; + // push new action to middle buffer, so that it becomes visible by the backend. + write_buffer = middle_buffer.exchange(write_buffer, std::memory_order_acq_rel); + if (write_buffer->id == timer_id_t::invalid) { + // during the exchange, we got back the previous middle_buffer. Two scenarios can occur: + // - the action of the previous middle_buffer is marked as processed (id == invalid). This means that the backend + // was able to process the previously dispatched action, and we need to notify the backend again of a new pending + // action for this timer. + // - the action of the previous middle_buffer was not marked as processed (id != invalid). This means that the + // backend did not have time to process the previously dispatched event, however, it still knows it has + // events to process for this timer. We avoid notifying the backend again in this case. + backend_signaller.notify_timer_update(tid); + } + srsran_sanity_check(new_cmd_id == cmd_id.load(std::memory_order_relaxed), + "Detected race condition for timer {}", + fmt::underlying(tid)); + } - // Note: commands may arrive out-of-order due to the nature of this MPMC queue. We will order based on cmd_id, so - // that it is easier to process by the backend. - std::sort(list.begin(), list.end(), [](const cmd_t& lhs, const cmd_t& rhs) { return lhs.cmd_id < rhs.cmd_id; }); - return list; + /// Called by the backend to pop the pending action for this timer. Returns false if no action is pending. + bool pop(cmd_t& elem) + { + read_buffer = middle_buffer.exchange(read_buffer, std::memory_order_acq_rel); + if (read_buffer->id != timer_id_t::invalid) { + // There is new action to process. + elem = *read_buffer; + // mark the action as processed. + read_buffer->id = timer_id_t::invalid; + return true; + } + return false; } private: - using timer_creation_queue = moodycamel::ConcurrentQueue>; + timer_update_signaller& backend_signaller; + + /// \brief Identifier of the last command sent from the front-end to the back-end side of the timer_manager. We use + /// this value to track whether a timer run has been overwritten and there is no need to trigger a timeout. + /// Atomic is used because we let the timer_manager back-end fetch this value to get a coarse estimation of the + /// last cmd_id at the front-end side, and avoid an unnecessary timeout callback trigger. + /// We only used memory_order_relaxed, because the real synchronization is done via the backend_signaller and + /// middle_buffer. + std::atomic cmd_id{0}; + + // TODO: Consider using alignas to avoid false sharing. + std::array buffers; + + std::atomic middle_buffer{&buffers[1]}; + cmd_t* read_buffer{&buffers[0]}; + cmd_t* write_buffer{&buffers[2]}; +}; - template - size_t dequeue_events(moodycamel::ConcurrentQueue& q, - typename moodycamel::ConcurrentQueue::consumer_token_t& t, - std::vector& buffer) +/// Frontend side of the timer. +class frontend_handle : public frontend_state +{ +public: + /// Callback triggered when timer expires. Callback updates are protected by backend lock. + unique_function timeout_callback; + /// Task executor used to dispatch expiry callback. When set to nullptr, the timer is not allocated. + task_executor* exec = nullptr; + /// Pending commands to be handled by the backend. + backend_channel backend_ch; + + frontend_handle(timer_id_t id_, timer_update_signaller& ev_signaller) : frontend_state(id_), backend_ch(ev_signaller) { - size_t nread = 0; - for (size_t pos = 0; true;) { - // pop whatever space we have left in temporary buffer. - size_t max_n = buffer.size() - pos; - size_t n = q.try_dequeue_bulk(t, buffer.begin() + pos, max_n); - nread += n; + } - if (n < max_n) { - // We drained the queue. - break; - } + /// Initiate destruction of timer. + void destroy() + { + state = state_t::stopped; + backend_ch.push(id, cmd_t::destroy{}); + } - // Need to grow temp timer buffer and dequeue more objects. - pos = buffer.size(); - buffer.resize(buffer.size() + initial_batch_size); - } - if (nread == warn_on_nof_dequeues_per_tick) { - logger.warning("Number of timer events within one tick exceeded {}", warn_on_nof_dequeues_per_tick); + /// Set timer duration. If it is running, restart timer. + void set(timer_duration dur) + { + srsran_assert(dur <= MAX_TIMER_DURATION, "Invalid timer duration ({}>{})", dur.count(), MAX_TIMER_DURATION.count()); + duration = dur; + if (state == state_t::running) { + // If we are setting the timer when it is already running, force run restart. + backend_ch.push(id, cmd_t::start{duration}); } + } - return nread; + void set(timer_duration dur, unique_function callback_) + { + set(dur); + // Note: we can set the timeout_callback after pushing the cmd to the backend, because the callback is going to be + // run in the frontend (via the executor defer call). + timeout_callback = std::move(callback_); } - srslog::basic_logger& logger; + void run() + { + srsran_assert(duration != INVALID_DURATION, "Calling timer::run with invalid duration"); + state = state_t::running; + backend_ch.push(id, cmd_t::start{duration}); + } - // Queue of new commands. - moodycamel::ConcurrentQueue commands; - moodycamel::ConcurrentQueue::consumer_token_t cmd_dequeuer; - std::vector temp_cmd_buffer; + void stop() + { + state = state_t::stopped; + backend_ch.push(id, cmd_t::stop{}); + } }; -// +} // namespace -timer_manager::timer_manager(size_t capacity) : - logger(srslog::fetch_basic_logger("ALL")), - timer_pool(std::make_unique(*this, capacity)), - time_wheel(WHEEL_SIZE), - pending_events(std::make_unique(logger)) +/// Implementation of timer_manager. +class timer_manager::manager_impl { - // Pre-reserve timers. - while (timers.size() < capacity) { - timers.emplace_back().frontend = - std::make_unique(*this, (timer_id_t)next_timer_id.fetch_add(1, std::memory_order_relaxed)); - } + constexpr static size_t TIMER_CREATION_QUEUE_INITIAL_CAPACITY = 32; - // Push to free list in ascending id order. - for (const auto& t : timers) { - timer_pool->push(t.frontend.get()); +public: + /// Timer context used solely by the back-end side of the timer manager. + struct timer_backend_context { + /// Last command to be handled in the timer backend. + cmd_id_t cmd_id = 0; + /// Current stand of the timer from the backend perspective. + state_t state = state_t::stopped; + /// Timeout set for the last timer run. + unsigned timeout = 0; + }; + + /// Object holding both the front-end and back-end contexts of the timers. + struct timer_handle : public intrusive_double_linked_list_element<>, public intrusive_forward_list_element<> { + std::unique_ptr frontend; + timer_backend_context backend; + + timer_handle() = default; + }; + + manager_impl(size_t capacity) : + logger(srslog::fetch_basic_logger("ALL")), + timer_free_list(capacity), + time_wheel(WHEEL_SIZE), + pending_timers_to_create(TIMER_CREATION_QUEUE_INITIAL_CAPACITY), + timers_with_pending_events(logger) + { + // Pre-reserve timers. + for (unsigned i = 0; i != capacity; ++i) { + timers.emplace_back().frontend = std::make_unique( + static_cast(next_timer_id.fetch_add(1, std::memory_order_relaxed)), timers_with_pending_events); + } + + // Push to free list in ascending id order. + for (const auto& t : timers) { + timer_free_list.enqueue(t.frontend.get()); + } } -} -timer_manager::~timer_manager() {} + /// \brief Handle pending timer creation events. + void handle_pending_timer_creations() + { + std::unique_ptr new_frontend; + while (pending_timers_to_create.try_dequeue(new_frontend)) { + auto timer_idx = static_cast(new_frontend->id); + srsran_assert(timer_idx >= timers.size() or timers[timer_idx].frontend == nullptr, + "Duplicate timer id detection"); + + if (timer_idx >= timers.size()) { + // Resize timers list if needed. + timers.resize(timer_idx + 1); + } -void timer_manager::tick() -{ - // Dequeue new commands from the timer front-ends to be processed in this tick. - handle_timer_commands(); + // Connect frontend to backend. + timers[timer_idx].frontend = std::move(new_frontend); + } + } - // Re-trigger timeout for timers that failed to be triggered in the previous slot. - handle_postponed_timeouts(); + /// Handle in the timer manager backend side the command sent by the timer frontends + void handle_timer_commands(); - // Advance time. - ++cur_time; + /// Handle in the timer manager backend side, a single command sent by the respective timer frontend. + void handle_timer_updates(timer_handle& timer); - // Process the timer runs which expire in this tick. - auto& wheel_list = time_wheel[cur_time & WHEEL_MASK]; + /// Stop a timer from ticking. If \c expiry_reason is set to true, the timer callback is dispatched to the frontend + /// execution context. + bool try_stop_timer_backend(timer_handle& timer, bool expiry_reason); + void stop_timer_backend(timer_handle& timer, bool expiry_reason) + { + srsran_assert(timer.backend.state == state_t::running, "Stopping timer that was not running"); + try_stop_timer_backend(timer, expiry_reason); + } - // Iterate intrusive linked list of running timers with same wheel index. - for (auto it = wheel_list.begin(); it != wheel_list.end();) { - srsran_assert(it->frontend != nullptr, "invalid state of timer in timer wheel"); - timer_handle& timer = timers[static_cast(it->frontend->id)]; - // We move iterator already, in case, the current timer gets removed from the linked list. - ++it; + /// Dispatch timeout handling to the front-end side. + static bool trigger_timeout_handling(timer_handle& timer); - // If the timer does not expire yet, continue the iteration in the same wheel bucket. - if (cur_time != timer.backend.timeout) { - continue; - } + /// Handle timers for which the timeout handling dispatch failed and was postponed. + void handle_postponed_timeouts(); - // Stop timer. Note: callback has to see the timer has already expired. - stop_timer_backend(timer, true); - } -} + /// Start the ticking of a timer with a given duration. + void start_timer_backend(timer_handle& timer, timer_duration duration) + { + srsran_assert(timer.backend.state != state_t::running, "Invalid timer state"); + srsran_assert(timer.frontend != nullptr, "Invalid timer state"); -void timer_manager::create_timer_handle(std::unique_ptr timer) -{ - auto timer_idx = static_cast(timer->id); - srsran_assert(timer_idx >= timers.size() or timers[timer_idx].frontend == nullptr, "Duplicate timer id detection"); - if (timer_idx >= timers.size()) { - timers.resize(timer_idx + 1); + timer.backend.timeout = cur_time + std::max((unsigned)duration.count(), 1U); + timer.backend.state = state_t::running; + time_wheel[timer.backend.timeout & WHEEL_MASK].push_front(&timer); + ++nof_timers_running; } - timers[timer_idx].frontend = std::move(timer); -} -void timer_manager::handle_timer_creations() -{ - for (auto new_frontend = pending_events->pop_timer_to_create(); new_frontend != nullptr; - new_frontend = pending_events->pop_timer_to_create()) { - // Create new timer. - create_timer_handle(std::move(new_frontend)); + /// Called to destroy a timer context and return it back to the free timer pool. + void destroy_timer_backend(timer_handle& timer) + { + srsran_assert(timer.backend.state != state_t::running, "Destroying timer that is running not allowed"); + + // Clear frontend (it is already released by unique_timer). + timer.frontend->state = state_t::stopped; + timer.frontend->duration = INVALID_DURATION; + timer.frontend->timeout_callback = {}; + timer.frontend->exec = nullptr; + // Clear backend. + timer.backend.state = state_t::stopped; + timer.backend.timeout = 0; + // Add timer handle in free list. + timer_free_list.enqueue(timer.frontend.get()); } -} -void timer_manager::handle_timer_commands() + /// \brief Create a new front-end context to be used by a newly created unique_timer. + frontend_handle& create_frontend_timer(task_executor& exec); + + srslog::basic_logger& logger; + + /// Counter of the number of ticks elapsed. This counter gets incremented on every \c tick call. + unsigned cur_time = 0; + + /// Number of created timer_handle objects that are currently running. + size_t nof_timers_running = 0; + + /// \brief List of timer_handle objects currently being managed by the timer_manager class. The index of the element + /// in this container matches the id of the corresponding unique_timer object. + /// This container will only grow in size. "Deleted" unique_timers in the front-end side are just tagged as empty + /// in their respective timer_handle, in the back-end side. This allows already created timer_handle objects to be + /// reused for the creation of new unique_timers in the front-end. The fact that the timer_handle is not deleted + /// also avoids potential dangling pointer when the timer expiry callback is dispatched to the front-end execution + /// context. + // Note: we use a deque to maintain reference validity. + std::deque timers; + + // Pool of timers that can be reused. + moodycamel::ConcurrentQueue timer_free_list; + std::atomic next_timer_id{0}; + + /// \brief Timer wheel, which is circularly indexed via a running timer timeout. Collisions are resolved via an + /// intrusive linked list stored in the timer_handle objects. + /// For a number of running timers N, and uniform distribution of timeout values, the tick() complexity + /// should be O(N/WHEEL_SIZE). Thus, the performance should improve with a larger WHEEL_SIZE, at the expense of more + /// used memory. + std::vector> time_wheel; + + /// \brief This is a list of timers that already timeouted, but for some unforeseen reason, + /// it was not possible to dispatch their respective timeout handling to the timer front-end. + /// The timeout handling will be re-triggered in the next slot. + std::deque> failed_to_trigger_timers; + + /// Pending timers to be created in the backend. + moodycamel::ConcurrentQueue> pending_timers_to_create; + + /// Commands sent by the timer front-end to the backend. + timer_update_signaller timers_with_pending_events; +}; + +void timer_manager::manager_impl::handle_timer_commands() { // Dequeue timers with new updates to be processed in this tick. - span updated_timers = pending_events->pop_updated_timers(); + span updated_timers = timers_with_pending_events.pop_updated_timers(); for (timer_id_t tid : updated_timers) { const auto t_idx = static_cast(tid); if (t_idx >= timers.size() or timers[t_idx].frontend == nullptr) { // Timer has not been created yet. - handle_timer_creations(); + handle_pending_timer_creations(); if (t_idx >= timers.size() or timers[t_idx].frontend == nullptr) { // Unexpected - timer creation event is nowhere to be found. @@ -334,13 +432,13 @@ void timer_manager::handle_timer_commands() } } -void timer_manager::handle_timer_updates(timer_handle& timer) +void timer_manager::manager_impl::handle_timer_updates(timer_handle& timer) { srsran_assert(timer.frontend != nullptr, "Invalid timer frontend"); cmd_t cmd; - if (not timer.frontend->command_buffer.pop(cmd)) { - // this is unexpected. + if (not timer.frontend->backend_ch.pop(cmd)) { + // This shouldn't happen, but if it happens, it is not a big deal. logger.warning("New timer update signal for tid={} but no pending commands", fmt::underlying(timer.frontend->id)); return; } @@ -349,6 +447,7 @@ void timer_manager::handle_timer_updates(timer_handle& timer) timer.backend.cmd_id = cmd.cmd_id; if (std::holds_alternative(cmd.action)) { + // In case of new timer creation, there are no more actions. return; } @@ -363,36 +462,7 @@ void timer_manager::handle_timer_updates(timer_handle& timer) } } -void timer_manager::start_timer_backend(timer_handle& timer, timer_duration duration) -{ - srsran_assert(timer.backend.state != state_t::running, "Invalid timer state"); - srsran_assert(timer.frontend != nullptr, "Invalid timer state"); - - timer.backend.timeout = cur_time + std::max((unsigned)duration.count(), 1U); - timer.backend.state = state_t::running; - time_wheel[timer.backend.timeout & WHEEL_MASK].push_front(&timer); - ++nof_timers_running; -} - -bool timer_manager::trigger_timeout_handling(timer_handle& timer) -{ - return timer.frontend->exec->defer([frontend = timer.frontend.get(), expiry_epoch = timer.backend.cmd_id]() { - // In case, the timer state has not been updated since the task was dispatched (epoches match). - // Note: Now that we are in the same execution context as the timer frontend, the frontend cmd_id is precise. - if (frontend->cmd_id.load(std::memory_order_relaxed) == expiry_epoch) { - srsran_assert(frontend->state == state_t::running, "The timer can only expire if it was already running"); - // Update timer frontend state to expired. - frontend->state = state_t::expired; - - // Run callback if configured. - if (not frontend->timeout_callback.is_empty()) { - frontend->timeout_callback(frontend->id); - } - } - }); -} - -bool timer_manager::try_stop_timer_backend(timer_handle& timer, bool expiry_reason) +bool timer_manager::manager_impl::try_stop_timer_backend(timer_handle& timer, bool expiry_reason) { if (timer.backend.state != state_t::running) { return false; @@ -412,7 +482,7 @@ bool timer_manager::try_stop_timer_backend(timer_handle& timer, bool expiry_reas // In case of expiry, and the backend and frontend (estimate) epoches match, dispatch the callback to frontend // executor. - cmd_id_t current_cmd_id_estim = timer.frontend->cmd_id.load(std::memory_order_relaxed); + cmd_id_t current_cmd_id_estim = timer.frontend->backend_ch.current_cmd_id(); if (timer.backend.cmd_id == current_cmd_id_estim) { bool success = trigger_timeout_handling(timer); @@ -426,14 +496,31 @@ bool timer_manager::try_stop_timer_backend(timer_handle& timer, bool expiry_reas return true; } -void timer_manager::handle_postponed_timeouts() +bool timer_manager::manager_impl::trigger_timeout_handling(timer_handle& timer) +{ + return timer.frontend->exec->defer([frontend = timer.frontend.get(), expiry_epoch = timer.backend.cmd_id]() { + // In case, the timer state has not been updated since the task was dispatched (epoches match). + // Note: Now that we are in the same execution context as the timer frontend, the frontend cmd_id is precise. + if (frontend->backend_ch.current_cmd_id() == expiry_epoch) { + srsran_assert(frontend->state == state_t::running, "The timer can only expire if it was already running"); + // Update timer frontend state to expired. + frontend->state = state_t::expired; + + // Run callback if configured. + if (not frontend->timeout_callback.is_empty()) { + frontend->timeout_callback(frontend->id); + } + } + }); +} + +void timer_manager::manager_impl::handle_postponed_timeouts() { while (not failed_to_trigger_timers.empty()) { timer_handle& timer = timers[(size_t)failed_to_trigger_timers.front().first]; cmd_id_t prev_cmd_id = failed_to_trigger_timers.front().second; - if (timer.backend.cmd_id == prev_cmd_id and - timer.backend.cmd_id == timer.frontend->cmd_id.load(std::memory_order_relaxed) and + if (timer.backend.cmd_id == prev_cmd_id and timer.backend.cmd_id == timer.frontend->backend_ch.current_cmd_id() and not trigger_timeout_handling(timer)) { // Timeout handling dispatch failed again. No point in continuing loop. break; @@ -444,68 +531,81 @@ void timer_manager::handle_postponed_timeouts() } } -void timer_manager::stop_timer_backend(timer_handle& timer, bool expiry_reason) -{ - srsran_assert(timer.backend.state == state_t::running, "Stopping timer that was not running"); - try_stop_timer_backend(timer, expiry_reason); -} - -void timer_manager::destroy_timer_backend(timer_handle& timer) +frontend_handle& timer_manager::manager_impl::create_frontend_timer(task_executor& exec) { - srsran_assert(timer.backend.state != state_t::running, "Destroying timer that is running not allowed"); - - // Clear frontend (it is already released by unique_timer). - timer.frontend->state = state_t::stopped; - timer.frontend->duration = timer_manager::INVALID_DURATION; - timer.frontend->timeout_callback = {}; - timer.frontend->exec = nullptr; - // Clear backend. - timer.backend.state = state_t::stopped; - timer.backend.timeout = 0; - // Add timer handle in free list. - timer_pool->push(timer.frontend.get()); -} - -timer_manager::timer_frontend& timer_manager::create_frontend_timer(task_executor& exec) -{ - // Pop cached timer from pool. - timer_frontend* cached_timer = timer_pool->pop(); - if (cached_timer != nullptr) { + // Pop cached timer from pool of unused timers. + frontend_handle* cached_timer; + if (timer_free_list.try_dequeue(cached_timer)) { + srsran_assert(cached_timer != nullptr, "Invalid timer cached"); srsran_assert(cached_timer->exec == nullptr, "Reassignment of timer detected"); // Assign new executor to created timer. cached_timer->exec = &exec; return *cached_timer; } - // In case it fails to reuse a cached timer frontend object. Need to create a new one. + // In case it fails to reuse a cached timer frontend object, we create a new one. const auto id = (timer_id_t)next_timer_id.fetch_add(1, std::memory_order_relaxed); - auto new_handle = std::make_unique(*this, id); + auto new_handle = std::make_unique(id, timers_with_pending_events); new_handle->exec = &exec; cached_timer = new_handle.get(); // Forward created timer handle to the backend. - cmd_id_t new_cmd_id = new_handle->cmd_id.fetch_add(1, std::memory_order::memory_order_relaxed) + 1; - new_handle->command_buffer.push(cmd_t{new_handle->id, new_cmd_id, cmd_t::create{}}); - // Note: This cannot fail, otherwise the created timer "id" cannot be reused. - pending_events->push_new_timer(std::move(new_handle)); - push_timer_command(id); + pending_timers_to_create.enqueue(std::move(new_handle)); + cached_timer->backend_ch.push(cached_timer->id, cmd_t::create{}); return *cached_timer; } -void timer_manager::push_timer_command(timer_id_t tid) +// timer_manager methods. + +timer_manager::timer_manager(size_t capacity) : impl(std::make_unique(capacity)) {} + +timer_manager::~timer_manager() = default; + +void timer_manager::tick() { - pending_events->push_timer_update(tid); + // Dequeue new commands from the timer front-ends to be processed in this tick. + impl->handle_timer_commands(); + + // Re-trigger timeout for timers that failed to be triggered in the previous slot. + impl->handle_postponed_timeouts(); + + // Advance time. + ++impl->cur_time; + + // Process the timer runs which expire in this tick. + auto& wheel_list = impl->time_wheel[impl->cur_time & WHEEL_MASK]; + + // Iterate intrusive linked list of running timers with same wheel index. + for (auto it = wheel_list.begin(); it != wheel_list.end();) { + srsran_assert(it->frontend != nullptr, "invalid state of timer in timer wheel"); + manager_impl::timer_handle& timer = impl->timers[static_cast(it->frontend->id)]; + // We move iterator already, in case, the current timer gets removed from the linked list. + ++it; + + // If the timer does not expire yet, continue the iteration in the same wheel bucket. + if (impl->cur_time != timer.backend.timeout) { + continue; + } + + // Stop timer. Note: callback has to see the timer has already expired. + impl->stop_timer_backend(timer, true); + } } unique_timer timer_manager::create_unique_timer(task_executor& exec) { - return unique_timer(create_frontend_timer(exec)); + return unique_timer(impl->create_frontend_timer(exec)); } size_t timer_manager::nof_timers() const { - return timers.size() - std::min(timers.size(), timer_pool->size_approx()); + return impl->timers.size() - std::min(impl->timers.size(), impl->timer_free_list.size_approx()); +} + +size_t timer_manager::nof_running_timers() const +{ + return impl->nof_timers_running; } // unique_timer methods @@ -525,7 +625,7 @@ unique_timer::~unique_timer() void unique_timer::reset() { if (is_valid()) { - handle->destroy(); + static_cast(handle)->destroy(); handle = nullptr; } } @@ -533,24 +633,24 @@ void unique_timer::reset() void unique_timer::set(timer_duration duration, unique_function callback) { srsran_assert(is_valid(), "Trying to setup empty timer pimpl"); - handle->set(duration, std::move(callback)); + static_cast(handle)->set(duration, std::move(callback)); } void unique_timer::set(timer_duration duration) { srsran_assert(is_valid(), "Trying to setup empty timer pimpl"); - handle->set(duration); + static_cast(handle)->set(duration); } void unique_timer::run() { srsran_assert(is_valid(), "Starting invalid timer"); - handle->run(); + static_cast(handle)->run(); } void unique_timer::stop() { if (is_running()) { - handle->stop(); + static_cast(handle)->stop(); } } diff --git a/tests/unittests/mac/dl_sch_pdu_assembler_test.cpp b/tests/unittests/mac/dl_sch_pdu_assembler_test.cpp index e3ccdfc270..680ace761c 100644 --- a/tests/unittests/mac/dl_sch_pdu_assembler_test.cpp +++ b/tests/unittests/mac/dl_sch_pdu_assembler_test.cpp @@ -16,6 +16,7 @@ #include "srsran/support/bit_encoding.h" #include "srsran/support/executors/manual_task_worker.h" #include "srsran/support/test_utils.h" +#include #include using namespace srsran; From fcd3d0780fed40024a767c29e204e1054a8b9d07 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Tue, 7 Jan 2025 10:52:52 +0100 Subject: [PATCH 051/107] trx: fix compilation --- utils/trx_srsran/trx_srsran.cpp | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/utils/trx_srsran/trx_srsran.cpp b/utils/trx_srsran/trx_srsran.cpp index 0d9fd61b12..405d0c16a2 100644 --- a/utils/trx_srsran/trx_srsran.cpp +++ b/utils/trx_srsran/trx_srsran.cpp @@ -383,9 +383,13 @@ static int trx_srsran_start(TRXState* s1, const TRXDriverParams* p) // Check that all sampling rates for all channels are the same. span sampling_rates_frac = span(p->sample_rate, context.rf_port_count).last(context.rf_port_count - 1); - std::all_of(sampling_rates_frac.begin(), sampling_rates_frac.end(), [&](const TRXFraction& x) { - return x.num == p->sample_rate[0].num && x.den == p->sample_rate[0].den; - }); + srsran_assert(std::all_of(sampling_rates_frac.begin(), + sampling_rates_frac.end(), + [&](const TRXFraction& x) { + return x.num == sampling_rates_frac.front().num && + x.den == sampling_rates_frac.front().den; + }), + "Not all sampling rates are equal."); // Prepare configuration. radio_configuration::radio configuration = {}; From f522895b02baf0525c36d3124578e746d25004d0 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 20 Dec 2024 12:58:09 +0100 Subject: [PATCH 052/107] phy: optimize resource grid read in AVX and NEON for PUCCH F2 phy: review documentation --- .../pucch/pucch_demodulator_format2.cpp | 96 ++++++++++++++----- .../pucch/pucch_processor_benchmark.cpp | 2 +- .../pucch/pucch_demodulator_format2_test.cpp | 5 + .../pucch_processor_format1_vectortest.cpp | 6 +- .../pucch_processor_format2_vectortest.cpp | 2 +- .../dmrs_pucch_estimator_test.cpp | 2 +- .../dmrs_pusch_estimator_test.cpp | 5 + .../port_channel_estimator_test.cpp | 2 +- 8 files changed, 91 insertions(+), 29 deletions(-) diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp index 4a8213206b..ae934f1568 100644 --- a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp @@ -14,14 +14,68 @@ #include "pucch_demodulator_format2.h" #include "srsran/phy/support/mask_types.h" #include "srsran/phy/support/resource_grid_reader.h" +#include "srsran/srsvec/copy.h" + +#if defined(__AVX2__) +#include +#elif defined(__ARM_NEON) +#include +#endif using namespace srsran; -/// \brief Control data RE allocation pattern for PUCCH Format 2. -/// -/// Indicates the Resource Elements containing control data symbols within a PRB, as per TS38.211 Section 6.4.1.3.2.2. -static const re_prb_mask format2_prb_re_mask = - {true, false, true, true, false, true, true, false, true, true, false, true}; +/// \brief Extracts REs containing PUCCH Format 2 data from a block of contiguous PRBs. +/// \param[out] out Output buffer. +/// \param[in] in Input buffer containing data and DM-RS. +static inline void extract_re(span out, span in) +{ + srsran_assert(in.size() % NRE == 0, "Invalid output size."); + unsigned nof_prb = in.size() / NRE; + srsran_assert(out.size() == nof_prb * pucch_constants::FORMAT2_NOF_DATA_SC, + "Invalid output size (i.e., {}), expected {}.", + out.size(), + nof_prb * pucch_constants::FORMAT2_NOF_DATA_SC); + +#if defined(__AVX2__) + const int* in_ptr = reinterpret_cast(in.data()); + __m256i* out_ptr = reinterpret_cast<__m256i*>(out.data()); + __m256i data_idx = _mm256_setr_epi32(0, 2, 3, 5, 6, 8, 9, 11); + + for (unsigned i_prb = 0; i_prb != nof_prb; ++i_prb) { + // Gather data RE as 32-bit unsigned integers. + __m256i avx2_data = _mm256_i32gather_epi32(in_ptr, data_idx, 4); + in_ptr += NRE; + + // Store data REs. + _mm256_storeu_si256(out_ptr++, avx2_data); + } +#elif defined(__ARM_NEON) + const int* in_ptr = reinterpret_cast(in.data()); + int* out_ptr = reinterpret_cast(out.data()); + + for (unsigned i_prb = 0; i_prb != nof_prb; ++i_prb) { + // Load and deinterleave 12 REs as 32-bit unsigned integers. + int32x4x3_t data_prb_s32 = vld3q_s32(in_ptr); + in_ptr += NRE; + + // Discard second RE. + int32x4x2_t data_s32 = {data_prb_s32.val[0], data_prb_s32.val[2]}; + + // Interleave and store. + vst2q_s32(out_ptr, data_s32); + out_ptr += pucch_constants::FORMAT2_NOF_DATA_SC; + } +#else + for (unsigned k = 0, k_end = nof_prb * NRE, count = 0; k != k_end; ++k) { + // Skip DM-RS. + if (k % 3 == 1) { + continue; + } + + out[count++] = in[k]; + } +#endif +} void pucch_demodulator_format2::demodulate(span llr, const resource_grid_reader& grid, @@ -97,14 +151,6 @@ void pucch_demodulator_format2::get_data_re_ests(const resource_grid_reader& const channel_estimate& channel_ests, const pucch_demodulator::format2_configuration& config) { - // Prepare RB mask. RB allocation is contiguous for PUCCH Format 2. - bounded_bitset prb_mask; - prb_mask.resize(config.nof_prb); - prb_mask.fill(0, config.nof_prb, true); - - // Prepare RE mask. - bounded_bitset re_mask = prb_mask.kronecker_product(format2_prb_re_mask); - for (unsigned i_port = 0, i_port_end = config.rx_ports.size(); i_port != i_port_end; ++i_port) { // Get a view of the data RE destination buffer for a single Rx port. span re_port_buffer = ch_re.get_slice(i_port); @@ -122,17 +168,23 @@ void pucch_demodulator_format2::get_data_re_ests(const resource_grid_reader& first_subc = config.second_hop_prb.value() * NRE; } - // Extract data RE from the resource grid. - re_port_buffer = resource_grid.get(re_port_buffer, i_port, i_symbol, first_subc, re_mask); + // Extract data RE view from the resource grid. + span grid_view = + resource_grid.get_view(i_port, i_symbol).subspan(first_subc, config.nof_prb * NRE); // View over the channel estimation coefficients for a single OFDM symbol. - span ests_symbol = channel_ests.get_symbol_ch_estimate(i_symbol, i_port); - - // Copy channel estimation coefficients of the data RE into the destination buffer. - re_mask.for_each(0, re_mask.size(), [&ests_port_buffer, &ests_symbol, &first_subc](unsigned bitpos) { - ests_port_buffer.front() = ests_symbol[first_subc + bitpos]; - ests_port_buffer = ests_port_buffer.last(ests_port_buffer.size() - 1); - }); + span ests_symbol = + channel_ests.get_symbol_ch_estimate(i_symbol, i_port).subspan(first_subc, config.nof_prb * NRE); + + // Extract data resource elements. + extract_re(re_port_buffer.first(config.nof_prb * pucch_constants::FORMAT2_NOF_DATA_SC), grid_view); + extract_re(ests_port_buffer.first(config.nof_prb * pucch_constants::FORMAT2_NOF_DATA_SC), ests_symbol); + + // Advance buffers. + re_port_buffer = + re_port_buffer.last(re_port_buffer.size() - config.nof_prb * pucch_constants::FORMAT2_NOF_DATA_SC); + ests_port_buffer = + ests_port_buffer.last(ests_port_buffer.size() - config.nof_prb * pucch_constants::FORMAT2_NOF_DATA_SC); } // Assert that all port data RE buffer elements have been filled. diff --git a/tests/benchmarks/phy/upper/channel_processors/pucch/pucch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pucch/pucch_processor_benchmark.cpp index 5d6e64d79f..012f92abcf 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pucch/pucch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pucch/pucch_processor_benchmark.cpp @@ -87,7 +87,7 @@ static std::unique_ptr> static std::unique_ptr> executor = nullptr; // Thread shared variables. -static constexpr auto thread_sync_sleep_duration = std::chrono::nanoseconds(100U); +static constexpr auto thread_sync_sleep_duration = std::chrono::nanoseconds(10U); static std::atomic thread_quit = {}; static std::atomic pending_count = {0}; static std::atomic finish_count = {0}; diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format2_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format2_test.cpp index b8f87a1bd0..051f63d257 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format2_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_demodulator_format2_test.cpp @@ -65,6 +65,11 @@ class PucchDemodulatorFixture : public ::testing::TestWithParam demodulator; + // Default constructor - initializes the resource grid with the maximum size possible. + PucchDemodulatorFixture() : ::testing::TestWithParam(), rg_spy(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_NOF_PRBS) + { + } + static void SetUpTestSuite() { if (!demodulator) { diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_format1_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_format1_vectortest.cpp index 97e97cdcf9..a2acc13ef9 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_format1_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_format1_vectortest.cpp @@ -66,7 +66,7 @@ namespace { TEST_P(PucchProcessorFormat1Fixture, FromVector) { // Prepare resource grid. - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_NOF_PRBS); grid.write(GetParam().grid.read()); const PucchProcessorFormat1Param& param = GetParam(); @@ -107,7 +107,7 @@ TEST_P(PucchProcessorFormat1Fixture, FromVector) TEST_P(PucchProcessorFormat1Fixture, FromVectorFalseCs) { // Prepare resource grid. - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_NOF_PRBS); grid.write(GetParam().grid.read()); // Get original parameters and change the initial cyclic shift. @@ -160,7 +160,7 @@ TEST_P(PucchProcessorFormat1Fixture, FalseAlarm) float acceptable_pfa = 0.1; // Prepare resource grid. - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_NOF_PRBS); unsigned counter = 0; for (unsigned i = 0; i != nof_trials; ++i) { grid.reset(); diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_format2_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_format2_vectortest.cpp index 01b0202d73..a16bf81439 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_format2_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_format2_vectortest.cpp @@ -47,7 +47,7 @@ TEST_P(PucchProcessorF2Fixture, PucchProcessorF2VectorTest) const pucch_processor::format2_configuration& config = context.config; // Prepare resource grid. - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_NOF_PRBS); grid.write(test_case.grid.read()); // Read expected UCI payload fields. diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pucch_estimator_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pucch_estimator_test.cpp index 35d72689f6..6e212ea9cd 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pucch_estimator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pucch_estimator_test.cpp @@ -163,7 +163,7 @@ TEST_P(DmrsPucchEstimatorFixture, DmrsPucchEstimatorTest) const std::vector testvector_symbols = test_case.symbols.read(); // Create resource grid - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_NOF_PRBS); grid.write(testvector_symbols); std::visit( diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp index e3c83116d6..7ee138e2ca 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp @@ -72,6 +72,11 @@ class DmrsPuschEstimatorFixture : public ::testing::TestWithParam std::unique_ptr estimator; resource_grid_reader_spy grid; + // Default constructor - initializes the resource grid with the maximum size possible. + DmrsPuschEstimatorFixture() : ::testing::TestWithParam(), grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_NOF_PRBS) + { + } + void SetUp() override { test_case_t test_case = GetParam(); diff --git a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp index 8043e134f8..6d2d75b463 100644 --- a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp @@ -167,7 +167,7 @@ TEST_P(ChannelEstFixture, test) pilot_read = pilot_read.last(pilot_read.size() - nof_dmrs_pilots); } - resource_grid_reader_spy grid; + resource_grid_reader_spy grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_NOF_PRBS); grid.write(grid_entries); ch_estimator->compute(estimates, grid, 0, pilots_arranged, cfg); From 0bb9385364dd1cfbeab196e54e2c8b117d3d8cd8 Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 3 Jan 2025 23:20:54 +0100 Subject: [PATCH 053/107] sched: add halt condition to stop allocating UEs to PDCCH --- lib/scheduler/cell/cell_harq_manager.cpp | 2 +- .../ue_scheduling/ue_cell_grid_allocator.cpp | 22 +++++++++++++++++-- 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/lib/scheduler/cell/cell_harq_manager.cpp b/lib/scheduler/cell/cell_harq_manager.cpp index 51c90bebbb..9eb84e4561 100644 --- a/lib/scheduler/cell/cell_harq_manager.cpp +++ b/lib/scheduler/cell/cell_harq_manager.cpp @@ -947,7 +947,7 @@ unsigned unique_ue_harq_entity::total_ul_bytes_waiting_ack() const } unsigned harq_bytes = 0; - for (unsigned i = 0; i != nof_ul_harqs(); ++i) { + for (unsigned i = 0, e = nof_ul_harqs(); i != e; ++i) { if (get_ul_ue().harqs[i].status != harq_utils::harq_state_t::empty) { harq_bytes += get_ul_ue().harqs[i].prev_tx_params.tbs_bytes; } diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index d7cd85193e..3b72ecd4e4 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -276,7 +276,16 @@ dl_alloc_result ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& if (pdcch == nullptr) { logger.info( "ue={} rnti={}: Failed to allocate PDSCH. Cause: No space in PDCCH.", fmt::underlying(u.ue_index), u.crnti); - return {alloc_status::skip_ue}; + // Note: (Implementation-defined) Assuming all UEs share the same CORESET, if there are no more CCEs left in the + // CORESET, stop attempting to allocate new PDCCHs in the slot. + unsigned nof_cces_left = ss_info.coreset->get_nof_cces(); + for (const auto& dl_pdcch : pdcch_alloc.result.dl.dl_pdcchs) { + nof_cces_left -= std::min(nof_cces_left, to_nof_cces(dl_pdcch.ctx.cces.aggr_lvl)); + } + for (const auto& ul_pdcch : pdcch_alloc.result.dl.ul_pdcchs) { + nof_cces_left -= std::min(nof_cces_left, to_nof_cces(ul_pdcch.ctx.cces.aggr_lvl)); + } + return {nof_cces_left == 0 ? alloc_status::skip_slot : alloc_status::skip_ue}; } // Allocate UCI. UCI destination (i.e., PUCCH or PUSCH) depends on whether there exist a PUSCH grant for the UE. @@ -806,7 +815,16 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice if (pdcch == nullptr) { logger.info( "ue={} rnti={}: Failed to allocate PUSCH. Cause: No space in PDCCH.", fmt::underlying(u.ue_index), u.crnti); - return {alloc_status::skip_ue}; + // Note: (Implementation-defined) Assuming all UEs share the same CORESET, if there are no more CCEs left in the + // CORESET, stop attempting to allocate new PDCCHs in the slot. + unsigned nof_cces_left = ss_info.coreset->get_nof_cces(); + for (const auto& dl_pdcch : pdcch_alloc.result.dl.dl_pdcchs) { + nof_cces_left -= std::min(nof_cces_left, to_nof_cces(dl_pdcch.ctx.cces.aggr_lvl)); + } + for (const auto& ul_pdcch : pdcch_alloc.result.dl.ul_pdcchs) { + nof_cces_left -= std::min(nof_cces_left, to_nof_cces(ul_pdcch.ctx.cces.aggr_lvl)); + } + return {nof_cces_left == 0 ? alloc_status::skip_slot : alloc_status::skip_ue}; } const unsigned nof_harq_ack_bits = From 632199bc66c5e3d5f51a087a8da2d2c945b88404 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Tue, 17 Dec 2024 11:23:04 +0100 Subject: [PATCH 054/107] phy: remove channel processors validators ASSERT_DEATH phy: fix formater --- .../channel_processors/pusch/formatters.h | 1 + .../pdsch/pdsch_processor_validator_test.cpp | 18 ----------------- ...pucch_processor_validator_format0_test.cpp | 9 --------- ...pucch_processor_validator_format1_test.cpp | 9 --------- ...pucch_processor_validator_format2_test.cpp | 10 ---------- ...pucch_processor_validator_format3_test.cpp | 12 ----------- ...pucch_processor_validator_format4_test.cpp | 13 ------------ .../pusch/pusch_processor_validator_test.cpp | 20 ------------------- 8 files changed, 1 insertion(+), 91 deletions(-) diff --git a/include/srsran/phy/upper/channel_processors/pusch/formatters.h b/include/srsran/phy/upper/channel_processors/pusch/formatters.h index a6aeb7cbb8..241b602ff8 100644 --- a/include/srsran/phy/upper/channel_processors/pusch/formatters.h +++ b/include/srsran/phy/upper/channel_processors/pusch/formatters.h @@ -16,6 +16,7 @@ #include "srsran/phy/upper/channel_state_information_formatters.h" #include "srsran/ran/pusch/pusch_context_formatter.h" #include "srsran/ran/uci/uci_formatters.h" +#include namespace srsran { namespace detail { diff --git a/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_validator_test.cpp b/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_validator_test.cpp index 2b226c2b8d..f752f7fd0a 100644 --- a/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_validator_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_validator_test.cpp @@ -8,8 +8,6 @@ * */ -#include "../../../support/resource_grid_mapper_test_doubles.h" -#include "../../rx_buffer_test_doubles.h" #include "pdsch_processor_test_doubles.h" #include "srsran/phy/support/support_factories.h" #include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" @@ -226,8 +224,6 @@ class pdschProcessorFixture : public ::testing::TestWithParam create_resource_grid_mapper_factory(precoding_factory); ASSERT_NE(rg_mapper_factory, nullptr); - resource_grid_writer_spy grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_RB); - // Create DM-RS for pdsch channel estimator. std::shared_ptr dmrs_pdsch_proc_factory = create_dmrs_pdsch_processor_factory_sw(prg_factory, rg_mapper_factory); @@ -284,20 +280,6 @@ TEST_P(pdschProcessorFixture, pdschProcessorValidatorDeathTest) ASSERT_FALSE(validator_out.has_value()) << "Validation should fail."; ASSERT_TRUE(std::regex_match(validator_out.error(), std::regex(param.expr))) << "The assertion message doesn't match the expected pattern."; - - // Prepare resource grid spy. - resource_grid_writer_spy grid(MAX_PORTS, MAX_NSYMB_PER_SLOT, MAX_RB); - - // Prepare receive data. - std::vector data; - - pdsch_processor_notifier_spy notifier_spy; - - // Process pdsch PDU. -#ifdef ASSERTS_ENABLED - ASSERT_DEATH({ pdsch_proc->process(grid, notifier_spy, {shared_transport_block(data)}, param.get_pdu()); }, - param.expr); -#endif // ASSERTS_ENABLED } // Creates test suite that combines all possible parameters. diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format0_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format0_test.cpp index 22117f1078..c7f57dd7e1 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format0_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format0_test.cpp @@ -8,7 +8,6 @@ * */ -#include "../../../support/resource_grid_test_doubles.h" #include "pucch_processor_test_fixture.h" #include "srsran/adt/expected.h" #include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" @@ -206,14 +205,6 @@ TEST_P(PucchProcessorFormat0Fixture, PucchProcessorValidatortest) ASSERT_FALSE(validator_out.has_value()) << "Validation should fail."; ASSERT_TRUE(std::regex_match(validator_out.error(), std::regex(param.get_test_params().assert_message))) << "The assertion message doesn't match the expected pattern."; - - // Prepare resource grid. - resource_grid_reader_spy grid; - - // Process PUCCH PDU. -#ifdef ASSERTS_ENABLED - ASSERT_DEATH({ processor->process(grid, param.get_test_params().config); }, param.get_test_params().assert_message); -#endif // ASSERTS_ENABLED } // Creates test suite that combines all possible parameters. diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format1_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format1_test.cpp index fd11c66d90..b6e003d0ac 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format1_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format1_test.cpp @@ -8,7 +8,6 @@ * */ -#include "../../../support/resource_grid_test_doubles.h" #include "pucch_processor_test_fixture.h" #include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" #include "srsran/phy/upper/channel_processors/pucch/factories.h" @@ -190,14 +189,6 @@ TEST_P(PucchProcessorFormat1Fixture, PucchProcessorValidatortest) ASSERT_FALSE(validator_out.has_value()) << "Validation should fail."; ASSERT_TRUE(std::regex_match(validator_out.error(), std::regex(param.get_test_params().assert_message))) << "The assertion message doesn't match the expected pattern."; - - // Prepare resource grid. - resource_grid_reader_spy grid; - - // Process PUCCH PDU. -#ifdef ASSERTS_ENABLED - ASSERT_DEATH({ processor->process(grid, param.get_test_params().config); }, param.get_test_params().assert_message); -#endif // ASSERTS_ENABLED } // Creates test suite that combines all possible parameters. diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format2_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format2_test.cpp index 1e260fbc28..86374b40bd 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format2_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format2_test.cpp @@ -8,9 +8,7 @@ * */ -#include "../../../support/resource_grid_test_doubles.h" #include "pucch_processor_test_fixture.h" - #include "srsran/adt/to_array.h" #include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" #include "srsran/phy/upper/channel_processors/pucch/factories.h" @@ -241,14 +239,6 @@ TEST_P(PucchProcessorFormat2Fixture, PucchProcessorValidatortest) ASSERT_FALSE(validator_out.has_value()) << "Validation should fail."; ASSERT_TRUE(std::regex_match(validator_out.error(), std::regex(param.get_test_params().assert_message))) << "The assertion message doesn't match the expected pattern."; - - // Prepare resource grid. - resource_grid_reader_spy grid; - - // Process PUCCH PDU. -#ifdef ASSERTS_ENABLED - ASSERT_DEATH({ processor->process(grid, param.get_test_params().config); }, param.get_test_params().assert_message); -#endif // ASSERTS_ENABLED } // Creates test suite that combines all possible parameters. diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format3_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format3_test.cpp index bbb1e54b45..60dd990c44 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format3_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format3_test.cpp @@ -8,14 +8,10 @@ * */ -#include "../../../support/resource_grid_test_doubles.h" #include "pucch_processor_test_fixture.h" #include "srsran/adt/to_array.h" -#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" #include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" -#include "srsran/phy/upper/channel_processors/pucch/factories.h" #include "srsran/phy/upper/channel_processors/pucch/formatters.h" -#include "srsran/phy/upper/equalization/equalization_factories.h" #include "srsran/ran/pucch/pucch_constants.h" #include "fmt/ostream.h" #include "gtest/gtest.h" @@ -248,14 +244,6 @@ TEST_P(PucchProcessorFormat3Fixture, PucchProcessorValidatortest) ASSERT_FALSE(validator_out.has_value()) << "Validation should fail."; ASSERT_TRUE(std::regex_match(validator_out.error(), std::regex(param.get_test_params().assert_message))) << "The assertion message doesn't match the expected pattern."; - - // Prepare resource grid. - resource_grid_reader_spy grid; - - // Process PUCCH PDU. -#ifdef ASSERTS_ENABLED - ASSERT_DEATH({ processor->process(grid, param.get_test_params().config); }, param.get_test_params().assert_message); -#endif // ASSERTS_ENABLED } // Creates test suite that combines all possible parameters. diff --git a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format4_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format4_test.cpp index 23b1225c9b..da85bbd725 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format4_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch/pucch_processor_validator_format4_test.cpp @@ -8,15 +8,10 @@ * */ -#include "../../../support/resource_grid_test_doubles.h" #include "pucch_processor_test_fixture.h" - #include "srsran/adt/to_array.h" -#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" #include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" -#include "srsran/phy/upper/channel_processors/pucch/factories.h" #include "srsran/phy/upper/channel_processors/pucch/formatters.h" -#include "srsran/phy/upper/equalization/equalization_factories.h" #include "srsran/ran/pucch/pucch_constants.h" #include "fmt/ostream.h" #include "gtest/gtest.h" @@ -233,14 +228,6 @@ TEST_P(PucchProcessorFormat4Fixture, PucchProcessorValidatortest) auto str = param.get_test_params().assert_message; ASSERT_TRUE(std::regex_match(validator_out.error(), std::regex(param.get_test_params().assert_message))) << "The assertion message doesn't match the expected pattern."; - - // Prepare resource grid. - resource_grid_reader_spy grid; - - // Process PUCCH PDU. -#ifdef ASSERTS_ENABLED - ASSERT_DEATH({ processor->process(grid, param.get_test_params().config); }, param.get_test_params().assert_message); -#endif // ASSERTS_ENABLED } // Creates test suite that combines all possible parameters. diff --git a/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_validator_test.cpp b/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_validator_test.cpp index e615b3689e..8ab09b43ca 100644 --- a/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_validator_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pusch/pusch_processor_validator_test.cpp @@ -8,9 +8,6 @@ * */ -#include "../../../support/resource_grid_test_doubles.h" -#include "../../rx_buffer_test_doubles.h" -#include "pusch_processor_result_test_doubles.h" #include "srsran/phy/upper/channel_processors/pusch/factories.h" #include "srsran/phy/upper/channel_processors/pusch/formatters.h" #include "srsran/phy/upper/equalization/equalization_factories.h" @@ -342,23 +339,6 @@ TEST_P(PuschProcessorFixture, PuschProcessorValidatortest) ASSERT_FALSE(validator_out.has_value()) << "Validation should fail."; ASSERT_TRUE(std::regex_match(validator_out.error(), std::regex(param.expr))) << "The assertion message doesn't match the expected pattern."; - - // Prepare resource grid. - resource_grid_reader_spy grid; - - // Prepare receive data. - std::vector data; - - // Prepare buffer. - rx_buffer_spy rm_buffer_spy(ldpc::MAX_CODEBLOCK_SIZE, 0); - unique_rx_buffer rm_buffer(rm_buffer_spy); - - // Process PUSCH PDU. -#ifdef ASSERTS_ENABLED - pusch_processor_result_notifier_spy result_notifier_spy; - ASSERT_DEATH({ pusch_proc->process(data, std::move(rm_buffer), result_notifier_spy, grid, param.get_pdu()); }, - param.expr); -#endif // ASSERTS_ENABLED } // Creates test suite that combines all possible parameters. From 086cd67ac5f5cb7bd11cb2cb17a3121c3e095312 Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 20 Dec 2024 13:13:47 +0100 Subject: [PATCH 055/107] sched: account for per-LCG UL GBR in the PF scheduler --- lib/scheduler/policy/scheduler_time_pf.cpp | 59 +++++++++++++++------ lib/scheduler/policy/scheduler_time_pf.h | 12 +++-- lib/scheduler/slicing/slice_ue_repository.h | 15 ++++++ 3 files changed, 65 insertions(+), 21 deletions(-) diff --git a/lib/scheduler/policy/scheduler_time_pf.cpp b/lib/scheduler/policy/scheduler_time_pf.cpp index 8ad3c96ef3..2f69a38b3f 100644 --- a/lib/scheduler/policy/scheduler_time_pf.cpp +++ b/lib/scheduler/policy/scheduler_time_pf.cpp @@ -286,30 +286,28 @@ static double compute_dl_rate_weight(const slice_ue& u, span dl_avg_rate } /// \brief Computes UL rate weight used in computation of UL priority value for a UE in a slot. -static double compute_ul_rate_weight(const slice_ue& u, double current_ue_ul_avg_rate, subcarrier_spacing bwp_scs) +static double compute_ul_rate_weight(const slice_ue& u, span ul_avg_rate_per_lcg, subcarrier_spacing bwp_scs) { span drbs_qos_info = u.get_drbs_qos_info(); // [Implementation-defined] Rate weight to assign if the UE has only non-GBR bearers or average UL rate of UE is 0. const double initial_rate_weight = 1; - double qos_gbr_rate_sum = 0; + double rate_weight = 0; // Compute sum of GBR rates of all LCs belonging to this slice. for (const sched_drb_info& drb_qos_info : drbs_qos_info) { // LC is not part of the slice or a non-GBR flow. if (not u.contains(drb_qos_info.lcid) and not drb_qos_info.gbr_qos_info.has_value()) { continue; } - qos_gbr_rate_sum += to_bytes_per_slot(drb_qos_info.gbr_qos_info->gbr_ul, bwp_scs); - } - if (qos_gbr_rate_sum == 0 or current_ue_ul_avg_rate == 0) { - return initial_rate_weight; + // GBR flow. + lcg_id_t lcg_id = u.get_lcg_id(drb_qos_info.lcid); + if (ul_avg_rate_per_lcg[lcg_id] != 0) { + rate_weight += (to_bytes_per_slot(drb_qos_info.gbr_qos_info->gbr_ul, bwp_scs) / ul_avg_rate_per_lcg[lcg_id]); + } } - // [Implementation-defined] Since scheduler does not have the information related to nof. bytes sent by UE for each - // LCG, the computation of rate weight is simplified by dividing the sum of GBR rates in all LC with average UL rate - // experienced by the UE. - return qos_gbr_rate_sum / current_ue_ul_avg_rate; + return rate_weight == 0 ? initial_rate_weight : rate_weight; } void scheduler_time_pf::ue_ctxt::compute_dl_prio(const slice_ue& u, @@ -400,7 +398,7 @@ void scheduler_time_pf::ue_ctxt::compute_ul_prio(const slice_ue& u, ul_prio = forbid_prio; // Process bytes allocated in previous slot and compute average. - compute_ul_avg_rate(); + compute_ul_avg_rate(u); const ue_cell* ue_cc = u.find_cell(cell_index); if (ue_cc == nullptr) { @@ -481,7 +479,7 @@ void scheduler_time_pf::ue_ctxt::compute_ul_prio(const slice_ue& u, pf_weight = estimated_rate == 0 ? 0 : std::numeric_limits::max(); } const double rate_weight = compute_ul_rate_weight( - u, current_avg_rate, ue_cc->cfg().cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params.scs); + u, ul_avg_rate_per_lcg, ue_cc->cfg().cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params.scs); ul_prio = rate_weight * pf_weight; sr_ind_received = u.has_pending_sr(); } @@ -530,21 +528,48 @@ void scheduler_time_pf::ue_ctxt::compute_dl_avg_rate(const slice_ue& u) dl_nof_samples++; } -void scheduler_time_pf::ue_ctxt::compute_ul_avg_rate() +void scheduler_time_pf::ue_ctxt::compute_ul_avg_rate(const slice_ue& u) { + // Redimension LCID arrays to the UE configured bearers. + ul_alloc_bytes_per_lcg.resize(u.get_lcgs().size(), 0); + ul_avg_rate_per_lcg.resize(ul_alloc_bytes_per_lcg.size(), 0); + + // Compute UL average rate on a per-logical channel group basis. + for (unsigned i = 0; i != ul_alloc_bytes_per_lcg.size(); ++i) { + if (not u.contains(uint_to_lcg_id(i))) { + // Skip LCIDs that are not configured. + ul_alloc_bytes_per_lcg[i] = 0; + ul_avg_rate_per_lcg[i] = 0; + } + + unsigned sched_bytes = ul_alloc_bytes_per_lcg[i]; + + if (ul_nof_samples < 1 / parent->exp_avg_alpha) { + // Fast start before transitioning to exponential average. + ul_avg_rate_per_lcg[i] += (sched_bytes - ul_avg_rate_per_lcg[i]) / (ul_nof_samples + 1); + } else { + ul_avg_rate_per_lcg[i] = + (1 - parent->exp_avg_alpha) * ul_avg_rate_per_lcg[i] + (parent->exp_avg_alpha) * sched_bytes; + } + + // Flush allocated bytes for the current slot. + ul_alloc_bytes_per_lcg[i] = 0; + } + + // Compute UL average rate of the UE. if (ul_nof_samples < 1 / parent->exp_avg_alpha) { // Fast start before transitioning to exponential average. - total_ul_avg_rate_ = total_ul_avg_rate_ + (ul_sum_alloc_bytes - total_ul_avg_rate_) / (ul_nof_samples + 1); + total_ul_avg_rate_ += (ul_sum_alloc_bytes - total_ul_avg_rate_) / (ul_nof_samples + 1); } else { total_ul_avg_rate_ = (1 - parent->exp_avg_alpha) * total_ul_avg_rate_ + (parent->exp_avg_alpha) * ul_sum_alloc_bytes; } - // Increment number of samples. - ul_nof_samples++; - // Flush allocated bytes for the current slot. ul_sum_alloc_bytes = 0; + + // Increment number of samples. + ul_nof_samples++; } void scheduler_time_pf::ue_ctxt::save_dl_alloc(uint32_t total_alloc_bytes, const dl_msg_tb_info& tb_info) diff --git a/lib/scheduler/policy/scheduler_time_pf.h b/lib/scheduler/policy/scheduler_time_pf.h index dc8c7511ba..ff120613ca 100644 --- a/lib/scheduler/policy/scheduler_time_pf.h +++ b/lib/scheduler/policy/scheduler_time_pf.h @@ -74,17 +74,21 @@ class scheduler_time_pf : public scheduler_policy private: void compute_dl_avg_rate(const slice_ue& u); - void compute_ul_avg_rate(); + void compute_ul_avg_rate(const slice_ue& u); // Sum of DL bytes allocated for a given slot, before it is taken into account in the average rate computation. static_vector dl_alloc_bytes_per_lc; unsigned dl_sum_alloc_bytes = 0; - // Sum of UL bytes allocated for a given slot, before it is taken into account in the average rate computation. - unsigned ul_sum_alloc_bytes = 0; // Average DL rate expressed in bytes per slot experienced by UE in each of its logical channel. - static_vector dl_avg_rate_per_lc; + static_vector dl_avg_rate_per_lc; // Average DL rate expressed in bytes per slot experienced by UE. double total_dl_avg_rate_ = 0; + // Sum of UL bytes allocated for a given slot, before it is taken into account in the average rate computation. + static_vector ul_alloc_bytes_per_lcg; + // Average UL rate expressed in bytes per slot experienced by UE in each of its logical channel. + static_vector ul_avg_rate_per_lcg; + // Sum of UL bytes allocated for a given slot, before it is taken into account in the average rate computation. + unsigned ul_sum_alloc_bytes = 0; // Average UL rate expressed in bytes per slot experienced by UE. double total_ul_avg_rate_ = 0; // Nof. DL samples over which average DL bitrate is computed. diff --git a/lib/scheduler/slicing/slice_ue_repository.h b/lib/scheduler/slicing/slice_ue_repository.h index 074c8e6270..11e8360c3e 100644 --- a/lib/scheduler/slicing/slice_ue_repository.h +++ b/lib/scheduler/slicing/slice_ue_repository.h @@ -46,9 +46,24 @@ class slice_ue /// Fetches the bitmap of bearers belonging to this slice. const bounded_bitset& get_bearers() const { return bearers; } + /// Fetches the bitmap of active logical channel groups belonging to this slice. + const bounded_bitset& get_lcgs() const { return lcg_ids; } + + /// Fetches the logical channel group associated with a given LCID. + lcg_id_t get_lcg_id(lcid_t lcid) const + { + const auto& lchs = u.ue_cfg_dedicated()->logical_channels(); + auto it = + std::find_if(lchs.begin(), lchs.end(), [lcid](const logical_channel_config& lc) { return lc.lcid == lcid; }); + return it != lchs.end() ? it->lc_group : MAX_NOF_LCGS; + } + /// Determines if bearer with LCID is part of this slice. bool contains(lcid_t lcid) const { return bearers.size() > lcid and bearers.test(lcid); } + /// Determines if LCG is part of this slice. + bool contains(lcg_id_t lcg_id) const { return lcg_ids.size() > lcg_id and lcg_ids.test(lcg_id); } + /// Fetch DU cell index of UE's PCell. const ue_cell& get_pcell() const { return u.get_pcell(); } From 100a6e6391d4fb61492cd7aeadd0e9d7ff9c4fce Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 2 Jan 2025 17:40:15 +0100 Subject: [PATCH 056/107] sched: estimate UL bytes that were allocated to an UL grant for UL QOS purposes --- lib/scheduler/policy/scheduler_time_pf.cpp | 16 ++++++-- lib/scheduler/policy/scheduler_time_pf.h | 2 +- lib/scheduler/slicing/slice_ue_repository.cpp | 37 +++++++++++++++++++ lib/scheduler/slicing/slice_ue_repository.h | 5 +++ 4 files changed, 56 insertions(+), 4 deletions(-) diff --git a/lib/scheduler/policy/scheduler_time_pf.cpp b/lib/scheduler/policy/scheduler_time_pf.cpp index 2f69a38b3f..5ec66c6828 100644 --- a/lib/scheduler/policy/scheduler_time_pf.cpp +++ b/lib/scheduler/policy/scheduler_time_pf.cpp @@ -111,7 +111,6 @@ void scheduler_time_pf::dl_sched(ue_pdsch_allocator& pdsch_alloc, while (not dl_queue.empty() and rem_rbs > 0 and alloc_result.status != alloc_status::skip_slot) { ue_ctxt& ue = *dl_queue.top(); alloc_result = try_dl_alloc(ue, ues, pdsch_alloc, rem_rbs); - ue.save_dl_alloc(alloc_result.alloc_bytes, alloc_result.tb_info); dl_queue.pop(); rem_rbs = slice_candidate.remaining_rbs(); } @@ -203,7 +202,6 @@ void scheduler_time_pf::ul_sched(ue_pusch_allocator& pusch_alloc, while (not ul_queue.empty() and rem_rbs > 0 and alloc_result.status != alloc_status::skip_slot) { ue_ctxt& ue = *ul_queue.top(); alloc_result = try_ul_alloc(ue, ues, pusch_alloc, rem_rbs); - ue.save_ul_alloc(alloc_result.alloc_bytes); ul_queue.pop(); rem_rbs = slice_candidate.remaining_rbs(); } @@ -224,6 +222,7 @@ dl_alloc_result scheduler_time_pf::try_dl_alloc(ue_ctxt& ctxt, if (alloc_result.status == alloc_status::success) { ctxt.dl_prio = forbid_prio; } + ctxt.save_dl_alloc(alloc_result.alloc_bytes, alloc_result.tb_info); return alloc_result; } @@ -242,6 +241,7 @@ ul_alloc_result scheduler_time_pf::try_ul_alloc(ue_ctxt& ctxt, if (alloc_result.status == alloc_status::success) { ctxt.ul_prio = forbid_prio; } + ctxt.save_ul_alloc(ues[ctxt.ue_index], alloc_result.alloc_bytes); return alloc_result; } @@ -589,8 +589,18 @@ void scheduler_time_pf::ue_ctxt::save_dl_alloc(uint32_t total_alloc_bytes, const dl_sum_alloc_bytes += total_alloc_bytes; } -void scheduler_time_pf::ue_ctxt::save_ul_alloc(uint32_t alloc_bytes) +void scheduler_time_pf::ue_ctxt::save_ul_alloc(const slice_ue& u, unsigned alloc_bytes) { + if (alloc_bytes == 0) { + return; + } + // We do not know which LCGs will be allocated by the UE, but we can make an estimation based on BSRs. + auto lcg_info = u.estimate_ul_alloc_bytes_per_lcg(alloc_bytes); + for (auto [lcgid, lcg_bytes] : lcg_info) { + if (lcgid < ul_alloc_bytes_per_lcg.size()) { + ul_alloc_bytes_per_lcg[lcgid] = lcg_bytes; + } + } ul_sum_alloc_bytes += alloc_bytes; } diff --git a/lib/scheduler/policy/scheduler_time_pf.h b/lib/scheduler/policy/scheduler_time_pf.h index ff120613ca..484e2704e2 100644 --- a/lib/scheduler/policy/scheduler_time_pf.h +++ b/lib/scheduler/policy/scheduler_time_pf.h @@ -58,7 +58,7 @@ class scheduler_time_pf : public scheduler_policy void compute_ul_prio(const slice_ue& u, ran_slice_id_t slice_id, slot_point pdcch_slot, slot_point pusch_slot); void save_dl_alloc(uint32_t total_alloc_bytes, const dl_msg_tb_info& tb_info); - void save_ul_alloc(uint32_t alloc_bytes); + void save_ul_alloc(const slice_ue& u, unsigned alloc_bytes); const du_ue_index_t ue_index; const du_cell_index_t cell_index; diff --git a/lib/scheduler/slicing/slice_ue_repository.cpp b/lib/scheduler/slicing/slice_ue_repository.cpp index 93fbe7a2eb..99a0b06025 100644 --- a/lib/scheduler/slicing/slice_ue_repository.cpp +++ b/lib/scheduler/slicing/slice_ue_repository.cpp @@ -103,6 +103,43 @@ bool slice_ue::has_pending_sr() const return u.has_pending_sr(); } +static_vector, MAX_NOF_LCGS> +slice_ue::estimate_ul_alloc_bytes_per_lcg(unsigned grant_size) const +{ + srsran_assert(grant_size > 0, "Invalid call with empty grant"); + static_vector, MAX_NOF_LCGS> result; + + // Compute the number of bytes that were already allocated in UL HARQs, before allocation. + unsigned bytes_in_harqs = 0; + for (unsigned c = 0, e = u.nof_cells(); c != e; ++c) { + const ue_cell& ue_cc = get_cell(to_ue_cell_index(c)); + bytes_in_harqs += ue_cc.harqs.total_ul_bytes_waiting_ack(); + } + bytes_in_harqs -= std::min(bytes_in_harqs, grant_size); + + // TODO: Use sorted LCG list for iteration. + for (unsigned lcgid = 0, e = lcg_ids.size(); lcgid != e and grant_size > 0; ++lcgid) { + if (not lcg_ids.test(lcgid)) { + continue; + } + // Get BSR for a given LCG ID. + unsigned lcg_bytes = std::min(u.pending_ul_newtx_bytes(uint_to_lcg_id(lcgid)), grant_size); + + // Only account for the BSR of a given LCG-ID after the bytes already allocated in HARQs are discounted. + if (bytes_in_harqs >= lcg_bytes) { + // We assum that this LCG-ID had already been allocated before in another HARQ. + bytes_in_harqs -= lcg_bytes; + continue; + } + lcg_bytes = lcg_bytes - bytes_in_harqs; + result.push_back(std::make_pair(uint_to_lcg_id(lcgid), lcg_bytes)); + bytes_in_harqs = 0; + grant_size -= lcg_bytes; + } + + return result; +} + lcg_id_t slice_ue::get_lcg_id_for_bearer(lcid_t lcid) const { const ue_configuration* ue_ded_cfg = u.ue_cfg_dedicated(); diff --git a/lib/scheduler/slicing/slice_ue_repository.h b/lib/scheduler/slicing/slice_ue_repository.h index 11e8360c3e..70c2bcd2ea 100644 --- a/lib/scheduler/slicing/slice_ue_repository.h +++ b/lib/scheduler/slicing/slice_ue_repository.h @@ -92,6 +92,11 @@ class slice_ue /// Get QoS information of DRBs configured for the UE. span get_drbs_qos_info() const { return u.ue_cfg_dedicated()->drbs_qos_info(); }; + /// Get an estimation of how many UL bytes were allocated per LCG for a given grant. + /// + /// Note: This function is called after the allocation has been made. + static_vector, MAX_NOF_LCGS> estimate_ul_alloc_bytes_per_lcg(unsigned grant_size) const; + private: /// Helper function to get LCG ID of a bearer. lcg_id_t get_lcg_id_for_bearer(lcid_t lcid) const; From 4144379b4feeec4f2610e4d6c127a3838b8b333d Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 18 Dec 2024 17:10:18 +0100 Subject: [PATCH 057/107] phy,du: add CFO to lower PHY --- .../flexible_o_du/flexible_o_du_commands.h | 46 ++++++++ .../split_helpers/flexible_o_du_factory.cpp | 1 + include/srsran/phy/lower/lower_phy.h | 7 ++ .../processors/downlink/downlink_processor.h | 8 +- .../processors/lower_phy_cfo_controller.h | 33 ++++++ .../processors/uplink/uplink_processor.h | 4 + include/srsran/ru/ru_controller.h | 12 ++ lib/phy/lower/lower_phy_impl.cpp | 10 ++ lib/phy/lower/lower_phy_impl.h | 6 + .../lower/processors/baseband_cfo_processor.h | 107 ++++++++++++++++++ .../downlink_processor_baseband_impl.cpp | 61 ++++++---- .../downlink_processor_baseband_impl.h | 8 +- .../downlink/downlink_processor_impl.cpp | 5 + .../downlink/downlink_processor_impl.h | 3 + .../uplink/uplink_processor_impl.cpp | 15 ++- .../processors/uplink/uplink_processor_impl.h | 8 ++ lib/ru/dummy/ru_dummy_impl.h | 6 + lib/ru/generic/ru_controller_generic_impl.cpp | 29 ++++- lib/ru/generic/ru_controller_generic_impl.h | 27 +++-- lib/ru/generic/ru_generic_impl.cpp | 16 +++ lib/ru/ofh/ru_ofh_controller_impl.h | 7 ++ .../downlink_processor_test_doubles.h | 10 ++ .../uplink_processor_notifier_test_doubles.h | 10 ++ 23 files changed, 397 insertions(+), 42 deletions(-) create mode 100644 include/srsran/phy/lower/processors/lower_phy_cfo_controller.h create mode 100644 lib/phy/lower/processors/baseband_cfo_processor.h diff --git a/apps/units/flexible_o_du/flexible_o_du_commands.h b/apps/units/flexible_o_du/flexible_o_du_commands.h index ff4a871091..9a350125b7 100644 --- a/apps/units/flexible_o_du/flexible_o_du_commands.h +++ b/apps/units/flexible_o_du/flexible_o_du_commands.h @@ -188,4 +188,50 @@ class change_log_level_app_command : public app_services::application_command } }; +/// Application command to set the carrier frequency offset. +class cfo_app_command : public app_services::application_command +{ + ru_controller& controller; + +public: + explicit cfo_app_command(ru_controller& controller_) : controller(controller_) {} + + // See interface for documentation. + std::string_view get_name() const override { return "cfo"; } + + // See interface for documentation. + std::string_view get_description() const override { return " : set CFO"; } + + // See interface for documentation. + void execute(span args) override + { + if (args.size() != 2) { + fmt::print("Invalid CFO command structure. Usage: cfo \n"); + return; + } + + expected sector_id = app_services::parse_int(args.front()); + if (not sector_id.has_value()) { + fmt::print("Invalid sector identifier.\n"); + return; + } + expected cfo = app_services::parse_double(args.back()); + if (not cfo.has_value()) { + fmt::print("Invalid CFO value.\n"); + return; + } + + if (!controller.set_tx_cfo(sector_id.value(), cfo.value())) { + fmt::print("Setting TX CFO was not successful. The radio may not support this feature.\n"); + return; + } + + if (!controller.set_rx_cfo(sector_id.value(), cfo.value())) { + fmt::print("Setting RX CFO was not successful. The radio may not support this feature.\n"); + return; + } + + fmt::print("CFO set to {}Hz for sector {}.\n", cfo.value(), sector_id.value()); + } +}; } // namespace srsran diff --git a/apps/units/flexible_o_du/split_helpers/flexible_o_du_factory.cpp b/apps/units/flexible_o_du/split_helpers/flexible_o_du_factory.cpp index cc45d0ca92..5b542760ec 100644 --- a/apps/units/flexible_o_du/split_helpers/flexible_o_du_factory.cpp +++ b/apps/units/flexible_o_du/split_helpers/flexible_o_du_factory.cpp @@ -112,6 +112,7 @@ o_du_unit flexible_o_du_factory::create_flexible_o_du(const o_du_unit_dependenci o_du.commands.push_back(std::make_unique(ru->get_controller())); o_du.commands.push_back(std::make_unique(ru->get_controller())); o_du.commands.push_back(std::make_unique(ru->get_controller())); + o_du.commands.push_back(std::make_unique(ru->get_controller())); // Configure the RU and DU in the dynamic DU. du_impl->add_ru(std::move(ru)); diff --git a/include/srsran/phy/lower/lower_phy.h b/include/srsran/phy/lower/lower_phy.h index c39d32ef9a..43b7d7dee8 100644 --- a/include/srsran/phy/lower/lower_phy.h +++ b/include/srsran/phy/lower/lower_phy.h @@ -18,6 +18,7 @@ class lower_phy_request_handler; class lower_phy_rg_handler; class lower_phy_rx_symbol_notifier; class lower_phy_timing_notifier; +class lower_phy_cfo_controller; /// \brief Lower PHY main interface. /// @@ -36,6 +37,12 @@ class lower_phy /// \brief Returns a reference to the lower PHY controller. virtual lower_phy_controller& get_controller() = 0; + + /// \brief Gets the transmit carrier frequency offset control. + virtual lower_phy_cfo_controller& get_tx_cfo_control() = 0; + + /// \brief Gets the receive carrier frequency offset control. + virtual lower_phy_cfo_controller& get_rx_cfo_control() = 0; }; } // namespace srsran diff --git a/include/srsran/phy/lower/processors/downlink/downlink_processor.h b/include/srsran/phy/lower/processors/downlink/downlink_processor.h index 022470c4b4..01ba4363f2 100644 --- a/include/srsran/phy/lower/processors/downlink/downlink_processor.h +++ b/include/srsran/phy/lower/processors/downlink/downlink_processor.h @@ -12,11 +12,12 @@ namespace srsran { -class downlink_processor_notifier; class downlink_processor_baseband; -class pdxch_processor_request_handler; class downlink_processor_notifier; +class downlink_processor_notifier; +class lower_phy_cfo_controller; class pdxch_processor_notifier; +class pdxch_processor_request_handler; /// \brief Downlink processor main interface. /// @@ -34,6 +35,9 @@ class lower_phy_downlink_processor /// Connects the downlink processor with notifiers. virtual void connect(downlink_processor_notifier& notifier, pdxch_processor_notifier& pdxch_notifier) = 0; + /// Gets the carrier frequency offset processor interface. + virtual lower_phy_cfo_controller& get_cfo_handler() = 0; + /// Gets the downlink processor request handler. virtual pdxch_processor_request_handler& get_downlink_request_handler() = 0; diff --git a/include/srsran/phy/lower/processors/lower_phy_cfo_controller.h b/include/srsran/phy/lower/processors/lower_phy_cfo_controller.h new file mode 100644 index 0000000000..fe7afc97ca --- /dev/null +++ b/include/srsran/phy/lower/processors/lower_phy_cfo_controller.h @@ -0,0 +1,33 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include + +namespace srsran { + +/// Lower physical layer carrier frequency processor interface. +class lower_phy_cfo_controller +{ +public: + /// Data type used for point in time. + using time_point = std::chrono::system_clock::time_point; + + /// Default destructor. + virtual ~lower_phy_cfo_controller() = default; + + /// \brief Schedules a CFO command. + /// \param time Time at which the CFO value is applied. + /// \param cfo_Hz New CFO value in Hertz. + virtual bool schedule_cfo_command(time_point time, float cfo_Hz) = 0; +}; + +} // namespace srsran diff --git a/include/srsran/phy/lower/processors/uplink/uplink_processor.h b/include/srsran/phy/lower/processors/uplink/uplink_processor.h index 5b3ab19ca7..a580a91358 100644 --- a/include/srsran/phy/lower/processors/uplink/uplink_processor.h +++ b/include/srsran/phy/lower/processors/uplink/uplink_processor.h @@ -18,6 +18,7 @@ class prach_processor_request_handler; class puxch_processor_request_handler; class prach_processor_notifier; class puxch_processor_notifier; +class lower_phy_cfo_controller; /// \brief Uplink processor main interface. /// @@ -43,6 +44,9 @@ class lower_phy_uplink_processor /// Gets the PUxCH request handler. virtual puxch_processor_request_handler& get_puxch_request_handler() = 0; + /// \brief Gets the carrier frequency offset processor interface. + virtual lower_phy_cfo_controller& get_cfo_handler() = 0; + /// \brief Gets the uplink processor baseband interface. /// \return A reference to the internal uplink processor baseband interface. virtual uplink_processor_baseband& get_baseband() = 0; diff --git a/include/srsran/ru/ru_controller.h b/include/srsran/ru/ru_controller.h index 4a0ed3e2fe..870c29dc85 100644 --- a/include/srsran/ru/ru_controller.h +++ b/include/srsran/ru/ru_controller.h @@ -44,6 +44,18 @@ class ru_controller /// \return \c true if the operation is successful, \c false otherwise. virtual bool set_rx_gain(unsigned port_id, double gain_dB) = 0; + /// \brief Sets the downlink carrier frequency offset for the specified sector. + /// \param[in] sector_id Sector identifier. + /// \param[in] cfo_hz Transmission CFO in Hz. + /// \return \c true if the operation is successful, \c false otherwise. + virtual bool set_tx_cfo(unsigned sector_id, float cfo_offset) = 0; + + /// \brief Sets the uplink carrier frequency offset for the specified sector. + /// \param[in] sector_id Sector identifier. + /// \param[in] cfo_hz Transmission CFO in Hz. + /// \return \c true if the operation is successful, \c false otherwise. + virtual bool set_rx_cfo(unsigned sector_id, float cfo_offset) = 0; + /// Prints RU specific metrics once. virtual void print_metrics() = 0; }; diff --git a/lib/phy/lower/lower_phy_impl.cpp b/lib/phy/lower/lower_phy_impl.cpp index 8ba06095bb..ec3a528b38 100644 --- a/lib/phy/lower/lower_phy_impl.cpp +++ b/lib/phy/lower/lower_phy_impl.cpp @@ -41,3 +41,13 @@ lower_phy_impl::lower_phy_impl(configuration& config) : notification_adaptor.get_prach_notifier(), notification_adaptor.get_puxch_notifier()); } + +lower_phy_cfo_controller& lower_phy_impl::get_tx_cfo_control() +{ + return downlink_proc->get_cfo_handler(); +} + +lower_phy_cfo_controller& lower_phy_impl::get_rx_cfo_control() +{ + return uplink_proc->get_cfo_handler(); +} diff --git a/lib/phy/lower/lower_phy_impl.h b/lib/phy/lower/lower_phy_impl.h index 9f26ac5eb0..135bba7300 100644 --- a/lib/phy/lower/lower_phy_impl.h +++ b/lib/phy/lower/lower_phy_impl.h @@ -67,6 +67,12 @@ class lower_phy_impl : public lower_phy // See interface for documentation. lower_phy_rg_handler& get_rg_handler() override { return handler_adaptor.get_rg_handler(); } + // See interface for documentation. + lower_phy_cfo_controller& get_tx_cfo_control() override; + + // See interface for documentation. + lower_phy_cfo_controller& get_rx_cfo_control() override; + /// Downlink processor. std::unique_ptr downlink_proc; /// Uplink processor. diff --git a/lib/phy/lower/processors/baseband_cfo_processor.h b/lib/phy/lower/processors/baseband_cfo_processor.h new file mode 100644 index 0000000000..3919f876f0 --- /dev/null +++ b/lib/phy/lower/processors/baseband_cfo_processor.h @@ -0,0 +1,107 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/adt/complex.h" +#include "srsran/adt/concurrent_queue.h" +#include "srsran/adt/expected.h" +#include "srsran/adt/ring_buffer.h" +#include "srsran/adt/span.h" +#include "srsran/phy/lower/processors/lower_phy_cfo_controller.h" +#include "srsran/phy/lower/sampling_rate.h" +#include "srsran/srsvec/prod.h" +#include "srsran/support/math/math_utils.h" +#include + +namespace srsran { + +/// \brief Baseband carrier frequency offset processor. +/// +/// Applies the configured carrier frequency offset to baseband signals. +class baseband_cfo_processor : public lower_phy_cfo_controller +{ +public: + baseband_cfo_processor(sampling_rate srate_) : srate(srate_) {} + + /// \brief Notifies a new CFO command. + /// \param time Time at which the new CFO value is used. + /// \param cfo_Hz New CFO value in Hertz. + bool schedule_cfo_command(time_point time, float cfo_Hz) override + { + cfo_command command{time, cfo_Hz}; + if (!cfo_command_queue.try_push(command)) { + return false; + } + return true; + } + + /// Reset sample offset and update the CFO if any command is queued. + void next_cfo_command() + { + // Reset the sample offset. + sample_offset = 0; + + // Skip if there are no commands. + if (cfo_command_queue.empty()) { + return; + } + + // Get the reference of the next command. + const cfo_command& command = *cfo_command_queue.begin(); + + // Get the current time. + auto now = std::chrono::system_clock::now(); + + // If the time for the command has come... + if (now >= command.first) { + // Update the current normalized CFO. + current_cfo = command.second / srate.to_Hz(); + + // Pop the current command. + cfo_command_queue.pop(); + } + } + + /// Increments the CFO sample offset by a number of samples. + void advance(unsigned nof_samples) { sample_offset += nof_samples; } + + /// Applies carrier frequency offset in-place to a baseband buffer. + void process(span buffer) const + { + // Skip CFO process if the current CFO is zero, NaN or infinity. + if (!std::isnormal(current_cfo)) { + return; + } + + // Calculate the initial phase of the block in radians. + float initial_phase = TWOPI * current_cfo * static_cast(sample_offset); + + // Apply CFO. + srsvec::prod_cexp(buffer, buffer, current_cfo, initial_phase); + } + +private: + /// CFO command data type. + using cfo_command = std::pair; + /// Maximum number of commands that can be enqueued. + static constexpr unsigned max_nof_commands = 128; + + /// Baseband sampling rate + sampling_rate srate; + /// Queue of CFO commands. + static_ring_buffer cfo_command_queue; + /// Current sample count for keeping the phase coherent between calls. + unsigned sample_offset = 0; + /// Current normalized CFO. + float current_cfo = 0.0; +}; + +} // namespace srsran diff --git a/lib/phy/lower/processors/downlink/downlink_processor_baseband_impl.cpp b/lib/phy/lower/processors/downlink/downlink_processor_baseband_impl.cpp index 07dd031c5b..02a3101fb1 100644 --- a/lib/phy/lower/processors/downlink/downlink_processor_baseband_impl.cpp +++ b/lib/phy/lower/processors/downlink/downlink_processor_baseband_impl.cpp @@ -31,7 +31,8 @@ downlink_processor_baseband_impl::downlink_processor_baseband_impl( nof_samples_per_subframe(config.rate.to_kHz()), nof_slots_per_subframe(get_nof_slots_per_subframe(config.scs)), nof_symbols_per_slot(get_nsymb_per_slot(config.cp)), - temp_buffer(config.nof_tx_ports, 2 * config.rate.get_dft_size(config.scs)) + temp_buffer(config.nof_tx_ports, 2 * config.rate.get_dft_size(config.scs)), + cfo_processor(config.rate) { unsigned symbol_size_no_cp = config.rate.get_dft_size(config.scs); unsigned nof_symbols_per_subframe = nof_symbols_per_slot * nof_slots_per_subframe; @@ -221,39 +222,49 @@ bool downlink_processor_baseband_impl::process_new_symbol(baseband_gateway_buffe bool processed = pdxch_proc_baseband.process_symbol(buffer, pdxch_context); + // Skip any post-processing if no signal is generated. if (!processed) { return false; } - // Process amplitude control. - for (unsigned i_port = 0, i_port_end = buffer.get_nof_channels(); i_port != i_port_end; ++i_port) { - amplitude_control.process(buffer[i_port], buffer[i_port]); + // Reset CFO processor initial phase at the first OFDM symbol. + if (i_symbol == 0) { + cfo_processor.next_cfo_command(); } - // Perform signal measurements. - { - sample_statistics avg_power; - sample_statistics peak_power; - lower_phy_baseband_metrics metrics; - unsigned nof_channels = buffer.get_nof_channels(); - - uint64_t total_processed_samples = 0; - uint64_t nof_clipped_samples = 0; - - for (unsigned i_channel = 0; i_channel != nof_channels; ++i_channel) { - span channel_buffer = buffer.get_channel_buffer(i_channel); - avg_power.update(srsvec::average_power(channel_buffer)); - peak_power.update(srsvec::max_abs_element(channel_buffer).second); - nof_clipped_samples += srsvec::count_if_part_abs_greater_than(channel_buffer, 0.95); - total_processed_samples += channel_buffer.size(); - } + // Init signal measurements. + sample_statistics avg_power; + sample_statistics peak_power; + lower_phy_baseband_metrics metrics; + uint64_t total_processed_samples = 0; + uint64_t nof_clipped_samples = 0; + + // Post process modulated signal. + for (unsigned i_port = 0, i_port_end = buffer.get_nof_channels(); i_port != i_port_end; ++i_port) { + // Select channel buffer for the transmit port. + span channel_buffer = buffer.get_channel_buffer(i_port); - metrics.avg_power = avg_power.get_mean(); - metrics.peak_power = peak_power.get_max(); - metrics.clipping = std::pair{nof_clipped_samples, total_processed_samples}; + // Perform carrier frequency offset in place. + cfo_processor.process(channel_buffer); - notifier->on_new_metrics(metrics); + // Process amplitude control. + amplitude_control.process(channel_buffer, channel_buffer); + + // Perform signal measurements. + avg_power.update(srsvec::average_power(channel_buffer)); + peak_power.update(srsvec::max_abs_element(channel_buffer).second); + nof_clipped_samples += srsvec::count_if_part_abs_greater_than(channel_buffer, 0.95); + total_processed_samples += channel_buffer.size(); } + // Notify signal metrics. + notifier->on_new_metrics(lower_phy_baseband_metrics{ + .avg_power = avg_power.get_mean(), + .peak_power = peak_power.get_mean(), + .clipping = std::pair{nof_clipped_samples, total_processed_samples}}); + + // Advance CFO processor number of samples. + cfo_processor.advance(buffer.get_nof_samples()); + return true; } diff --git a/lib/phy/lower/processors/downlink/downlink_processor_baseband_impl.h b/lib/phy/lower/processors/downlink/downlink_processor_baseband_impl.h index 303927ff66..76159ccdf6 100644 --- a/lib/phy/lower/processors/downlink/downlink_processor_baseband_impl.h +++ b/lib/phy/lower/processors/downlink/downlink_processor_baseband_impl.h @@ -10,6 +10,7 @@ #pragma once +#include "../baseband_cfo_processor.h" #include "srsran/adt/blocking_queue.h" #include "srsran/gateways/baseband/buffer/baseband_gateway_buffer_dynamic.h" #include "srsran/phy/lower/amplitude_controller/amplitude_controller.h" @@ -179,9 +180,12 @@ class downlink_processor_baseband_impl : public downlink_processor_baseband amplitude_controller& amplitude_control_, const downlink_processor_baseband_configuration& config); - // See interface for documentation. + /// Connect the processor to a notifier. void connect(downlink_processor_notifier& notifier_) { notifier = ¬ifier_; } + /// Gets the CFO processor control interface. + baseband_cfo_processor& get_cfo_control() { return cfo_processor; } + // See interface for documentation. baseband_gateway_transmitter_metadata process(baseband_gateway_buffer_writer& buffer, baseband_gateway_timestamp timestamp) override; @@ -221,6 +225,8 @@ class downlink_processor_baseband_impl : public downlink_processor_baseband detail::baseband_symbol_buffer temp_buffer; /// Last notified slot boundary. std::optional last_notified_slot; + /// Carrier Frequency Offset processor. + baseband_cfo_processor cfo_processor; }; } // namespace srsran diff --git a/lib/phy/lower/processors/downlink/downlink_processor_impl.cpp b/lib/phy/lower/processors/downlink/downlink_processor_impl.cpp index 7a2a43f2b5..f221b883b9 100644 --- a/lib/phy/lower/processors/downlink/downlink_processor_impl.cpp +++ b/lib/phy/lower/processors/downlink/downlink_processor_impl.cpp @@ -41,3 +41,8 @@ downlink_processor_baseband& downlink_processor_impl::get_baseband() { return downlink_proc_baseband; } + +baseband_cfo_processor& downlink_processor_impl::get_cfo_handler() +{ + return downlink_proc_baseband.get_cfo_control(); +} diff --git a/lib/phy/lower/processors/downlink/downlink_processor_impl.h b/lib/phy/lower/processors/downlink/downlink_processor_impl.h index 314932e85c..15099244d3 100644 --- a/lib/phy/lower/processors/downlink/downlink_processor_impl.h +++ b/lib/phy/lower/processors/downlink/downlink_processor_impl.h @@ -38,6 +38,9 @@ class downlink_processor_impl : public lower_phy_downlink_processor // See interface for documentation. void connect(downlink_processor_notifier& notifier, pdxch_processor_notifier& pdxch_notifier) override; + // See interface for documentation. + baseband_cfo_processor& get_cfo_handler() override; + // See interface for documentation. pdxch_processor_request_handler& get_downlink_request_handler() override; diff --git a/lib/phy/lower/processors/uplink/uplink_processor_impl.cpp b/lib/phy/lower/processors/uplink/uplink_processor_impl.cpp index 1e2febc7ce..54cc5fe4b7 100644 --- a/lib/phy/lower/processors/uplink/uplink_processor_impl.cpp +++ b/lib/phy/lower/processors/uplink/uplink_processor_impl.cpp @@ -37,7 +37,8 @@ lower_phy_uplink_processor_impl::lower_phy_uplink_processor_impl(std::unique_ptr current_symbol_index(0), temp_buffer(config.nof_rx_ports, 2 * config.rate.get_dft_size(config.scs)), prach_proc(std::move(prach_proc_)), - puxch_proc(std::move(puxch_proc_)) + puxch_proc(std::move(puxch_proc_)), + cfo_processor(config.rate) { srsran_assert(prach_proc, "Invalid PRACH processor."); srsran_assert(puxch_proc, "Invalid PUxCH processor."); @@ -225,12 +226,17 @@ void lower_phy_uplink_processor_impl::process_collecting(const baseband_gateway_ uint64_t total_processed_samples = 0; uint64_t nof_clipped_samples = 0; + // Process received signal before demodulation. for (unsigned i_channel = 0; i_channel != nof_channels; ++i_channel) { - span channel_buffer = temp_buffer.get_reader().get_channel_buffer(i_channel); + // Perform signal measurements. + span channel_buffer = temp_buffer.get_writer().get_channel_buffer(i_channel); avg_power.update(srsvec::average_power(channel_buffer)); peak_power.update(srsvec::max_abs_element(channel_buffer).second); nof_clipped_samples += srsvec::count_if_part_abs_greater_than(channel_buffer, 0.95); total_processed_samples += channel_buffer.size(); + + // Perform carrier frequency offset. + cfo_processor.process(channel_buffer); } metrics.avg_power = avg_power.get_mean(); @@ -260,3 +266,8 @@ void lower_phy_uplink_processor_impl::process_collecting(const baseband_gateway_ baseband_gateway_buffer_reader_view samples2(samples, nof_samples, nof_input_samples - nof_samples); process_symbol_boundary(samples2, timestamp + nof_samples); } + +baseband_cfo_processor& lower_phy_uplink_processor_impl::get_cfo_handler() +{ + return cfo_processor; +} diff --git a/lib/phy/lower/processors/uplink/uplink_processor_impl.h b/lib/phy/lower/processors/uplink/uplink_processor_impl.h index 7d46d7fa1a..916fef5f83 100644 --- a/lib/phy/lower/processors/uplink/uplink_processor_impl.h +++ b/lib/phy/lower/processors/uplink/uplink_processor_impl.h @@ -10,6 +10,7 @@ #pragma once +#include "../baseband_cfo_processor.h" #include "srsran/adt/tensor.h" #include "srsran/gateways/baseband/buffer/baseband_gateway_buffer_dynamic.h" #include "srsran/phy/lower/processors/uplink/prach/prach_processor.h" @@ -74,6 +75,11 @@ class lower_phy_uplink_processor_impl : public lower_phy_uplink_processor, priva }; // See interface for documentation. +public: + baseband_cfo_processor& get_cfo_handler() override; + +private: + // See interface for documentation. void process(const baseband_gateway_buffer_reader& samples, baseband_gateway_timestamp timestamp) override; /// \brief Processes samples in alignment state. @@ -130,6 +136,8 @@ class lower_phy_uplink_processor_impl : public lower_phy_uplink_processor, priva std::unique_ptr puxch_proc; /// Uplink processor notifier. uplink_processor_notifier* notifier = nullptr; + /// Carrier Frequency Offset processor. + baseband_cfo_processor cfo_processor; }; } // namespace srsran diff --git a/lib/ru/dummy/ru_dummy_impl.h b/lib/ru/dummy/ru_dummy_impl.h index c71b756f4c..d5f7d2a5b2 100644 --- a/lib/ru/dummy/ru_dummy_impl.h +++ b/lib/ru/dummy/ru_dummy_impl.h @@ -57,6 +57,12 @@ class ru_dummy_impl : public radio_unit, // See radio_unit interface for documentation. ru_uplink_plane_handler& get_uplink_plane_handler() override { return *this; } + // See ru_controller interface for documentation. + bool set_tx_cfo(unsigned port_id, float cfo_Hz) override { return false; } + + // See ru_controller interface for documentation. + bool set_rx_cfo(unsigned port_id, float cfo_Hz) override { return false; } + private: /// Possible internal states. enum class state : uint8_t { idle = 0, running, wait_stop, stopped }; diff --git a/lib/ru/generic/ru_controller_generic_impl.cpp b/lib/ru/generic/ru_controller_generic_impl.cpp index de95eed302..e4f7fcc39f 100644 --- a/lib/ru/generic/ru_controller_generic_impl.cpp +++ b/lib/ru/generic/ru_controller_generic_impl.cpp @@ -10,17 +10,22 @@ #include "ru_controller_generic_impl.h" #include "srsran/phy/lower/lower_phy_controller.h" +#include "srsran/phy/lower/processors/lower_phy_cfo_controller.h" #include "srsran/radio/radio_session.h" #include "srsran/support/math/math_utils.h" using namespace srsran; -ru_controller_generic_impl::ru_controller_generic_impl(std::vector low_phy_crtl_, - std::vector low_phy_metrics_, - radio_session& radio_, - double srate_MHz_) : +ru_controller_generic_impl::ru_controller_generic_impl(std::vector low_phy_crtl_, + std::vector low_phy_metrics_, + std::vector tx_cfo_control_, + std::vector rx_cfo_control_, + radio_session& radio_, + double srate_MHz_) : low_phy_crtl(std::move(low_phy_crtl_)), low_phy_metrics(std::move(low_phy_metrics_)), + tx_cfo_control(std::move(tx_cfo_control_)), + rx_cfo_control(std::move(rx_cfo_control_)), radio(radio_), srate_MHz(srate_MHz_) { @@ -63,6 +68,22 @@ bool ru_controller_generic_impl::set_rx_gain(unsigned port_id, double gain_dB) return radio.get_management_plane().set_rx_gain(port_id, gain_dB); } +bool ru_controller_generic_impl::set_tx_cfo(unsigned sector_id, float cfo_hz) +{ + if (sector_id < tx_cfo_control.size()) { + return tx_cfo_control[sector_id]->schedule_cfo_command(std::chrono::system_clock::now(), cfo_hz); + } + return false; +} + +bool ru_controller_generic_impl::set_rx_cfo(unsigned sector_id, float cfo_hz) +{ + if (sector_id < rx_cfo_control.size()) { + return rx_cfo_control[sector_id]->schedule_cfo_command(std::chrono::system_clock::now(), cfo_hz); + } + return false; +} + void ru_controller_generic_impl::print_metrics() { phy_metrics_adapter::print_header(); diff --git a/lib/ru/generic/ru_controller_generic_impl.h b/lib/ru/generic/ru_controller_generic_impl.h index 9fd322b459..67a2c7050f 100644 --- a/lib/ru/generic/ru_controller_generic_impl.h +++ b/lib/ru/generic/ru_controller_generic_impl.h @@ -21,15 +21,18 @@ namespace srsran { class lower_phy_controller; class lower_phy_metrics_notifier; class radio_session; +class lower_phy_cfo_controller; /// Radio Unit controller generic implementation. class ru_controller_generic_impl : public ru_controller { public: - ru_controller_generic_impl(std::vector low_phy_crtl_, - std::vector low_phy_metrics_, - radio_session& radio_, - double srate_MHz_); + ru_controller_generic_impl(std::vector low_phy_crtl_, + std::vector low_phy_metrics_, + std::vector tx_cfo_control_, + std::vector rx_cfo_control_, + radio_session& radio_, + double srate_MHz_); // See interface for documentation. void start() override; @@ -43,14 +46,22 @@ class ru_controller_generic_impl : public ru_controller // See interface for documentation. bool set_rx_gain(unsigned port_id, double gain_dB) override; + // See interface for documentation. + bool set_tx_cfo(unsigned port_id, float cfo_hz) override; + + // See interface for documentation. + bool set_rx_cfo(unsigned port_id, float cfo_hz) override; + // See interface for documentation. void print_metrics() override; private: - std::vector low_phy_crtl; - std::vector low_phy_metrics; - radio_session& radio; - const double srate_MHz; + std::vector low_phy_crtl; + std::vector low_phy_metrics; + std::vector tx_cfo_control; + std::vector rx_cfo_control; + radio_session& radio; + const double srate_MHz; }; } // namespace srsran diff --git a/lib/ru/generic/ru_generic_impl.cpp b/lib/ru/generic/ru_generic_impl.cpp index 9751a3d086..3189265180 100644 --- a/lib/ru/generic/ru_generic_impl.cpp +++ b/lib/ru/generic/ru_generic_impl.cpp @@ -36,6 +36,22 @@ ru_generic_impl::ru_generic_impl(ru_generic_impl_config&& config) : } return out; }(phy_metric_printer), + [](span> sectors) { + std::vector out; + for (auto& sector : sectors) { + out.push_back(§or->get_tx_cfo_control()); + srsran_assert(out.back(), "Invalid lower PHY controller"); + } + return out; + }(low_phy), + [](span> sectors) { + std::vector out; + for (auto& sector : sectors) { + out.push_back(§or->get_rx_cfo_control()); + srsran_assert(out.back(), "Invalid lower PHY controller"); + } + return out; + }(low_phy), *radio, config.srate_MHz), ru_downlink_hdlr([](span> sectors) { diff --git a/lib/ru/ofh/ru_ofh_controller_impl.h b/lib/ru/ofh/ru_ofh_controller_impl.h index f27b06b2ba..2c8592d9c7 100644 --- a/lib/ru/ofh/ru_ofh_controller_impl.h +++ b/lib/ru/ofh/ru_ofh_controller_impl.h @@ -38,6 +38,13 @@ class ru_ofh_controller_impl : public ru_controller // See interface for documentation. bool set_rx_gain(unsigned port_id, double gain_dB) override { return false; } + // See interface for documentation. + bool set_tx_cfo(unsigned port_id, float cfo_Hz) override { return false; } + + // See interface for documentation. + bool set_rx_cfo(unsigned port_id, float cfo_Hz) override { return false; } + + // See interface for documentation. void print_metrics() override; private: diff --git a/tests/unittests/phy/lower/processors/downlink/downlink_processor_test_doubles.h b/tests/unittests/phy/lower/processors/downlink/downlink_processor_test_doubles.h index a6589f3311..0eb743e6f9 100644 --- a/tests/unittests/phy/lower/processors/downlink/downlink_processor_test_doubles.h +++ b/tests/unittests/phy/lower/processors/downlink/downlink_processor_test_doubles.h @@ -16,6 +16,7 @@ #include "srsran/phy/lower/processors/downlink/downlink_processor_factories.h" #include "srsran/phy/lower/processors/downlink/pdxch/pdxch_processor_notifier.h" #include "srsran/phy/lower/processors/downlink/pdxch/pdxch_processor_request_handler.h" +#include "srsran/phy/lower/processors/lower_phy_cfo_controller.h" #include "srsran/phy/support/resource_grid_context.h" #include "srsran/srsvec/copy.h" #include @@ -83,6 +84,12 @@ class downlink_processor_baseband_spy : public downlink_processor_baseband std::vector entries; }; +class baseband_cfo_processor_spy : public lower_phy_cfo_controller +{ +public: + bool schedule_cfo_command(time_point time, float cfo_Hz) override { return false; } +}; + class lower_phy_downlink_processor_spy : public lower_phy_downlink_processor { public: @@ -104,6 +111,8 @@ class lower_phy_downlink_processor_spy : public lower_phy_downlink_processor pdxch_processor_notifier* get_pdxch_notifier() { return pdxch_notifier; } + baseband_cfo_processor_spy& get_cfo_handler() override { return cfo_processor_spy; } + const pdxch_processor_request_handler_spy& get_pdxch_proc_request_handler_spy() const { return pdxch_proc_request_handler_spy; @@ -123,6 +132,7 @@ class lower_phy_downlink_processor_spy : public lower_phy_downlink_processor downlink_processor_configuration config; downlink_processor_notifier* notifier; pdxch_processor_notifier* pdxch_notifier; + baseband_cfo_processor_spy cfo_processor_spy; pdxch_processor_request_handler_spy pdxch_proc_request_handler_spy; downlink_processor_baseband_spy downlink_proc_baseband_spy; }; diff --git a/tests/unittests/phy/lower/processors/uplink/uplink_processor_notifier_test_doubles.h b/tests/unittests/phy/lower/processors/uplink/uplink_processor_notifier_test_doubles.h index ae948f9efb..2ac669a1b4 100644 --- a/tests/unittests/phy/lower/processors/uplink/uplink_processor_notifier_test_doubles.h +++ b/tests/unittests/phy/lower/processors/uplink/uplink_processor_notifier_test_doubles.h @@ -14,6 +14,7 @@ #include "prach/prach_processor_test_doubles.h" #include "puxch/puxch_processor_test_doubles.h" #include "srsran/phy/lower/lower_phy_timing_context.h" +#include "srsran/phy/lower/processors/lower_phy_cfo_controller.h" #include "srsran/phy/lower/processors/uplink/uplink_processor.h" #include "srsran/phy/lower/processors/uplink/uplink_processor_baseband.h" #include "srsran/phy/lower/processors/uplink/uplink_processor_factories.h" @@ -69,6 +70,12 @@ class uplink_processor_baseband_spy : public uplink_processor_baseband std::vector entries; }; +class lower_phy_cfo_controller_spy : public lower_phy_cfo_controller +{ +public: + bool schedule_cfo_command(time_point time, float cfo_Hz) override { return false; } +}; + class lower_phy_uplink_processor_spy : public lower_phy_uplink_processor { public: @@ -83,6 +90,8 @@ class lower_phy_uplink_processor_spy : public lower_phy_uplink_processor puxch_notifier = &puxch_notifier_; } + lower_phy_cfo_controller& get_cfo_handler() override { return cfo_processor_spy; } + const uplink_processor_configuration& get_config() const { return config; } prach_processor_request_handler& get_prach_request_handler() override { return prach_req_handler_spy; } @@ -117,6 +126,7 @@ class lower_phy_uplink_processor_spy : public lower_phy_uplink_processor uplink_processor_notifier* notifier = nullptr; prach_processor_notifier* prach_notifier = nullptr; puxch_processor_notifier* puxch_notifier = nullptr; + lower_phy_cfo_controller_spy cfo_processor_spy; prach_processor_request_handler_spy prach_req_handler_spy; puxch_processor_request_handler_spy puxch_req_handler_spy; uplink_processor_baseband_spy uplink_proc_baseband_spy; From eafb3198e1d7f890eca2ea344c03e49ecc437e30 Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 3 Jan 2025 12:19:51 +0100 Subject: [PATCH 058/107] sched: remove mallocs from TA manager --- .../du_high/du_high_config_translators.cpp | 63 ++++++---------- include/srsran/scheduler/result/pdsch_info.h | 5 +- lib/mac/mac_dl/dl_sch_pdu_assembler.cpp | 4 +- lib/scheduler/ue_context/ta_manager.cpp | 75 ++++++++++++++----- lib/scheduler/ue_context/ta_manager.h | 18 +++-- lib/scheduler/ue_context/ue.cpp | 5 +- .../ue_scheduling/logical_channel_test.cpp | 4 +- .../ue_scheduling/ta_manager_test.cpp | 6 +- 8 files changed, 104 insertions(+), 76 deletions(-) diff --git a/apps/units/flexible_o_du/o_du_high/du_high/du_high_config_translators.cpp b/apps/units/flexible_o_du/o_du_high/du_high/du_high_config_translators.cpp index 235d1dac72..e08bb86ba9 100644 --- a/apps/units/flexible_o_du/o_du_high/du_high/du_high_config_translators.cpp +++ b/apps/units/flexible_o_du/o_du_high/du_high/du_high_config_translators.cpp @@ -597,56 +597,39 @@ std::vector srsran::generate_du_cell_config(const du_hig if (not out_cell.ue_ded_serv_cell_cfg.ul_config.has_value()) { out_cell.ue_ded_serv_cell_cfg.ul_config.emplace(); } - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.emplace(); + auto& ul_cfg = out_cell.ue_ded_serv_cell_cfg.ul_config.value(); + if (not ul_cfg.init_ul_bwp.pusch_cfg.has_value()) { + ul_cfg.init_ul_bwp.pusch_cfg.emplace(); } + auto& pusch_cfg = ul_cfg.init_ul_bwp.pusch_cfg.value(); // Set DMRS additional position. - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg->pusch_mapping_type_a_dmrs->additional_positions = + pusch_cfg.pusch_mapping_type_a_dmrs->additional_positions = uint_to_dmrs_additional_positions(base_cell.pusch_cfg.dmrs_add_pos); // Set UL MCS table. - out_cell.ue_ded_serv_cell_cfg.ul_config->init_ul_bwp.pusch_cfg->mcs_table = base_cell.pusch_cfg.mcs_table; + pusch_cfg.mcs_table = base_cell.pusch_cfg.mcs_table; // Configure PUSCH transform precoding. if (base_cell.pusch_cfg.enable_transform_precoding) { - pusch_config& pusch_cfg = out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value(); pusch_cfg.trans_precoder = pusch_config::transform_precoder::enabled; pusch_cfg.pusch_mapping_type_a_dmrs.value().trans_precoder_enabled.emplace( dmrs_uplink_config::transform_precoder_enabled{std::nullopt, false, false}); } - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.emplace(); - } - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg->emplace(); - } - if (not std::holds_alternative( - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value())) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.reset(); - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg->emplace(); - } - auto& b_offsets = std::get(out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()); + if (not pusch_cfg.uci_cfg.has_value()) { + pusch_cfg.uci_cfg.emplace(); + } + auto& uci_cfg = pusch_cfg.uci_cfg.value(); + if (not uci_cfg.beta_offsets_cfg.has_value()) { + uci_cfg.beta_offsets_cfg.emplace(); + uci_cfg.beta_offsets_cfg->emplace(); + } + if (not std::holds_alternative(uci_cfg.beta_offsets_cfg.value())) { + uci_cfg.beta_offsets_cfg.reset(); + uci_cfg.beta_offsets_cfg->emplace(); + } + auto& b_offsets = std::get(uci_cfg.beta_offsets_cfg.value()); b_offsets.beta_offset_ack_idx_1 = base_cell.pusch_cfg.beta_offset_ack_idx_1; b_offsets.beta_offset_ack_idx_2 = base_cell.pusch_cfg.beta_offset_ack_idx_2; b_offsets.beta_offset_ack_idx_3 = base_cell.pusch_cfg.beta_offset_ack_idx_3; @@ -656,12 +639,10 @@ std::vector srsran::generate_du_cell_config(const du_hig b_offsets.beta_offset_csi_p2_idx_2 = base_cell.pusch_cfg.beta_offset_csi_p2_idx_2; // Set PUSCH power control parameters. - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().pusch_pwr_ctrl.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().pusch_pwr_ctrl.emplace( - pusch_config::pusch_power_control{}); + if (not pusch_cfg.pusch_pwr_ctrl.has_value()) { + pusch_cfg.pusch_pwr_ctrl.emplace(pusch_config::pusch_power_control{}); } - auto& pusch_pwr_ctrl = - out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().pusch_pwr_ctrl.value(); + auto& pusch_pwr_ctrl = pusch_cfg.pusch_pwr_ctrl.value(); if (pusch_pwr_ctrl.p0_alphasets.empty()) { pusch_pwr_ctrl.p0_alphasets.emplace_back(); } diff --git a/include/srsran/scheduler/result/pdsch_info.h b/include/srsran/scheduler/result/pdsch_info.h index 488da332a1..30c9f52e7a 100644 --- a/include/srsran/scheduler/result/pdsch_info.h +++ b/include/srsran/scheduler/result/pdsch_info.h @@ -17,6 +17,7 @@ #include "srsran/ran/precoding/precoding_constants.h" #include "srsran/ran/rnti.h" #include "srsran/ran/slot_pdu_capacity_constants.h" +#include "srsran/ran/time_alignment_config.h" #include "srsran/scheduler/config/bwp_configuration.h" #include "srsran/scheduler/harq_id.h" #include "srsran/scheduler/result/dmrs_info.h" @@ -94,8 +95,8 @@ using dummy_ce_payload = unsigned; /// Timing Advance Command CE payload. struct ta_cmd_ce_payload { - uint8_t tag_id; - unsigned ta_cmd; + time_alignment_group::id_t tag_id; + unsigned ta_cmd; }; struct dl_msg_lc_info { diff --git a/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp b/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp index 409c40272b..5aa696f3af 100644 --- a/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp +++ b/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp @@ -164,7 +164,7 @@ void dl_sch_pdu::add_tag_cmd(const ta_cmd_ce_payload& ce_payload) encode_subheader(false, lcid_dl_sch_t::TA_CMD, header_len, payload_len); // Encode Timing Advance Command. - pdu[byte_offset++] = (ce_payload.tag_id & 0xc0U) | (ce_payload.ta_cmd & 0x3fU); + pdu[byte_offset++] = (ce_payload.tag_id.value() & 0xc0U) | (ce_payload.ta_cmd & 0x3fU); } void dl_sch_pdu::add_padding(unsigned len) @@ -254,7 +254,7 @@ class dl_sch_pdu_assembler::pdu_log_builder fmt::format_to(std::back_inserter(fmtbuf), "{}TA_CMD: tag_id={}, ta_cmd={}", separator(), - ce_payload.tag_id, + ce_payload.tag_id.value(), ce_payload.ta_cmd); } diff --git a/lib/scheduler/ue_context/ta_manager.cpp b/lib/scheduler/ue_context/ta_manager.cpp index 6dba0da98a..d613940312 100644 --- a/lib/scheduler/ue_context/ta_manager.cpp +++ b/lib/scheduler/ue_context/ta_manager.cpp @@ -15,12 +15,20 @@ using namespace srsran; ta_manager::ta_manager(const scheduler_ue_expert_config& expert_cfg_, subcarrier_spacing ul_scs_, + time_alignment_group::id_t pcell_tag_id, dl_logical_channel_manager* dl_lc_ch_mgr_) : - ul_scs(ul_scs_), dl_lc_ch_mgr(dl_lc_ch_mgr_), expert_cfg(expert_cfg_), state(state_t::idle) + ul_scs(ul_scs_), + dl_lc_ch_mgr(dl_lc_ch_mgr_), + expert_cfg(expert_cfg_), + logger(srslog::fetch_basic_logger("SCHED")), + state(state_t::idle) { if (expert_cfg.ta_cmd_offset_threshold < 0) { state = state_t::disabled; + return; } + + update_tags(std::array{pcell_tag_id}); } void ta_manager::handle_ul_n_ta_update_indication(time_alignment_group::id_t tag_id, int64_t n_ta_diff_, float ul_sinr) @@ -30,7 +38,24 @@ void ta_manager::handle_ul_n_ta_update_indication(time_alignment_group::id_t tag // NOTE: From the testing with COTS UE its observed that N_TA update measurements with UL SINR less than 10 dB were // majorly outliers. if (state == state_t::measure and ul_sinr > expert_cfg.ta_update_measurement_ul_sinr_threshold) { - tag_n_ta_diff_measurements[tag_id.value()].emplace_back(n_ta_diff_); + // Note: Linear search is faster than binary for very small arrays. + auto it = std::find_if( + n_ta_reports.begin(), n_ta_reports.end(), [tag_id](const auto& meas) { return meas.tag_id == tag_id; }); + if (it != n_ta_reports.end() and it->tag_id == tag_id) { + n_ta_reports[tag_id.value()].samples.emplace_back(n_ta_diff_); + } else { + logger.warning("Discarding TA report. Cause: TAG Id {} is not configured", tag_id.value()); + } + } +} + +void ta_manager::update_tags(span tag_ids) +{ + n_ta_reports.resize(tag_ids.size()); + for (unsigned i = 0, e = n_ta_reports.size(); i != e; ++i) { + n_ta_reports[i].tag_id = tag_ids[i]; + n_ta_reports[i].samples.clear(); + n_ta_reports[i].samples.reserve(expert_cfg.ta_measurement_slot_period); } } @@ -46,6 +71,7 @@ void ta_manager::slot_indication(slot_point current_sl) if (state == state_t::idle) { meas_start_time = current_sl; state = state_t::measure; + return; } // Early return if measurement interval is short. @@ -53,15 +79,16 @@ void ta_manager::slot_indication(slot_point current_sl) return; } - for (uint8_t tag_id = 0; tag_id < tag_n_ta_diff_measurements.size(); ++tag_id) { - if (tag_n_ta_diff_measurements[tag_id].empty()) { + for (uint8_t tag_idx = 0; tag_idx < n_ta_reports.size(); ++tag_idx) { + if (n_ta_reports[tag_idx].samples.empty()) { continue; } + const time_alignment_group::id_t tag_id = n_ta_reports[tag_idx].tag_id; // Send Timing Advance command only if the offset is equal to or greater than the threshold. // The new Timing Advance Command is a value ranging from [0,...,63] as per TS 38.213, clause 4.2. Hence, we // need to subtract a value of 31 (as per equation in the same clause) to get the change in Timing Advance Command. - const unsigned new_t_a = compute_new_t_a(compute_avg_n_ta_difference(tag_id)); + const unsigned new_t_a = compute_new_t_a(compute_avg_n_ta_difference(tag_idx)); if (abs((int)new_t_a - ta_cmd_offset_zero) >= expert_cfg.ta_cmd_offset_threshold) { // Send Timing Advance Command to UE. dl_lc_ch_mgr->handle_mac_ce_indication( @@ -69,36 +96,44 @@ void ta_manager::slot_indication(slot_point current_sl) } // Reset stored measurements. - reset_measurements(tag_id); + reset_measurements(tag_idx); } // Set TA manager state to idle. state = state_t::idle; } -int64_t ta_manager::compute_avg_n_ta_difference(uint8_t tag_id) +int64_t ta_manager::compute_avg_n_ta_difference(unsigned tag_idx) { // Adjust this threshold as needed. - static const double num_std_deviations = 1.95; + static const double num_std_deviations = 1.75; - const span n_ta_diff_meas = tag_n_ta_diff_measurements[tag_id]; + span samples = n_ta_reports[tag_idx].samples; + if (samples.size() == 1) { + return samples.front(); + } + if (samples.size() == 2) { + return (samples[0] + samples[1]) / 2; + } // Compute mean. - const double sum = std::accumulate(n_ta_diff_meas.begin(), n_ta_diff_meas.end(), 0.0); - const double mean = sum / static_cast(n_ta_diff_meas.size()); + const double sum = std::accumulate(samples.begin(), samples.end(), 0.0); + const double mean = sum / static_cast(samples.size()); // Compute standard deviation. - std::vector diff(n_ta_diff_meas.size()); - std::transform( - n_ta_diff_meas.begin(), n_ta_diff_meas.end(), diff.begin(), [mean](double value) { return value - mean; }); - const double sq_sum = std::inner_product(diff.begin(), diff.end(), diff.begin(), 0.0); - const double std_dev = std::sqrt(sq_sum / static_cast(n_ta_diff_meas.size())); + const double sample_variance = + std::accumulate(samples.begin(), + samples.end(), + 0.0, + [mean](double acc, int64_t samp) { return acc + std::pow(samp - mean, 2); }) / + (samples.size() - 1); + const double sample_std_dev = std::sqrt(sample_variance); int64_t sum_n_ta_difference = 0; unsigned count = 0; - for (const int64_t meas : n_ta_diff_meas) { + for (const int64_t meas : samples) { // Filter out outliers. - if (std::abs((double)meas - mean) <= num_std_deviations * std_dev) { + if (std::abs((double)meas - mean) <= num_std_deviations * sample_std_dev) { sum_n_ta_difference += meas; ++count; } @@ -114,7 +149,7 @@ unsigned ta_manager::compute_new_t_a(int64_t n_ta_diff) static_cast(ta_cmd_offset_zero) - expert_cfg.ta_target)); } -void ta_manager::reset_measurements(uint8_t tag_id) +void ta_manager::reset_measurements(unsigned tag_idx) { - tag_n_ta_diff_measurements[tag_id].clear(); + n_ta_reports[tag_idx].samples.clear(); } diff --git a/lib/scheduler/ue_context/ta_manager.h b/lib/scheduler/ue_context/ta_manager.h index 8394babe28..d63a9c3cb6 100644 --- a/lib/scheduler/ue_context/ta_manager.h +++ b/lib/scheduler/ue_context/ta_manager.h @@ -11,11 +11,10 @@ #pragma once #include "dl_logical_channel_manager.h" -#include "srsran/adt/static_vector.h" -#include "srsran/ran/phy_time_unit.h" #include "srsran/ran/slot_point.h" #include "srsran/ran/time_alignment_config.h" #include "srsran/scheduler/config/scheduler_expert_config.h" +#include "srsran/srslog/srslog.h" #include namespace srsran { @@ -26,8 +25,11 @@ class ta_manager public: explicit ta_manager(const scheduler_ue_expert_config& expert_cfg_, subcarrier_spacing ul_scs_, + time_alignment_group::id_t pcell_tag_id, dl_logical_channel_manager* dl_lc_ch_mgr_); + void update_tags(span tag_ids); + /// \brief Handles Timing Advance adaptation related tasks at slot indication. void slot_indication(slot_point current_sl); @@ -48,15 +50,20 @@ class ta_manager disabled, }; + struct tag_measurement { + time_alignment_group::id_t tag_id; + std::vector samples; + }; + /// \brief Computes the average of N_TA update measurements. - int64_t compute_avg_n_ta_difference(uint8_t tag_id); + int64_t compute_avg_n_ta_difference(unsigned tag_idx); /// \brief Computes new Timing Advance Command value (T_A) as per TS 38.213, clause 4.2. /// \return Timing Advance Command value. Values [0,...,63]. unsigned compute_new_t_a(int64_t n_ta_diff); /// \brief Resets stored N_TA update measurements. - void reset_measurements(uint8_t tag_id); + void reset_measurements(unsigned tag_idx); /// Subcarrier spacing of UL BWP for which Timing Advance Command is applicable. const subcarrier_spacing ul_scs; @@ -64,13 +71,14 @@ class ta_manager dl_logical_channel_manager* dl_lc_ch_mgr = nullptr; /// Expert config parameters used for UE scheduler. const scheduler_ue_expert_config& expert_cfg; + srslog::basic_logger& logger; /// Starting point of the measurement interval. slot_point meas_start_time; /// List of N_TA update (N_TA_new - N_TA_old value in T_C units) measurements maintained per Timing Advance Group. /// The array index corresponds to TAG ID. And, the corresponding array value (i.e. vector) holds N_TA update /// measurements for that TAG ID. - std::array, MAX_NOF_TIME_ALIGNMENT_GROUPS> tag_n_ta_diff_measurements; + std::vector n_ta_reports; /// State of the Timing Advance manager. state_t state; }; diff --git a/lib/scheduler/ue_context/ue.cpp b/lib/scheduler/ue_context/ue.cpp index f4bdb53c39..76feec9a3f 100644 --- a/lib/scheduler/ue_context/ue.cpp +++ b/lib/scheduler/ue_context/ue.cpp @@ -23,7 +23,10 @@ ue::ue(const ue_creation_command& cmd) : ue_ded_cfg(&cmd.cfg), pcell_harq_pool(cmd.pcell_harq_pool), logger(srslog::fetch_basic_logger("SCHED")), - ta_mgr(expert_cfg, cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params.scs, &dl_lc_ch_mgr), + ta_mgr(expert_cfg, + cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params.scs, + ue_ded_cfg->pcell_cfg().cfg_dedicated().tag_id, + &dl_lc_ch_mgr), drx(cell_cfg_common.ul_cfg_common.init_ul_bwp.generic_params.scs, cell_cfg_common.ul_cfg_common.init_ul_bwp.rach_cfg_common->ra_con_res_timer, cmd.cfg.drx_cfg(), diff --git a/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp b/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp index b531cf152e..3ccc4338df 100644 --- a/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp @@ -307,12 +307,12 @@ TEST(dl_logical_channel_test, assign_leftover_bytes_to_sdu_if_leftover_bytes_is_ TEST(dl_logical_channel_test, ta_cmd_mac_ce_gets_updated_if_already_in_pending_ces_queue) { dl_logical_channel_manager lch_mng; - const auto first_ta_cmd_ce_payload = ta_cmd_ce_payload{.tag_id = 0, .ta_cmd = 29}; + const auto first_ta_cmd_ce_payload = ta_cmd_ce_payload{.tag_id = time_alignment_group::id_t{0}, .ta_cmd = 29}; lch_mng.handle_mac_ce_indication({.ce_lcid = lcid_dl_sch_t::TA_CMD, .ce_payload = first_ta_cmd_ce_payload}); ASSERT_TRUE(lch_mng.has_pending_bytes()); ASSERT_TRUE(lch_mng.has_pending_ces()); - const auto second_ta_cmd_ce_payload = ta_cmd_ce_payload{.tag_id = 0, .ta_cmd = 33}; + const auto second_ta_cmd_ce_payload = ta_cmd_ce_payload{.tag_id = time_alignment_group::id_t{0}, .ta_cmd = 33}; lch_mng.handle_mac_ce_indication({.ce_lcid = lcid_dl_sch_t::TA_CMD, .ce_payload = second_ta_cmd_ce_payload}); ASSERT_TRUE(lch_mng.has_pending_bytes()); ASSERT_TRUE(lch_mng.has_pending_ces()); diff --git a/tests/unittests/scheduler/ue_scheduling/ta_manager_test.cpp b/tests/unittests/scheduler/ue_scheduling/ta_manager_test.cpp index 223797761d..035d97969f 100644 --- a/tests/unittests/scheduler/ue_scheduling/ta_manager_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ta_manager_test.cpp @@ -23,7 +23,7 @@ class ta_manager_tester : public ::testing::TestWithParam protected: ta_manager_tester() : ul_scs(GetParam() == duplex_mode::FDD ? subcarrier_spacing::kHz15 : subcarrier_spacing::kHz30), - ta_mgr(expert_cfg.ue, ul_scs, &dl_lc_ch_mgr), + ta_mgr(expert_cfg.ue, ul_scs, time_alignment_group::id_t{0}, &dl_lc_ch_mgr), current_sl(to_numerology_value(ul_scs), test_rgen::uniform_int(0, 10239)) { run_slot(); @@ -108,10 +108,10 @@ TEST_P(ta_manager_tester, ta_cmd_is_successfully_triggered) TEST_P(ta_manager_tester, verify_computed_new_ta_cmd_based_on_multiple_n_ta_diff_reported) { // Expected value. Average of ta_values_reported excluding the outlier 45. - const uint8_t expected_new_ta_cmd = 34; + const unsigned expected_new_ta_cmd = 34; // TA values used to compute N_TA diff to be reported. - const std::vector ta_values_reported = {34, 35, 45, 34, 33}; + const std::vector ta_values_reported = {34, 35, 43, 34, 33}; const float ul_sinr = expert_cfg.ue.ta_update_measurement_ul_sinr_threshold + 10; for (const auto ta : ta_values_reported) { ta_mgr.handle_ul_n_ta_update_indication( From 1475c97df78842084a012d01b6446beda094f97d Mon Sep 17 00:00:00 2001 From: frankist Date: Tue, 7 Jan 2025 10:47:10 +0100 Subject: [PATCH 059/107] sched: minor cast fixes --- lib/scheduler/ue_context/ta_manager.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/scheduler/ue_context/ta_manager.cpp b/lib/scheduler/ue_context/ta_manager.cpp index d613940312..f50195c4dc 100644 --- a/lib/scheduler/ue_context/ta_manager.cpp +++ b/lib/scheduler/ue_context/ta_manager.cpp @@ -79,7 +79,7 @@ void ta_manager::slot_indication(slot_point current_sl) return; } - for (uint8_t tag_idx = 0; tag_idx < n_ta_reports.size(); ++tag_idx) { + for (unsigned tag_idx = 0; tag_idx != n_ta_reports.size(); ++tag_idx) { if (n_ta_reports[tag_idx].samples.empty()) { continue; } @@ -133,7 +133,7 @@ int64_t ta_manager::compute_avg_n_ta_difference(unsigned tag_idx) unsigned count = 0; for (const int64_t meas : samples) { // Filter out outliers. - if (std::abs((double)meas - mean) <= num_std_deviations * sample_std_dev) { + if (std::abs(static_cast(meas) - mean) <= num_std_deviations * sample_std_dev) { sum_n_ta_difference += meas; ++count; } From db6c2f273cf32a241aba244ca87b417298aa891b Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Tue, 7 Jan 2025 15:48:53 +0100 Subject: [PATCH 060/107] phy: move PDCCH processor to a subdirectory --- .../srsran/fapi_adaptor/phy/messages/pdcch.h | 2 +- .../channel_processor_factories.h | 44 ---- .../channel_processor_formatters.h | 84 +------ .../channel_processors/pdcch/factories.h | 65 +++++ .../channel_processors/pdcch/formatters.h | 101 ++++++++ .../{ => pdcch}/pdcch_encoder.h | 0 .../{ => pdcch}/pdcch_modulator.h | 0 .../{ => pdcch}/pdcch_processor.h | 0 .../channel_processors/pucch/formatters.h | 2 - include/srsran/phy/upper/downlink_processor.h | 2 +- .../ran/pdcch/pdcch_context_formatter.h | 1 + .../upper/channel_processors/CMakeLists.txt | 14 +- .../channel_processor_factories.cpp | 235 ------------------ .../channel_processors/pdcch/CMakeLists.txt | 13 + .../channel_processors/pdcch/factories.cpp | 193 ++++++++++++++ .../pdcch/logging_pdcch_processor_decorator.h | 91 +++++++ .../{ => pdcch}/pdcch_encoder_impl.cpp | 0 .../{ => pdcch}/pdcch_encoder_impl.h | 2 +- .../{ => pdcch}/pdcch_modulator_impl.cpp | 0 .../{ => pdcch}/pdcch_modulator_impl.h | 2 +- .../{ => pdcch}/pdcch_processor_impl.cpp | 0 .../{ => pdcch}/pdcch_processor_impl.h | 6 +- .../{ => pdcch}/pdcch_processor_pool.h | 2 +- ...ownlink_processor_single_executor_impl.cpp | 2 + lib/phy/upper/upper_phy_factories.cpp | 1 + .../dmrs_pdsch_processor_benchmark.cpp | 14 +- tests/unittests/ofh/receiver/helpers.h | 1 - .../resource_grid_mapper_test_doubles.h | 38 --- .../upper/channel_processors/CMakeLists.txt | 22 +- .../channel_processors/pdcch/CMakeLists.txt | 42 ++++ .../{ => pdcch}/pdcch_encoder_test.cpp | 2 +- .../{ => pdcch}/pdcch_encoder_test_data.h | 4 +- .../pdcch/pdcch_encoder_test_data.tar.gz | 3 + .../{ => pdcch}/pdcch_encoder_test_doubles.h | 4 +- .../{ => pdcch}/pdcch_modulator_test.cpp | 3 +- .../{ => pdcch}/pdcch_modulator_test_data.h | 6 +- .../pdcch/pdcch_modulator_test_data.tar.gz | 3 + .../pdcch_modulator_test_doubles.h | 2 +- .../{ => pdcch}/pdcch_processor_test_data.h | 6 +- .../pdcch/pdcch_processor_test_data.tar.gz | 3 + .../pdcch_processor_test_doubles.h | 4 +- .../{ => pdcch}/pdcch_processor_unittest.cpp | 6 +- .../pdcch_processor_vectortest.cpp | 5 +- .../pdcch_encoder_test_data.tar.gz | 3 - .../pdcch_modulator_test_data.tar.gz | 3 - .../pdcch_processor_test_data.tar.gz | 3 - .../pdsch/pdsch_modulator_test.cpp | 1 - .../pdsch/pdsch_processor_vectortest.cpp | 1 - .../phy/upper/downlink_processor_test.cpp | 2 +- .../dmrs_pdcch_processor_test.cpp | 1 - .../dmrs_pdsch_processor_test.cpp | 1 - .../nzp_csi_rs_generator_test.cpp | 1 - 52 files changed, 553 insertions(+), 493 deletions(-) create mode 100644 include/srsran/phy/upper/channel_processors/pdcch/factories.h create mode 100644 include/srsran/phy/upper/channel_processors/pdcch/formatters.h rename include/srsran/phy/upper/channel_processors/{ => pdcch}/pdcch_encoder.h (100%) rename include/srsran/phy/upper/channel_processors/{ => pdcch}/pdcch_modulator.h (100%) rename include/srsran/phy/upper/channel_processors/{ => pdcch}/pdcch_processor.h (100%) create mode 100644 lib/phy/upper/channel_processors/pdcch/CMakeLists.txt create mode 100644 lib/phy/upper/channel_processors/pdcch/factories.cpp create mode 100644 lib/phy/upper/channel_processors/pdcch/logging_pdcch_processor_decorator.h rename lib/phy/upper/channel_processors/{ => pdcch}/pdcch_encoder_impl.cpp (100%) rename lib/phy/upper/channel_processors/{ => pdcch}/pdcch_encoder_impl.h (98%) rename lib/phy/upper/channel_processors/{ => pdcch}/pdcch_modulator_impl.cpp (100%) rename lib/phy/upper/channel_processors/{ => pdcch}/pdcch_modulator_impl.h (98%) rename lib/phy/upper/channel_processors/{ => pdcch}/pdcch_processor_impl.cpp (100%) rename lib/phy/upper/channel_processors/{ => pdcch}/pdcch_processor_impl.h (93%) rename lib/phy/upper/channel_processors/{ => pdcch}/pdcch_processor_pool.h (94%) delete mode 100644 tests/unittests/phy/support/resource_grid_mapper_test_doubles.h create mode 100644 tests/unittests/phy/upper/channel_processors/pdcch/CMakeLists.txt rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_encoder_test.cpp (95%) rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_encoder_test_data.h (97%) create mode 100644 tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_data.tar.gz rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_encoder_test_doubles.h (92%) rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_modulator_test.cpp (94%) rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_modulator_test_data.h (96%) create mode 100644 tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_data.tar.gz rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_modulator_test_doubles.h (93%) rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_processor_test_data.h (99%) create mode 100644 tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.tar.gz rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_processor_test_doubles.h (87%) rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_processor_unittest.cpp (97%) rename tests/unittests/phy/upper/channel_processors/{ => pdcch}/pdcch_processor_vectortest.cpp (94%) delete mode 100644 tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_data.tar.gz delete mode 100644 tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_data.tar.gz delete mode 100644 tests/unittests/phy/upper/channel_processors/pdcch_processor_test_data.tar.gz diff --git a/include/srsran/fapi_adaptor/phy/messages/pdcch.h b/include/srsran/fapi_adaptor/phy/messages/pdcch.h index 7572660ac6..3009221ccb 100644 --- a/include/srsran/fapi_adaptor/phy/messages/pdcch.h +++ b/include/srsran/fapi_adaptor/phy/messages/pdcch.h @@ -11,7 +11,7 @@ #pragma once #include "srsran/fapi/messages.h" -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" namespace srsran { namespace fapi_adaptor { diff --git a/include/srsran/phy/upper/channel_processors/channel_processor_factories.h b/include/srsran/phy/upper/channel_processors/channel_processor_factories.h index a1d3c743aa..905ee33e22 100644 --- a/include/srsran/phy/upper/channel_processors/channel_processor_factories.h +++ b/include/srsran/phy/upper/channel_processors/channel_processor_factories.h @@ -15,9 +15,6 @@ #include "srsran/phy/upper/channel_modulation/channel_modulation_factories.h" #include "srsran/phy/upper/channel_processors/pbch_encoder.h" #include "srsran/phy/upper/channel_processors/pbch_modulator.h" -#include "srsran/phy/upper/channel_processors/pdcch_encoder.h" -#include "srsran/phy/upper/channel_processors/pdcch_modulator.h" -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" #include "srsran/phy/upper/channel_processors/prach_detector.h" #include "srsran/phy/upper/channel_processors/prach_generator.h" #include "srsran/phy/upper/channel_processors/ssb_processor.h" @@ -52,47 +49,6 @@ std::shared_ptr create_pbch_modulator_factory_sw(std::shared_ptr, std::shared_ptr); -class pdcch_modulator_factory -{ -public: - virtual ~pdcch_modulator_factory() = default; - virtual std::unique_ptr create() = 0; -}; - -std::shared_ptr - create_pdcch_modulator_factory_sw(std::shared_ptr, - std::shared_ptr, - std::shared_ptr); - -class pdcch_encoder_factory -{ -public: - virtual ~pdcch_encoder_factory() = default; - virtual std::unique_ptr create() = 0; -}; - -std::shared_ptr -create_pdcch_encoder_factory_sw(std::shared_ptr crc_factory, - std::shared_ptr encoder_factory); - -class pdcch_processor_factory -{ -public: - virtual ~pdcch_processor_factory() = default; - virtual std::unique_ptr create() = 0; - virtual std::unique_ptr create_validator() = 0; - virtual std::unique_ptr create(srslog::basic_logger& logger, bool enable_logging_broadcast); -}; - -std::shared_ptr -create_pdcch_processor_factory_sw(std::shared_ptr encoder_factory, - std::shared_ptr modulator_factory, - std::shared_ptr dmrs_factory); - -std::shared_ptr -create_pdcch_processor_pool_factory(std::shared_ptr processor_factory, - unsigned nof_concurrent_threads); - class prach_detector_factory { public: diff --git a/include/srsran/phy/upper/channel_processors/channel_processor_formatters.h b/include/srsran/phy/upper/channel_processors/channel_processor_formatters.h index 121515657b..41b510def7 100644 --- a/include/srsran/phy/upper/channel_processors/channel_processor_formatters.h +++ b/include/srsran/phy/upper/channel_processors/channel_processor_formatters.h @@ -12,96 +12,14 @@ #include "srsran/phy/support/precoding_formatters.h" #include "srsran/phy/support/re_pattern_formatters.h" -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" #include "srsran/phy/upper/channel_processors/prach_detector.h" #include "srsran/phy/upper/channel_processors/ssb_processor.h" #include "srsran/phy/upper/channel_state_information_formatters.h" -#include "srsran/ran/pdcch/pdcch_context_formatter.h" #include "srsran/srsvec/copy.h" namespace fmt { -/// \brief Custom formatter for \c pdcch_processor::coreset_description. -template <> -struct formatter { - /// Helper used to parse formatting options and format fields. - srsran::delimited_formatter helper; - - /// Default constructor. - formatter() = default; - - template - auto parse(ParseContext& ctx) - { - return helper.parse(ctx); - } - - template - auto format(const srsran::pdcch_processor::coreset_description& coreset, FormatContext& ctx) const - { - helper.format_always(ctx, "bwp=[{}, {})", coreset.bwp_start_rb, coreset.bwp_start_rb + coreset.bwp_size_rb); - helper.format_always( - ctx, "symb=[{}, {})", coreset.start_symbol_index, coreset.start_symbol_index + coreset.duration); - helper.format_always(ctx, "f_re={}", coreset.frequency_resources); - - switch (coreset.cce_to_reg_mapping) { - case srsran::pdcch_processor::cce_to_reg_mapping_type::CORESET0: - helper.format_if_verbose(ctx, "mapping=coreset0"); - break; - case srsran::pdcch_processor::cce_to_reg_mapping_type::INTERLEAVED: - helper.format_if_verbose(ctx, "mapping=interleaved"); - helper.format_if_verbose(ctx, "reg_bundle_size={}", coreset.reg_bundle_size); - helper.format_if_verbose(ctx, "interleaver_size={}", coreset.interleaver_size); - break; - case srsran::pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED: - helper.format_if_verbose(ctx, "mapping=non-interleaved"); - break; - } - - helper.format_if_verbose(ctx, "shift_idx={}", coreset.shift_index); - - return ctx.out(); - } -}; - -/// \brief Custom formatter for \c pdcch_processor::pdu_t. -template <> -struct formatter { - /// Helper used to parse formatting options and format fields. - srsran::delimited_formatter helper; - - /// Default constructor. - formatter() = default; - - template - auto parse(ParseContext& ctx) - { - return helper.parse(ctx); - } - - template - auto format(const srsran::pdcch_processor::pdu_t& pdu, FormatContext& ctx) const - { - helper.format_always(ctx, "rnti=0x{:04x}", pdu.dci.rnti); - if (pdu.context.has_value()) { - helper.format_always(ctx, pdu.context.value()); - } - helper.format_if_verbose(ctx, "slot={}", pdu.slot); - helper.format_if_verbose(ctx, "cp={}", pdu.cp.to_string()); - helper.format_if_verbose(ctx, pdu.coreset); - helper.format_always(ctx, "cce={}", pdu.dci.cce_index); - helper.format_always(ctx, "al={}", pdu.dci.aggregation_level); - helper.format_if_verbose(ctx, "size={}", pdu.dci.payload.size()); - helper.format_if_verbose(ctx, "n_id_dmrs={}", pdu.dci.n_id_pdcch_dmrs); - helper.format_if_verbose(ctx, "n_id_data={}", pdu.dci.n_id_pdcch_data); - helper.format_if_verbose(ctx, "n_rnti={}", pdu.dci.n_rnti); - helper.format_if_verbose(ctx, "power_dmrs={:+.1f}dB", pdu.dci.dmrs_power_offset_dB); - helper.format_if_verbose(ctx, "power_data={:+.1f}dB", pdu.dci.data_power_offset_dB); - helper.format_if_verbose(ctx, "precoding={}", pdu.dci.precoding); - return ctx.out(); - } -}; - /// \brief Custom formatter for \c prach_detector::configuration. template <> struct formatter { diff --git a/include/srsran/phy/upper/channel_processors/pdcch/factories.h b/include/srsran/phy/upper/channel_processors/pdcch/factories.h new file mode 100644 index 0000000000..d4b1f0577b --- /dev/null +++ b/include/srsran/phy/upper/channel_processors/pdcch/factories.h @@ -0,0 +1,65 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/phy/generic_functions/generic_functions_factories.h" +#include "srsran/phy/upper/channel_coding/channel_coding_factories.h" +#include "srsran/phy/upper/channel_modulation/channel_modulation_factories.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_encoder.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_modulator.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" +#include "srsran/phy/upper/signal_processors/signal_processor_factories.h" +#include + +namespace srsran { + +class pdcch_modulator_factory +{ +public: + virtual ~pdcch_modulator_factory() = default; + virtual std::unique_ptr create() = 0; +}; + +std::shared_ptr + create_pdcch_modulator_factory_sw(std::shared_ptr, + std::shared_ptr, + std::shared_ptr); + +class pdcch_encoder_factory +{ +public: + virtual ~pdcch_encoder_factory() = default; + virtual std::unique_ptr create() = 0; +}; + +std::shared_ptr +create_pdcch_encoder_factory_sw(std::shared_ptr crc_factory, + std::shared_ptr encoder_factory); + +class pdcch_processor_factory +{ +public: + virtual ~pdcch_processor_factory() = default; + virtual std::unique_ptr create() = 0; + virtual std::unique_ptr create_validator() = 0; + virtual std::unique_ptr create(srslog::basic_logger& logger, bool enable_logging_broadcast); +}; + +std::shared_ptr +create_pdcch_processor_factory_sw(std::shared_ptr encoder_factory, + std::shared_ptr modulator_factory, + std::shared_ptr dmrs_factory); + +std::shared_ptr +create_pdcch_processor_pool_factory(std::shared_ptr processor_factory, + unsigned nof_concurrent_threads); + +} // namespace srsran \ No newline at end of file diff --git a/include/srsran/phy/upper/channel_processors/pdcch/formatters.h b/include/srsran/phy/upper/channel_processors/pdcch/formatters.h new file mode 100644 index 0000000000..df9194bc7f --- /dev/null +++ b/include/srsran/phy/upper/channel_processors/pdcch/formatters.h @@ -0,0 +1,101 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/phy/support/precoding_formatters.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" +#include "srsran/ran/pdcch/pdcch_context_formatter.h" +#include "srsran/ran/precoding/precoding_weight_matrix_formatters.h" + +namespace fmt { + +/// \brief Custom formatter for \c pdcch_processor::coreset_description. +template <> +struct formatter { + /// Helper used to parse formatting options and format fields. + srsran::delimited_formatter helper; + + /// Default constructor. + formatter() = default; + + template + auto parse(ParseContext& ctx) + { + return helper.parse(ctx); + } + + template + auto format(const srsran::pdcch_processor::coreset_description& coreset, FormatContext& ctx) const + { + helper.format_always(ctx, "bwp=[{}, {})", coreset.bwp_start_rb, coreset.bwp_start_rb + coreset.bwp_size_rb); + helper.format_always( + ctx, "symb=[{}, {})", coreset.start_symbol_index, coreset.start_symbol_index + coreset.duration); + helper.format_always(ctx, "f_re={}", coreset.frequency_resources); + + switch (coreset.cce_to_reg_mapping) { + case srsran::pdcch_processor::cce_to_reg_mapping_type::CORESET0: + helper.format_if_verbose(ctx, "mapping=coreset0"); + break; + case srsran::pdcch_processor::cce_to_reg_mapping_type::INTERLEAVED: + helper.format_if_verbose(ctx, "mapping=interleaved"); + helper.format_if_verbose(ctx, "reg_bundle_size={}", coreset.reg_bundle_size); + helper.format_if_verbose(ctx, "interleaver_size={}", coreset.interleaver_size); + break; + case srsran::pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED: + helper.format_if_verbose(ctx, "mapping=non-interleaved"); + break; + } + + helper.format_if_verbose(ctx, "shift_idx={}", coreset.shift_index); + + return ctx.out(); + } +}; + +/// \brief Custom formatter for \c pdcch_processor::pdu_t. +template <> +struct formatter { + /// Helper used to parse formatting options and format fields. + srsran::delimited_formatter helper; + + /// Default constructor. + formatter() = default; + + template + auto parse(ParseContext& ctx) + { + return helper.parse(ctx); + } + + template + auto format(const srsran::pdcch_processor::pdu_t& pdu, FormatContext& ctx) const + { + helper.format_always(ctx, "rnti=0x{:04x}", pdu.dci.rnti); + if (pdu.context.has_value()) { + helper.format_always(ctx, pdu.context.value()); + } + helper.format_if_verbose(ctx, "slot={}", pdu.slot); + helper.format_if_verbose(ctx, "cp={}", pdu.cp.to_string()); + helper.format_if_verbose(ctx, pdu.coreset); + helper.format_always(ctx, "cce={}", pdu.dci.cce_index); + helper.format_always(ctx, "al={}", pdu.dci.aggregation_level); + helper.format_if_verbose(ctx, "size={}", pdu.dci.payload.size()); + helper.format_if_verbose(ctx, "n_id_dmrs={}", pdu.dci.n_id_pdcch_dmrs); + helper.format_if_verbose(ctx, "n_id_data={}", pdu.dci.n_id_pdcch_data); + helper.format_if_verbose(ctx, "n_rnti={}", pdu.dci.n_rnti); + helper.format_if_verbose(ctx, "power_dmrs={:+.1f}dB", pdu.dci.dmrs_power_offset_dB); + helper.format_if_verbose(ctx, "power_data={:+.1f}dB", pdu.dci.data_power_offset_dB); + helper.format_if_verbose(ctx, "precoding={}", pdu.dci.precoding); + return ctx.out(); + } +}; + +} // namespace fmt \ No newline at end of file diff --git a/include/srsran/phy/upper/channel_processors/pdcch_encoder.h b/include/srsran/phy/upper/channel_processors/pdcch/pdcch_encoder.h similarity index 100% rename from include/srsran/phy/upper/channel_processors/pdcch_encoder.h rename to include/srsran/phy/upper/channel_processors/pdcch/pdcch_encoder.h diff --git a/include/srsran/phy/upper/channel_processors/pdcch_modulator.h b/include/srsran/phy/upper/channel_processors/pdcch/pdcch_modulator.h similarity index 100% rename from include/srsran/phy/upper/channel_processors/pdcch_modulator.h rename to include/srsran/phy/upper/channel_processors/pdcch/pdcch_modulator.h diff --git a/include/srsran/phy/upper/channel_processors/pdcch_processor.h b/include/srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h similarity index 100% rename from include/srsran/phy/upper/channel_processors/pdcch_processor.h rename to include/srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h diff --git a/include/srsran/phy/upper/channel_processors/pucch/formatters.h b/include/srsran/phy/upper/channel_processors/pucch/formatters.h index f944d60995..80dca612f0 100644 --- a/include/srsran/phy/upper/channel_processors/pucch/formatters.h +++ b/include/srsran/phy/upper/channel_processors/pucch/formatters.h @@ -12,11 +12,9 @@ #include "srsran/phy/support/precoding_formatters.h" #include "srsran/phy/support/re_pattern_formatters.h" -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" #include "srsran/phy/upper/channel_processors/prach_detector.h" #include "srsran/phy/upper/channel_processors/ssb_processor.h" #include "srsran/phy/upper/channel_state_information_formatters.h" -#include "srsran/ran/pdcch/pdcch_context_formatter.h" #include "srsran/ran/pucch/pucch_context_formatter.h" #include "srsran/srsvec/copy.h" #include "srsran/support/format/delimited_formatter.h" diff --git a/include/srsran/phy/upper/downlink_processor.h b/include/srsran/phy/upper/downlink_processor.h index f4cd0f12d5..8266032180 100644 --- a/include/srsran/phy/upper/downlink_processor.h +++ b/include/srsran/phy/upper/downlink_processor.h @@ -10,7 +10,7 @@ #pragma once -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" #include "srsran/phy/upper/channel_processors/pdsch/pdsch_processor.h" #include "srsran/phy/upper/channel_processors/ssb_processor.h" #include "srsran/phy/upper/signal_processors/nzp_csi_rs_generator.h" diff --git a/include/srsran/ran/pdcch/pdcch_context_formatter.h b/include/srsran/ran/pdcch/pdcch_context_formatter.h index 8cbd719dce..a50dc634ad 100644 --- a/include/srsran/ran/pdcch/pdcch_context_formatter.h +++ b/include/srsran/ran/pdcch/pdcch_context_formatter.h @@ -11,6 +11,7 @@ #pragma once #include "srsran/ran/pdcch/pdcch_context.h" +#include "srsran/support/format/delimited_formatter.h" namespace fmt { diff --git a/lib/phy/upper/channel_processors/CMakeLists.txt b/lib/phy/upper/channel_processors/CMakeLists.txt index e056efcf43..d918eb0ec7 100644 --- a/lib/phy/upper/channel_processors/CMakeLists.txt +++ b/lib/phy/upper/channel_processors/CMakeLists.txt @@ -6,6 +6,7 @@ # the distribution. # +add_subdirectory(pdcch) add_subdirectory(pdsch) add_subdirectory(pusch) add_subdirectory(pucch) @@ -17,15 +18,6 @@ target_link_libraries(srsran_pbch_encoder srsran_channel_coding srsran_sequence_ add_library(srsran_pbch_modulator STATIC pbch_modulator_impl.cpp) target_link_libraries(srsran_pbch_modulator srsran_channel_modulation srsran_sequence_generators srsvec) -add_library(srsran_pdcch_encoder STATIC pdcch_encoder_impl.cpp) -target_link_libraries(srsran_pdcch_encoder srsran_channel_coding srsvec) - -add_library(srsran_pdcch_modulator STATIC pdcch_modulator_impl.cpp) -target_link_libraries(srsran_pdcch_modulator srsvec) - -add_library(srsran_pdcch_processor STATIC pdcch_processor_impl.cpp) -target_link_libraries(srsran_pdcch_processor srsran_ran) - add_library(srsran_prach_detector STATIC prach_detector_generic_impl.cpp prach_detector_generic_thresholds.cpp @@ -40,8 +32,6 @@ add_library(srsran_channel_processors STATIC channel_processor_factories.cpp) target_link_libraries(srsran_channel_processors srsran_pbch_encoder srsran_pbch_modulator - srsran_pdcch_encoder - srsran_pdcch_modulator srsran_pdcch_processor srsran_pdsch_processor srsran_prach_detector @@ -55,8 +45,6 @@ target_link_libraries(srsran_channel_processors add_to_exported_libs(srsran_channel_processors srsran_pbch_encoder srsran_pbch_modulator - srsran_pdcch_encoder - srsran_pdcch_modulator srsran_pdcch_processor srsran_pdsch_processor srsran_prach_detector diff --git a/lib/phy/upper/channel_processors/channel_processor_factories.cpp b/lib/phy/upper/channel_processors/channel_processor_factories.cpp index b3f843c172..7aac2e5f0a 100644 --- a/lib/phy/upper/channel_processors/channel_processor_factories.cpp +++ b/lib/phy/upper/channel_processors/channel_processor_factories.cpp @@ -11,10 +11,6 @@ #include "srsran/phy/upper/channel_processors/channel_processor_factories.h" #include "pbch_encoder_impl.h" #include "pbch_modulator_impl.h" -#include "pdcch_encoder_impl.h" -#include "pdcch_modulator_impl.h" -#include "pdcch_processor_impl.h" -#include "pdcch_processor_pool.h" #include "prach_detector_generic_impl.h" #include "prach_detector_pool.h" #include "prach_generator_impl.h" @@ -85,139 +81,6 @@ class pbch_modulator_factory_sw : public pbch_modulator_factory } }; -class pdcch_modulator_factory_sw : public pdcch_modulator_factory -{ -private: - std::shared_ptr modulator_factory; - std::shared_ptr prg_factory; - std::shared_ptr rg_mapper_factory; - -public: - pdcch_modulator_factory_sw(std::shared_ptr modulator_factory_, - std::shared_ptr prg_factory_, - std::shared_ptr rg_mapper_factory_) : - modulator_factory(std::move(modulator_factory_)), - prg_factory(std::move(prg_factory_)), - rg_mapper_factory(std::move(rg_mapper_factory_)) - { - srsran_assert(modulator_factory, "Invalid modulator factory."); - srsran_assert(prg_factory, "Invalid PRG factory."); - srsran_assert(rg_mapper_factory, "Invalid resource grid mapper factory."); - } - - std::unique_ptr create() override - { - return std::make_unique( - modulator_factory->create_modulation_mapper(), prg_factory->create(), rg_mapper_factory->create()); - } -}; - -class pdcch_encoder_factory_sw : public pdcch_encoder_factory -{ -private: - std::shared_ptr crc_factory; - std::shared_ptr polar_code_factory; - -public: - pdcch_encoder_factory_sw(std::shared_ptr crc_factory_, - std::shared_ptr polar_code_factory_) : - crc_factory(std::move(crc_factory_)), polar_code_factory(std::move(polar_code_factory_)) - { - srsran_assert(crc_factory, "Invalid CRC calculator factory."); - srsran_assert(polar_code_factory, "Invalid Polar code factory."); - } - - std::unique_ptr create() override - { - return std::make_unique(crc_factory->create(crc_generator_poly::CRC24C), - polar_code_factory->create_interleaver(), - polar_code_factory->create_allocator(), - polar_code_factory->create_code(), - polar_code_factory->create_encoder(), - polar_code_factory->create_rate_matcher()); - } -}; - -class pdcch_processor_factory_sw : public pdcch_processor_factory -{ -private: - std::shared_ptr encoder_factory; - std::shared_ptr modulator_factory; - std::shared_ptr dmrs_factory; - -public: - pdcch_processor_factory_sw(std::shared_ptr encoder_factory_, - std::shared_ptr modulator_factory_, - std::shared_ptr dmrs_factory_) : - encoder_factory(std::move(encoder_factory_)), - modulator_factory(std::move(modulator_factory_)), - dmrs_factory(std::move(dmrs_factory_)) - { - srsran_assert(encoder_factory, "Invalid encoder factory."); - srsran_assert(modulator_factory, "Invalid modulator factory."); - srsran_assert(dmrs_factory, "Invalid DM-RS factory."); - } - - std::unique_ptr create() override - { - return std::make_unique( - encoder_factory->create(), modulator_factory->create(), dmrs_factory->create()); - } - - std::unique_ptr create_validator() override - { - return std::make_unique(); - } -}; - -class pdcch_processor_pool_factory : public pdcch_processor_factory -{ -public: - pdcch_processor_pool_factory(std::shared_ptr factory_, unsigned nof_concurrent_threads_) : - factory(std::move(factory_)), nof_concurrent_threads(nof_concurrent_threads_) - { - srsran_assert(factory, "Invalid PDCCH processor factory."); - srsran_assert(nof_concurrent_threads > 1, "Number of concurrent threads must be greater than one."); - } - - std::unique_ptr create() override - { - if (!processors) { - std::vector> procs(nof_concurrent_threads); - - for (auto& processor : procs) { - processor = factory->create(); - } - - processors = std::make_shared(std::move(procs)); - } - - return std::make_unique(std::move(processors)); - } - - std::unique_ptr create(srslog::basic_logger& logger, bool enable_logging_broadcast) override - { - if (!processors) { - std::vector> procs(nof_concurrent_threads); - - for (auto& processor : procs) { - processor = factory->create(logger, enable_logging_broadcast); - } - - processors = std::make_shared(std::move(procs)); - } - - return std::make_unique(std::move(processors)); - } - - std::unique_ptr create_validator() override { return factory->create_validator(); } - -private: - std::shared_ptr factory; - unsigned nof_concurrent_threads; - std::shared_ptr processors; -}; - class prach_detector_factory_sw : public prach_detector_factory { private: @@ -414,38 +277,6 @@ srsran::create_pbch_modulator_factory_sw(std::shared_ptr(std::move(modulator_factory), std::move(prg_factory)); } -std::shared_ptr -srsran::create_pdcch_encoder_factory_sw(std::shared_ptr crc_factory, - std::shared_ptr encoder_factory) -{ - return std::make_shared(std::move(crc_factory), std::move(encoder_factory)); -} - -std::shared_ptr -srsran::create_pdcch_modulator_factory_sw(std::shared_ptr modulator_factory, - std::shared_ptr prg_factory, - std::shared_ptr rg_mapper_factory) -{ - return std::make_shared( - std::move(modulator_factory), std::move(prg_factory), rg_mapper_factory); -} - -std::shared_ptr -srsran::create_pdcch_processor_factory_sw(std::shared_ptr encoder_factory, - std::shared_ptr modulator_factory, - std::shared_ptr dmrs_factory) -{ - return std::make_shared( - std::move(encoder_factory), std::move(modulator_factory), std::move(dmrs_factory)); -} - -std::shared_ptr -srsran::create_pdcch_processor_pool_factory(std::shared_ptr processor_factory, - unsigned nof_concurrent_threads) -{ - return std::make_shared(processor_factory, nof_concurrent_threads); -} - std::shared_ptr srsran::create_prach_detector_factory_sw(std::shared_ptr dft_factory, std::shared_ptr prach_gen_factory, @@ -489,68 +320,8 @@ static std::chrono::nanoseconds time_execution(Func&& func) return std::chrono::duration_cast(end - start); } -static bool is_broadcast_rnti(uint16_t rnti) -{ - return ((rnti < to_value(rnti_t::MIN_CRNTI)) || (rnti > to_value(rnti_t::MAX_CRNTI))); -} - namespace { -class logging_pdcch_processor_decorator : public pdcch_processor -{ -public: - logging_pdcch_processor_decorator(srslog::basic_logger& logger_, - bool enable_logging_broadcast_, - std::unique_ptr processor_) : - logger(logger_), enable_logging_broadcast(enable_logging_broadcast_), processor(std::move(processor_)) - { - srsran_assert(processor, "Invalid processor."); - } - - void process(resource_grid_writer& grid, const pdu_t& pdu) override - { - const auto&& func = [this, &grid, &pdu]() { processor->process(grid, pdu); }; - - if (!enable_logging_broadcast && is_broadcast_rnti(pdu.dci.rnti)) { - func(); - return; - } - - std::chrono::nanoseconds time_ns = time_execution(func); - - static_bit_buffer data(pdu.dci.payload.size()); - srsvec::zero(data.get_buffer()); - srsvec::bit_pack(data, pdu.dci.payload); - - if (logger.debug.enabled()) { - // Detailed log information, including a list of all PDU fields. - logger.debug(pdu.slot.sfn(), - pdu.slot.slot_index(), - data.get_buffer().data(), - divide_ceil(data.size(), 8), - "PDCCH: {:s} {}\n {:n}\n {}", - pdu, - time_ns, - pdu, - time_ns); - return; - } - // Single line log entry. - logger.info(pdu.slot.sfn(), - pdu.slot.slot_index(), - data.get_buffer().data(), - divide_ceil(data.size(), 8), - "PDCCH: {:s} {}", - pdu, - time_ns); - } - -private: - srslog::basic_logger& logger; - bool enable_logging_broadcast; - std::unique_ptr processor; -}; - class logging_prach_detector_decorator : public prach_detector { public: @@ -639,12 +410,6 @@ class logging_ssb_processor_decorator : public ssb_processor } // namespace -std::unique_ptr pdcch_processor_factory::create(srslog::basic_logger& logger, - bool enable_logging_broadcast) -{ - return std::make_unique(logger, enable_logging_broadcast, create()); -} - std::unique_ptr prach_detector_factory::create(srslog::basic_logger& logger, bool log_all_opportunities) { return std::make_unique(logger, log_all_opportunities, create()); diff --git a/lib/phy/upper/channel_processors/pdcch/CMakeLists.txt b/lib/phy/upper/channel_processors/pdcch/CMakeLists.txt new file mode 100644 index 0000000000..ad14e815f5 --- /dev/null +++ b/lib/phy/upper/channel_processors/pdcch/CMakeLists.txt @@ -0,0 +1,13 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the distribution. +# + +add_library(srsran_pdcch_processor STATIC + pdcch_encoder_impl.cpp + pdcch_modulator_impl.cpp + pdcch_processor_impl.cpp factories.cpp) +target_link_libraries(srsran_pdcch_processor srsran_ran srsvec) diff --git a/lib/phy/upper/channel_processors/pdcch/factories.cpp b/lib/phy/upper/channel_processors/pdcch/factories.cpp new file mode 100644 index 0000000000..0db23704d4 --- /dev/null +++ b/lib/phy/upper/channel_processors/pdcch/factories.cpp @@ -0,0 +1,193 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "srsran/phy/upper/channel_processors/pdcch/factories.h" +#include "logging_pdcch_processor_decorator.h" +#include "pdcch_encoder_impl.h" +#include "pdcch_modulator_impl.h" +#include "pdcch_processor_impl.h" +#include "pdcch_processor_pool.h" + +using namespace srsran; + +namespace { + +class pdcch_modulator_factory_sw : public pdcch_modulator_factory +{ +private: + std::shared_ptr modulator_factory; + std::shared_ptr prg_factory; + std::shared_ptr rg_mapper_factory; + +public: + pdcch_modulator_factory_sw(std::shared_ptr modulator_factory_, + std::shared_ptr prg_factory_, + std::shared_ptr rg_mapper_factory_) : + modulator_factory(std::move(modulator_factory_)), + prg_factory(std::move(prg_factory_)), + rg_mapper_factory(std::move(rg_mapper_factory_)) + { + srsran_assert(modulator_factory, "Invalid modulator factory."); + srsran_assert(prg_factory, "Invalid PRG factory."); + srsran_assert(rg_mapper_factory, "Invalid resource grid mapper factory."); + } + + std::unique_ptr create() override + { + return std::make_unique( + modulator_factory->create_modulation_mapper(), prg_factory->create(), rg_mapper_factory->create()); + } +}; + +class pdcch_encoder_factory_sw : public pdcch_encoder_factory +{ +private: + std::shared_ptr crc_factory; + std::shared_ptr polar_code_factory; + +public: + pdcch_encoder_factory_sw(std::shared_ptr crc_factory_, + std::shared_ptr polar_code_factory_) : + crc_factory(std::move(crc_factory_)), polar_code_factory(std::move(polar_code_factory_)) + { + srsran_assert(crc_factory, "Invalid CRC calculator factory."); + srsran_assert(polar_code_factory, "Invalid Polar code factory."); + } + + std::unique_ptr create() override + { + return std::make_unique(crc_factory->create(crc_generator_poly::CRC24C), + polar_code_factory->create_interleaver(), + polar_code_factory->create_allocator(), + polar_code_factory->create_code(), + polar_code_factory->create_encoder(), + polar_code_factory->create_rate_matcher()); + } +}; + +class pdcch_processor_factory_sw : public pdcch_processor_factory +{ +private: + std::shared_ptr encoder_factory; + std::shared_ptr modulator_factory; + std::shared_ptr dmrs_factory; + +public: + pdcch_processor_factory_sw(std::shared_ptr encoder_factory_, + std::shared_ptr modulator_factory_, + std::shared_ptr dmrs_factory_) : + encoder_factory(std::move(encoder_factory_)), + modulator_factory(std::move(modulator_factory_)), + dmrs_factory(std::move(dmrs_factory_)) + { + srsran_assert(encoder_factory, "Invalid encoder factory."); + srsran_assert(modulator_factory, "Invalid modulator factory."); + srsran_assert(dmrs_factory, "Invalid DM-RS factory."); + } + + std::unique_ptr create() override + { + return std::make_unique( + encoder_factory->create(), modulator_factory->create(), dmrs_factory->create()); + } + + std::unique_ptr create_validator() override + { + return std::make_unique(); + } +}; + +class pdcch_processor_pool_factory : public pdcch_processor_factory +{ +public: + pdcch_processor_pool_factory(std::shared_ptr factory_, unsigned nof_concurrent_threads_) : + factory(std::move(factory_)), nof_concurrent_threads(nof_concurrent_threads_) + { + srsran_assert(factory, "Invalid PDCCH processor factory."); + srsran_assert(nof_concurrent_threads > 1, "Number of concurrent threads must be greater than one."); + } + + std::unique_ptr create() override + { + if (!processors) { + std::vector> procs(nof_concurrent_threads); + + for (auto& processor : procs) { + processor = factory->create(); + } + + processors = std::make_shared(std::move(procs)); + } + + return std::make_unique(std::move(processors)); + } + + std::unique_ptr create(srslog::basic_logger& logger, bool enable_logging_broadcast) override + { + if (!processors) { + std::vector> procs(nof_concurrent_threads); + + for (auto& processor : procs) { + processor = factory->create(logger, enable_logging_broadcast); + } + + processors = std::make_shared(std::move(procs)); + } + + return std::make_unique(std::move(processors)); + } + + std::unique_ptr create_validator() override { return factory->create_validator(); } + +private: + std::shared_ptr factory; + unsigned nof_concurrent_threads; + std::shared_ptr processors; +}; + +} // namespace + +std::shared_ptr +srsran::create_pdcch_encoder_factory_sw(std::shared_ptr crc_factory, + std::shared_ptr encoder_factory) +{ + return std::make_shared(std::move(crc_factory), std::move(encoder_factory)); +} + +std::shared_ptr +srsran::create_pdcch_modulator_factory_sw(std::shared_ptr modulator_factory, + std::shared_ptr prg_factory, + std::shared_ptr rg_mapper_factory) +{ + return std::make_shared( + std::move(modulator_factory), std::move(prg_factory), rg_mapper_factory); +} + +std::shared_ptr +srsran::create_pdcch_processor_factory_sw(std::shared_ptr encoder_factory, + std::shared_ptr modulator_factory, + std::shared_ptr dmrs_factory) +{ + return std::make_shared( + std::move(encoder_factory), std::move(modulator_factory), std::move(dmrs_factory)); +} + +std::shared_ptr +srsran::create_pdcch_processor_pool_factory(std::shared_ptr processor_factory, + unsigned nof_concurrent_threads) +{ + return std::make_shared(processor_factory, nof_concurrent_threads); +} + +std::unique_ptr pdcch_processor_factory::create(srslog::basic_logger& logger, + bool enable_logging_broadcast) +{ + return std::make_unique(logger, enable_logging_broadcast, create()); +} \ No newline at end of file diff --git a/lib/phy/upper/channel_processors/pdcch/logging_pdcch_processor_decorator.h b/lib/phy/upper/channel_processors/pdcch/logging_pdcch_processor_decorator.h new file mode 100644 index 0000000000..c19afe256c --- /dev/null +++ b/lib/phy/upper/channel_processors/pdcch/logging_pdcch_processor_decorator.h @@ -0,0 +1,91 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "srsran/phy/support/support_formatters.h" +#include "srsran/phy/upper/channel_processors/pdcch/formatters.h" +#include "srsran/ran/precoding/precoding_weight_matrix_formatters.h" +#include "srsran/srsvec/bit.h" +#include "srsran/support/format/delimited_formatter.h" + +namespace srsran { + +inline bool is_broadcast_rnti(uint16_t rnti) +{ + return ((rnti < to_value(rnti_t::MIN_CRNTI)) || (rnti > to_value(rnti_t::MAX_CRNTI))); +} + +template +inline std::chrono::nanoseconds time_execution(Func&& func) +{ + auto start = std::chrono::steady_clock::now(); + func(); + auto end = std::chrono::steady_clock::now(); + + return std::chrono::duration_cast(end - start); +} + +class logging_pdcch_processor_decorator : public pdcch_processor +{ +public: + logging_pdcch_processor_decorator(srslog::basic_logger& logger_, + bool enable_logging_broadcast_, + std::unique_ptr processor_) : + logger(logger_), enable_logging_broadcast(enable_logging_broadcast_), processor(std::move(processor_)) + { + srsran_assert(processor, "Invalid processor."); + } + + void process(resource_grid_writer& grid, const pdu_t& pdu) override + { + const auto&& func = [this, &grid, &pdu]() { processor->process(grid, pdu); }; + + if (!enable_logging_broadcast && is_broadcast_rnti(pdu.dci.rnti)) { + func(); + return; + } + + std::chrono::nanoseconds time_ns = time_execution(func); + + static_bit_buffer data(pdu.dci.payload.size()); + srsvec::zero(data.get_buffer()); + srsvec::bit_pack(data, pdu.dci.payload); + + if (logger.debug.enabled()) { + // Detailed log information, including a list of all PDU fields. + logger.debug(pdu.slot.sfn(), + pdu.slot.slot_index(), + data.get_buffer().data(), + divide_ceil(data.size(), 8), + "PDCCH: {:s} {}\n {:n}\n {}", + pdu, + time_ns, + pdu, + time_ns); + return; + } + // Single line log entry. + logger.info(pdu.slot.sfn(), + pdu.slot.slot_index(), + data.get_buffer().data(), + divide_ceil(data.size(), 8), + "PDCCH: {:s} {}", + pdu, + time_ns); + } + +private: + srslog::basic_logger& logger; + bool enable_logging_broadcast; + std::unique_ptr processor; +}; + +} // namespace srsran \ No newline at end of file diff --git a/lib/phy/upper/channel_processors/pdcch_encoder_impl.cpp b/lib/phy/upper/channel_processors/pdcch/pdcch_encoder_impl.cpp similarity index 100% rename from lib/phy/upper/channel_processors/pdcch_encoder_impl.cpp rename to lib/phy/upper/channel_processors/pdcch/pdcch_encoder_impl.cpp diff --git a/lib/phy/upper/channel_processors/pdcch_encoder_impl.h b/lib/phy/upper/channel_processors/pdcch/pdcch_encoder_impl.h similarity index 98% rename from lib/phy/upper/channel_processors/pdcch_encoder_impl.h rename to lib/phy/upper/channel_processors/pdcch/pdcch_encoder_impl.h index ca0d0772b9..4517babcd3 100644 --- a/lib/phy/upper/channel_processors/pdcch_encoder_impl.h +++ b/lib/phy/upper/channel_processors/pdcch/pdcch_encoder_impl.h @@ -16,7 +16,7 @@ #include "srsran/phy/upper/channel_coding/polar/polar_encoder.h" #include "srsran/phy/upper/channel_coding/polar/polar_interleaver.h" #include "srsran/phy/upper/channel_coding/polar/polar_rate_matcher.h" -#include "srsran/phy/upper/channel_processors/pdcch_encoder.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_encoder.h" #include "srsran/ran/pdcch/pdcch_constants.h" #include "srsran/support/error_handling.h" diff --git a/lib/phy/upper/channel_processors/pdcch_modulator_impl.cpp b/lib/phy/upper/channel_processors/pdcch/pdcch_modulator_impl.cpp similarity index 100% rename from lib/phy/upper/channel_processors/pdcch_modulator_impl.cpp rename to lib/phy/upper/channel_processors/pdcch/pdcch_modulator_impl.cpp diff --git a/lib/phy/upper/channel_processors/pdcch_modulator_impl.h b/lib/phy/upper/channel_processors/pdcch/pdcch_modulator_impl.h similarity index 98% rename from lib/phy/upper/channel_processors/pdcch_modulator_impl.h rename to lib/phy/upper/channel_processors/pdcch/pdcch_modulator_impl.h index b715a0b1d7..e160826bd7 100644 --- a/lib/phy/upper/channel_processors/pdcch_modulator_impl.h +++ b/lib/phy/upper/channel_processors/pdcch/pdcch_modulator_impl.h @@ -13,7 +13,7 @@ #include "srsran/phy/support/re_buffer.h" #include "srsran/phy/support/resource_grid_writer.h" #include "srsran/phy/upper/channel_modulation/modulation_mapper.h" -#include "srsran/phy/upper/channel_processors/pdcch_modulator.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_modulator.h" #include "srsran/phy/upper/sequence_generators/pseudo_random_generator.h" namespace srsran { diff --git a/lib/phy/upper/channel_processors/pdcch_processor_impl.cpp b/lib/phy/upper/channel_processors/pdcch/pdcch_processor_impl.cpp similarity index 100% rename from lib/phy/upper/channel_processors/pdcch_processor_impl.cpp rename to lib/phy/upper/channel_processors/pdcch/pdcch_processor_impl.cpp diff --git a/lib/phy/upper/channel_processors/pdcch_processor_impl.h b/lib/phy/upper/channel_processors/pdcch/pdcch_processor_impl.h similarity index 93% rename from lib/phy/upper/channel_processors/pdcch_processor_impl.h rename to lib/phy/upper/channel_processors/pdcch/pdcch_processor_impl.h index c0cab571c1..1d5859b8a9 100644 --- a/lib/phy/upper/channel_processors/pdcch_processor_impl.h +++ b/lib/phy/upper/channel_processors/pdcch/pdcch_processor_impl.h @@ -10,9 +10,9 @@ #pragma once -#include "srsran/phy/upper/channel_processors/pdcch_encoder.h" -#include "srsran/phy/upper/channel_processors/pdcch_modulator.h" -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_encoder.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_modulator.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" #include "srsran/phy/upper/signal_processors/dmrs_pdcch_processor.h" namespace srsran { diff --git a/lib/phy/upper/channel_processors/pdcch_processor_pool.h b/lib/phy/upper/channel_processors/pdcch/pdcch_processor_pool.h similarity index 94% rename from lib/phy/upper/channel_processors/pdcch_processor_pool.h rename to lib/phy/upper/channel_processors/pdcch/pdcch_processor_pool.h index 3750d128fc..78bfe20233 100644 --- a/lib/phy/upper/channel_processors/pdcch_processor_pool.h +++ b/lib/phy/upper/channel_processors/pdcch/pdcch_processor_pool.h @@ -10,7 +10,7 @@ #pragma once -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" #include "srsran/support/memory_pool/concurrent_thread_local_object_pool.h" namespace srsran { diff --git a/lib/phy/upper/downlink_processor_single_executor_impl.cpp b/lib/phy/upper/downlink_processor_single_executor_impl.cpp index bf6e0f8c23..ae3fcdf82f 100644 --- a/lib/phy/upper/downlink_processor_single_executor_impl.cpp +++ b/lib/phy/upper/downlink_processor_single_executor_impl.cpp @@ -11,6 +11,8 @@ #include "downlink_processor_single_executor_impl.h" #include "srsran/instrumentation/traces/du_traces.h" #include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" +#include "srsran/phy/upper/channel_processors/pdcch/formatters.h" +#include "srsran/phy/upper/channel_processors/pdsch/formatters.h" #include "srsran/phy/upper/signal_processors/signal_processor_formatters.h" #include "srsran/phy/upper/upper_phy_rg_gateway.h" #include "srsran/support/executors/task_executor.h" diff --git a/lib/phy/upper/upper_phy_factories.cpp b/lib/phy/upper/upper_phy_factories.cpp index 3e083f2477..72e5926043 100644 --- a/lib/phy/upper/upper_phy_factories.cpp +++ b/lib/phy/upper/upper_phy_factories.cpp @@ -20,6 +20,7 @@ #include "srsran/phy/support/support_factories.h" #include "srsran/phy/upper/channel_estimation.h" #include "srsran/phy/upper/channel_processors/channel_processor_factories.h" +#include "srsran/phy/upper/channel_processors/pdcch/factories.h" #include "srsran/phy/upper/channel_processors/pdsch/factories.h" #include "srsran/phy/upper/channel_processors/pucch/factories.h" #include "srsran/phy/upper/channel_processors/pusch/factories.h" diff --git a/tests/benchmarks/phy/upper/signal_processors/dmrs_pdsch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/signal_processors/dmrs_pdsch_processor_benchmark.cpp index 64896756ae..cfba41e93a 100644 --- a/tests/benchmarks/phy/upper/signal_processors/dmrs_pdsch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/signal_processors/dmrs_pdsch_processor_benchmark.cpp @@ -8,7 +8,6 @@ * */ -#include "../../../tests/unittests/phy/support/resource_grid_mapper_test_doubles.h" #include "srsran/phy/support/support_factories.h" #include "srsran/phy/upper/signal_processors/dmrs_pdsch_processor.h" #include "srsran/phy/upper/signal_processors/signal_processor_factories.h" @@ -22,10 +21,9 @@ using namespace srsran; // Random generator. static std::mt19937 rgen(0); -static unsigned nof_repetitions = 1000; -static unsigned nof_rb = 106; -static bool silent = false; -static bool use_dummy_mapper = false; +static unsigned nof_repetitions = 1000; +static unsigned nof_rb = 106; +static bool silent = false; struct channel_topology { unsigned nof_ports; @@ -38,7 +36,6 @@ static void usage(const char* prog) { fmt::print("Usage: {} [-P precoder type] [-n number of RB] [-R repetitions] [-s silent]\n", prog); fmt::print("\t-n Number of resource blocks [Default {}]\n", nof_rb); - fmt::print("\t-D Use dummy resource grid mapper\n"); fmt::print("\t-R Repetitions [Default {}]\n", nof_repetitions); fmt::print("\t-s Toggle silent operation [Default {}]\n", silent); fmt::print("\t-h Show this message\n"); @@ -52,9 +49,6 @@ static void parse_args(int argc, char** argv) case 'n': nof_rb = std::strtol(optarg, nullptr, 10); break; - case 'D': - use_dummy_mapper = true; - break; case 'R': nof_repetitions = std::strtol(optarg, nullptr, 10); break; @@ -105,8 +99,6 @@ int main(int argc, char** argv) // Precoding weight distribution. std::uniform_real_distribution weight_dist(-1.0F, 1.0F); - resource_grid_mapper_dummy dummy_mapper; - benchmarker perf_meas("DM-RS PDSCH processor", nof_repetitions); for (auto topology : channel_topology_list) { diff --git a/tests/unittests/ofh/receiver/helpers.h b/tests/unittests/ofh/receiver/helpers.h index ba24f3b72d..b9308f281d 100644 --- a/tests/unittests/ofh/receiver/helpers.h +++ b/tests/unittests/ofh/receiver/helpers.h @@ -10,7 +10,6 @@ #pragma once -#include "../../phy/support/resource_grid_mapper_test_doubles.h" #include "../../phy/support/resource_grid_test_doubles.h" #include "srsran/phy/support/prach_buffer.h" #include "srsran/ran/resource_block.h" diff --git a/tests/unittests/phy/support/resource_grid_mapper_test_doubles.h b/tests/unittests/phy/support/resource_grid_mapper_test_doubles.h deleted file mode 100644 index 2777c14398..0000000000 --- a/tests/unittests/phy/support/resource_grid_mapper_test_doubles.h +++ /dev/null @@ -1,38 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the distribution. - * - */ - -#pragma once - -#include "resource_grid_test_doubles.h" - -namespace srsran { - -/// Resource grid mapper dummy. -class resource_grid_mapper_dummy : public resource_grid_mapper -{ -public: - void map(resource_grid_writer& grid, - const re_buffer_reader<>& input, - const re_pattern& pattern, - const precoding_configuration& precoding) override - { - } - - void map(resource_grid_writer& grid, - symbol_buffer& buffer, - const re_pattern_list& pattern, - const re_pattern_list& reserved, - const precoding_configuration& precoding, - unsigned skip_re) override - { - } -}; - -} // namespace srsran diff --git a/tests/unittests/phy/upper/channel_processors/CMakeLists.txt b/tests/unittests/phy/upper/channel_processors/CMakeLists.txt index 2cee5644d8..653d510df9 100644 --- a/tests/unittests/phy/upper/channel_processors/CMakeLists.txt +++ b/tests/unittests/phy/upper/channel_processors/CMakeLists.txt @@ -6,6 +6,7 @@ # the distribution. # +add_subdirectory(pdcch) add_subdirectory(pdsch) add_subdirectory(pucch) add_subdirectory(pusch) @@ -16,10 +17,6 @@ set(TEST_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/test_data) set_directory_properties(PROPERTIES LABELS "phy") -add_executable(pdcch_processor_unittest pdcch_processor_unittest.cpp) -target_link_libraries(pdcch_processor_unittest srsran_channel_processors srslog) -add_test(pdcch_processor_unittest pdcch_processor_unittest) - add_executable(ssb_processor_unittest ssb_processor_unittest.cpp) target_link_libraries(ssb_processor_unittest srsran_channel_processors srslog) add_test(ssb_processor_unittest ssb_processor_unittest) @@ -33,23 +30,6 @@ if (USE_PHY_TESTVECTORS) target_link_libraries(pbch_modulator_test srsran_channel_processors srslog) add_test_vector(pbch_modulator_test pbch_modulator_test_data.tar.gz "") - add_executable(pdcch_encoder_test pdcch_encoder_test.cpp) - target_link_libraries(pdcch_encoder_test srsran_channel_processors srslog) - add_test_vector(pdcch_encoder_test pdcch_encoder_test_data.tar.gz "") - - add_executable(pdcch_modulator_test pdcch_modulator_test.cpp) - target_link_libraries(pdcch_modulator_test - srsran_channel_processors - srsran_channel_precoder - srslog - srsran_channel_modulation - srsran_sequence_generators) - add_test_vector(pdcch_modulator_test pdcch_modulator_test_data.tar.gz "") - - add_executable(pdcch_processor_vectortest pdcch_processor_vectortest.cpp) - target_link_libraries(pdcch_processor_vectortest srsran_channel_processors srsran_channel_precoder srslog gtest gtest_main) - add_test_vector(pdcch_processor_vectortest pdcch_processor_test_data.tar.gz "") - add_executable(prach_detector_vectortest prach_detector_vectortest.cpp) target_link_libraries(prach_detector_vectortest srsran_channel_processors srsran_generic_funcs srsran_phy_support srslog gtest gtest_main) add_test_vector(prach_detector_vectortest prach_detector_test_data.tar.gz "") diff --git a/tests/unittests/phy/upper/channel_processors/pdcch/CMakeLists.txt b/tests/unittests/phy/upper/channel_processors/pdcch/CMakeLists.txt new file mode 100644 index 0000000000..bd02f39614 --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pdcch/CMakeLists.txt @@ -0,0 +1,42 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# By using this file, you agree to the terms and conditions set +# forth in the LICENSE file which can be found at the top level of +# the distribution. +# + +file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/test_data) +set(TEST_DATA_DIR ${CMAKE_CURRENT_BINARY_DIR}/test_data) + +set_directory_properties(PROPERTIES LABELS "phy") + +add_executable(pdcch_processor_unittest pdcch_processor_unittest.cpp) +target_link_libraries(pdcch_processor_unittest srsran_channel_processors srslog) +add_test(pdcch_processor_unittest pdcch_processor_unittest) +target_include_directories(pdcch_processor_unittest PRIVATE + ${CMAKE_SOURCE_DIR}/tests/unittests/phy/support + ${CMAKE_SOURCE_DIR}/tests/unittests/phy/upper/signal_processors) + +if (USE_PHY_TESTVECTORS) + add_executable(pdcch_encoder_test pdcch_encoder_test.cpp) + target_link_libraries(pdcch_encoder_test srsran_channel_processors srslog) + add_test_vector(pdcch_encoder_test pdcch_encoder_test_data.tar.gz "") + + add_executable(pdcch_modulator_test pdcch_modulator_test.cpp) + target_link_libraries(pdcch_modulator_test + srsran_channel_processors + srsran_channel_precoder + srslog + srsran_channel_modulation + srsran_sequence_generators) + add_test_vector(pdcch_modulator_test pdcch_modulator_test_data.tar.gz "") + target_include_directories(pdcch_modulator_test PRIVATE ${CMAKE_SOURCE_DIR}/tests/unittests/phy/support) + + add_executable(pdcch_processor_vectortest pdcch_processor_vectortest.cpp) + target_link_libraries(pdcch_processor_vectortest srsran_channel_processors srsran_channel_precoder srslog gtest gtest_main) + add_test_vector(pdcch_processor_vectortest pdcch_processor_test_data.tar.gz "") + target_include_directories(pdcch_processor_vectortest PRIVATE + ${CMAKE_SOURCE_DIR}/tests/unittests/phy/ + ${CMAKE_SOURCE_DIR}/tests/unittests/phy/support) +endif (USE_PHY_TESTVECTORS) diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test.cpp b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test.cpp similarity index 95% rename from tests/unittests/phy/upper/channel_processors/pdcch_encoder_test.cpp rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test.cpp index 25b908bbda..3c4691ce9e 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test.cpp @@ -9,7 +9,7 @@ */ #include "pdcch_encoder_test_data.h" -#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" +#include "srsran/phy/upper/channel_processors/pdcch/factories.h" #include "srsran/support/srsran_test.h" using namespace srsran; diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_data.h b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_data.h similarity index 97% rename from tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_data.h rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_data.h index 587830af29..aac6ef3cc2 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_data.h +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_data.h @@ -10,10 +10,10 @@ #pragma once -// This file was generated using the following MATLAB class on 14-09-2023 (seed 0): +// This file was generated using the following MATLAB class on 18-12-2024 (seed 0): // + "srsPDCCHEncoderUnittest.m" -#include "srsran/phy/upper/channel_processors/pdcch_encoder.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_encoder.h" #include "srsran/support/file_vector.h" namespace srsran { diff --git a/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_data.tar.gz b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_data.tar.gz new file mode 100644 index 0000000000..aaf0a00154 --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_data.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:dfb9d081edb02544c6f74c885b4e0b977f4a988fced8d3b543d4c0fd772bec74 +size 3689 diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_doubles.h b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_doubles.h similarity index 92% rename from tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_doubles.h rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_doubles.h index 097d6eb49a..28726210f8 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_doubles.h +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_encoder_test_doubles.h @@ -10,8 +10,8 @@ #pragma once -#include "../../phy_test_utils.h" -#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" +#include "../../../phy_test_utils.h" +#include "srsran/phy/upper/channel_processors/pdcch/factories.h" #include namespace srsran { diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test.cpp b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test.cpp similarity index 94% rename from tests/unittests/phy/upper/channel_processors/pdcch_modulator_test.cpp rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test.cpp index 30a578d62e..47491bcae0 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test.cpp @@ -8,10 +8,9 @@ * */ -#include "../../support/resource_grid_mapper_test_doubles.h" #include "pdcch_modulator_test_data.h" #include "srsran/phy/support/support_factories.h" -#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" +#include "srsran/phy/upper/channel_processors/pdcch/factories.h" using namespace srsran; diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_data.h b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_data.h similarity index 96% rename from tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_data.h rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_data.h index b9aed44260..349868b6fc 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_data.h +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_data.h @@ -10,11 +10,11 @@ #pragma once -// This file was generated using the following MATLAB class on 05-03-2024 (seed 0): +// This file was generated using the following MATLAB class on 18-12-2024 (seed 0): // + "srsPDCCHModulatorUnittest.m" -#include "../../support/resource_grid_test_doubles.h" -#include "srsran/phy/upper/channel_processors/pdcch_modulator.h" +#include "../../../support/resource_grid_test_doubles.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_modulator.h" #include "srsran/ran/precoding/precoding_codebooks.h" #include "srsran/support/file_vector.h" diff --git a/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_data.tar.gz b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_data.tar.gz new file mode 100644 index 0000000000..69a7d26d18 --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_data.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c7087701daed0e5b7b57e02681485805e0d104c29c4cb27cd7c8438ceecc0ff5 +size 16960 diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_doubles.h b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_doubles.h similarity index 93% rename from tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_doubles.h rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_doubles.h index e6e221e606..04d7f916fc 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_doubles.h +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_modulator_test_doubles.h @@ -10,7 +10,7 @@ #pragma once -#include "srsran/phy/upper/channel_processors/pdcch_modulator.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_modulator.h" #include namespace srsran { diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_processor_test_data.h b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.h similarity index 99% rename from tests/unittests/phy/upper/channel_processors/pdcch_processor_test_data.h rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.h index 610f694d50..ec9fb6086b 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_processor_test_data.h +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.h @@ -10,11 +10,11 @@ #pragma once -// This file was generated using the following MATLAB class on 05-03-2024 (seed 0): +// This file was generated using the following MATLAB class on 18-12-2024 (seed 0): // + "srsPDCCHProcessorUnittest.m" -#include "../../support/resource_grid_test_doubles.h" -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" +#include "../../../support/resource_grid_test_doubles.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" #include "srsran/ran/precoding/precoding_codebooks.h" #include "srsran/support/file_vector.h" diff --git a/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.tar.gz b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.tar.gz new file mode 100644 index 0000000000..0a7e40d016 --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.tar.gz @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b5275140c79bcf4d7c9025f3f9acb5fce7d23bc6942e02715567dfe5911e8de6 +size 125002 diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_processor_test_doubles.h b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_doubles.h similarity index 87% rename from tests/unittests/phy/upper/channel_processors/pdcch_processor_test_doubles.h rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_doubles.h index 60b1d34769..d6689abecc 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_processor_test_doubles.h +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_doubles.h @@ -10,8 +10,8 @@ #pragma once -#include "../../phy_test_utils.h" -#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" +#include "../../../phy_test_utils.h" +#include "srsran/phy/upper/channel_processors/pdcch/factories.h" namespace srsran { diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_processor_unittest.cpp b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_unittest.cpp similarity index 97% rename from tests/unittests/phy/upper/channel_processors/pdcch_processor_unittest.cpp rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_unittest.cpp index cffee22dc0..6704b177ff 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_processor_unittest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_unittest.cpp @@ -8,13 +8,13 @@ * */ -#include "../../support/resource_grid_test_doubles.h" -#include "../signal_processors/dmrs_pdcch_processor_test_doubles.h" +#include "dmrs_pdcch_processor_test_doubles.h" #include "pdcch_encoder_test_doubles.h" #include "pdcch_modulator_test_doubles.h" +#include "resource_grid_test_doubles.h" #include "srsran/phy/support/precoding_formatters.h" #include "srsran/phy/support/resource_grid_mapper.h" -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" +#include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" #include "srsran/ran/precoding/precoding_codebooks.h" #include diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_vectortest.cpp similarity index 94% rename from tests/unittests/phy/upper/channel_processors/pdcch_processor_vectortest.cpp rename to tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_vectortest.cpp index 015306842f..f6c6b745a2 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_vectortest.cpp @@ -8,11 +8,10 @@ * */ -#include "../../support/resource_grid_mapper_test_doubles.h" #include "pdcch_processor_test_data.h" #include "srsran/phy/support/support_factories.h" -#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" -#include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" +#include "srsran/phy/upper/channel_processors/pdcch/factories.h" +#include "srsran/phy/upper/channel_processors/pdcch/formatters.h" #include "fmt/ostream.h" #include diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_data.tar.gz b/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_data.tar.gz deleted file mode 100644 index cf624fdd16..0000000000 --- a/tests/unittests/phy/upper/channel_processors/pdcch_encoder_test_data.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:129c3250ee67b9c4c70fc3b0c284e5d4ce0bce65e0ffd0cd84e7fced3e182cc5 -size 3666 diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_data.tar.gz b/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_data.tar.gz deleted file mode 100644 index 58cece2279..0000000000 --- a/tests/unittests/phy/upper/channel_processors/pdcch_modulator_test_data.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3d0a705eb9d3e50833273bd0562b7c995f6eab361ea708cf7975bc3efa884e23 -size 16997 diff --git a/tests/unittests/phy/upper/channel_processors/pdcch_processor_test_data.tar.gz b/tests/unittests/phy/upper/channel_processors/pdcch_processor_test_data.tar.gz deleted file mode 100644 index 55b6e191eb..0000000000 --- a/tests/unittests/phy/upper/channel_processors/pdcch_processor_test_data.tar.gz +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0b7e65eff3bc130440c94fea4c79f1aae025caf257631ee0b53fba3a4a0eab32 -size 124919 diff --git a/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_modulator_test.cpp b/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_modulator_test.cpp index ea9f9fcbf1..0c322f1e09 100644 --- a/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_modulator_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_modulator_test.cpp @@ -8,7 +8,6 @@ * */ -#include "../../../support/resource_grid_mapper_test_doubles.h" #include "pdsch_modulator_test_data.h" #include "srsran/phy/support/support_factories.h" #include "srsran/phy/upper/channel_processors/pdsch/factories.h" diff --git a/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_vectortest.cpp index 232415c3a2..308dacf260 100644 --- a/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pdsch/pdsch_processor_vectortest.cpp @@ -8,7 +8,6 @@ * */ -#include "../../../support/resource_grid_mapper_test_doubles.h" #include "pdsch_processor_test_data.h" #include "pdsch_processor_test_doubles.h" #include "srsran/phy/support/support_factories.h" diff --git a/tests/unittests/phy/upper/downlink_processor_test.cpp b/tests/unittests/phy/upper/downlink_processor_test.cpp index 3cd7bdb8e0..acca5a5a49 100644 --- a/tests/unittests/phy/upper/downlink_processor_test.cpp +++ b/tests/unittests/phy/upper/downlink_processor_test.cpp @@ -11,7 +11,7 @@ #include "../../../lib/phy/upper/downlink_processor_single_executor_impl.h" #include "../../support/task_executor_test_doubles.h" #include "../support/resource_grid_test_doubles.h" -#include "channel_processors/pdcch_processor_test_doubles.h" +#include "channel_processors/pdcch/pdcch_processor_test_doubles.h" #include "channel_processors/pdsch/pdsch_processor_test_doubles.h" #include "channel_processors/ssb_processor_test_doubles.h" #include "signal_processors/nzp_csi_rs_generator_test_doubles.h" diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pdcch_processor_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pdcch_processor_test.cpp index 0a6739f725..53668031d5 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pdcch_processor_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pdcch_processor_test.cpp @@ -8,7 +8,6 @@ * */ -#include "../../support/resource_grid_mapper_test_doubles.h" #include "dmrs_pdcch_processor_test_data.h" #include "srsran/phy/support/support_factories.h" #include "srsran/phy/upper/signal_processors/signal_processor_factories.h" diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pdsch_processor_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pdsch_processor_test.cpp index 48932cf0ba..167208a861 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pdsch_processor_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pdsch_processor_test.cpp @@ -8,7 +8,6 @@ * */ -#include "../../support/resource_grid_mapper_test_doubles.h" #include "dmrs_pdsch_processor_test_data.h" #include "srsran/phy/support/support_factories.h" #include "srsran/phy/upper/signal_processors/signal_processor_factories.h" diff --git a/tests/unittests/phy/upper/signal_processors/nzp_csi_rs_generator_test.cpp b/tests/unittests/phy/upper/signal_processors/nzp_csi_rs_generator_test.cpp index 1fc6e14bd2..b24e88e241 100644 --- a/tests/unittests/phy/upper/signal_processors/nzp_csi_rs_generator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/nzp_csi_rs_generator_test.cpp @@ -8,7 +8,6 @@ * */ -#include "../../support/resource_grid_mapper_test_doubles.h" #include "nzp_csi_rs_generator_test_data.h" #include "srsran/phy/support/support_factories.h" #include "srsran/phy/upper/signal_processors/signal_processor_factories.h" From 2f53b5c7f40eda1bb20a9abcdf8db920cbc3b9f1 Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 2 Jan 2025 19:51:06 +0100 Subject: [PATCH 061/107] sched: fix logging of CE delay and CRC SINR in scheduler metrics --- .../du_high_scheduler_cell_metrics_consumers.cpp | 6 +++--- lib/scheduler/logging/scheduler_event_logger.cpp | 2 +- lib/scheduler/logging/scheduler_metric_handler.cpp | 11 ++++++----- 3 files changed, 10 insertions(+), 9 deletions(-) diff --git a/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_scheduler_cell_metrics_consumers.cpp b/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_scheduler_cell_metrics_consumers.cpp index a24419d828..a864fe15b6 100644 --- a/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_scheduler_cell_metrics_consumers.cpp +++ b/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_scheduler_cell_metrics_consumers.cpp @@ -405,15 +405,15 @@ void scheduler_cell_metrics_consumer_log::handle_metric(const app_services::metr " ul_error_rate={}%", ul_total > 0 ? to_percentage(ue.ul_nof_nok, ul_total) : 0); if (ul_total > 0) { - fmt::format_to(std::back_inserter(buffer), " crc_delay_ms={:.3}", ue.ul_delay_ms); + fmt::format_to(std::back_inserter(buffer), " crc_delay={:.3}ms", ue.ul_delay_ms); } else { - fmt::format_to(std::back_inserter(buffer), " crc_delay_ms=n/a"); + fmt::format_to(std::back_inserter(buffer), " crc_delay=n/a"); } fmt::format_to(std::back_inserter(buffer), " ul_nof_prbs={}", ue.tot_ul_prbs_used); fmt::format_to(std::back_inserter(buffer), " bsr={}", scaled_fmt_integer(ue.bsr, false)); fmt::format_to(std::back_inserter(buffer), " sr_count={}", ue.sr_count); if (ue.last_ul_olla.has_value()) { - fmt::format_to(std::back_inserter(buffer), " ul_olla={}", ue.last_ul_olla); + fmt::format_to(std::back_inserter(buffer), " ul_olla={}", ue.last_ul_olla.value()); } if (ue.ta_stats.get_nof_observations() > 0) { fmt::format_to(std::back_inserter(buffer), " ta={}s", float_to_eng_string(ue.ta_stats.get_mean(), 0, false)); diff --git a/lib/scheduler/logging/scheduler_event_logger.cpp b/lib/scheduler/logging/scheduler_event_logger.cpp index b5610647e8..ddecd0218c 100644 --- a/lib/scheduler/logging/scheduler_event_logger.cpp +++ b/lib/scheduler/logging/scheduler_event_logger.cpp @@ -222,7 +222,7 @@ void scheduler_event_logger::enqueue_impl(const crc_event& crc_ev) if (mode == debug) { if (crc_ev.ul_sinr_db.has_value()) { fmt::format_to(std::back_inserter(fmtbuf), - "\n- CRC: ue={} rnti={} pci={} rx_slot={} h_id={} crc={} sinr={}dB", + "\n- CRC: ue={} rnti={} pci={} rx_slot={} h_id={} crc={} sinr={:.2}dB", fmt::underlying(crc_ev.ue_index), crc_ev.rnti, pci, diff --git a/lib/scheduler/logging/scheduler_metric_handler.cpp b/lib/scheduler/logging/scheduler_metric_handler.cpp index 1a963c8c76..1a9c4c686e 100644 --- a/lib/scheduler/logging/scheduler_metric_handler.cpp +++ b/lib/scheduler/logging/scheduler_metric_handler.cpp @@ -204,9 +204,9 @@ void cell_metrics_handler::handle_ul_phr_indication(const ul_phr_indication_mess // Store last PHR. if (not phr_ind.phr.get_phr().empty()) { // Log the floor of the average of the PH interval. - interval rg = phr_ind.phr.get_phr().front().ph; - u.last_phr = (rg.start() + rg.stop()) / 2; - u.data.sum_ul_ce_delay_slots = last_slot_tx - phr_ind.slot_rx; + interval rg = phr_ind.phr.get_phr().front().ph; + u.last_phr = (rg.start() + rg.stop()) / 2; + u.data.sum_ul_ce_delay_slots += last_slot_tx - phr_ind.slot_rx; u.data.nof_ul_ces++; } } @@ -382,8 +382,9 @@ cell_metrics_handler::ue_metric_context::compute_report(std::chrono::millisecond ret.pucch_ta_stats = data.pucch_ta; ret.srs_ta_stats = data.srs_ta; ret.last_phr = last_phr; - ret.mean_ce_delay_msec = - data.nof_ul_ces > 0 ? (static_cast(data.sum_ul_ce_delay_slots) / (data.nof_ul_ces * slots_per_sf)) : 0; + if (data.nof_ul_ces > 0) { + ret.mean_ce_delay_msec = (static_cast(data.sum_ul_ce_delay_slots) / (data.nof_ul_ces * slots_per_sf)); + } // Reset UE stats metrics on every report. reset(); From 09ea31d00c21a37d7845192fb0f97e3c9ff2840f Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 2 Jan 2025 20:01:39 +0100 Subject: [PATCH 062/107] sched: refacto crc delay computation --- ..._high_scheduler_cell_metrics_consumers.cpp | 2 +- include/srsran/scheduler/scheduler_metrics.h | 2 +- .../e2sm_kpm_du_meas_provider_impl.cpp | 6 +++--- .../logging/scheduler_metric_handler.cpp | 20 ++++++++----------- .../logging/scheduler_metrics_handler.h | 6 ++---- .../ue_scheduling/ue_event_manager.cpp | 8 ++------ .../e2sm_kpm_meas_provider_metrics_test.cpp | 2 +- .../scheduler_metrics_handler_test.cpp | 8 ++++---- 8 files changed, 22 insertions(+), 32 deletions(-) diff --git a/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_scheduler_cell_metrics_consumers.cpp b/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_scheduler_cell_metrics_consumers.cpp index a864fe15b6..cdf4ccb3f6 100644 --- a/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_scheduler_cell_metrics_consumers.cpp +++ b/apps/units/flexible_o_du/o_du_high/du_high/metrics/du_high_scheduler_cell_metrics_consumers.cpp @@ -405,7 +405,7 @@ void scheduler_cell_metrics_consumer_log::handle_metric(const app_services::metr " ul_error_rate={}%", ul_total > 0 ? to_percentage(ue.ul_nof_nok, ul_total) : 0); if (ul_total > 0) { - fmt::format_to(std::back_inserter(buffer), " crc_delay={:.3}ms", ue.ul_delay_ms); + fmt::format_to(std::back_inserter(buffer), " crc_delay={:.3}ms", ue.crc_delay_ms); } else { fmt::format_to(std::back_inserter(buffer), " crc_delay=n/a"); } diff --git a/include/srsran/scheduler/scheduler_metrics.h b/include/srsran/scheduler/scheduler_metrics.h index 69f8a72dd8..03788f9aa2 100644 --- a/include/srsran/scheduler/scheduler_metrics.h +++ b/include/srsran/scheduler/scheduler_metrics.h @@ -36,7 +36,7 @@ struct scheduler_ue_metrics { sch_mcs_index ul_mcs; unsigned tot_ul_prbs_used; double ul_brate_kbps; - double ul_delay_ms; + double crc_delay_ms; unsigned ul_nof_ok; unsigned ul_nof_nok; unsigned bsr; diff --git a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.cpp b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.cpp index 621dcd9be4..3494a9b513 100644 --- a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.cpp +++ b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_du_meas_provider_impl.cpp @@ -601,7 +601,7 @@ bool e2sm_kpm_du_meas_provider_impl::get_delay_ul(const asn1::e2sm::label_info_l std::accumulate(last_ue_metrics.begin(), last_ue_metrics.end(), 0, - [](size_t sum, const scheduler_ue_metrics& metric) { return sum + metric.ul_delay_ms; }) / + [](size_t sum, const scheduler_ue_metrics& metric) { return sum + metric.crc_delay_ms; }) / last_ue_metrics.size(); meas_record_item_c meas_record_item; if (mean_ul_delay_ms) { @@ -617,8 +617,8 @@ bool e2sm_kpm_du_meas_provider_impl::get_delay_ul(const asn1::e2sm::label_info_l gnb_cu_ue_f1ap_id_t gnb_cu_ue_f1ap_id = int_to_gnb_cu_ue_f1ap_id(ue.gnb_du_ue_id().gnb_cu_ue_f1ap_id); uint32_t ue_idx = f1ap_ue_id_provider.get_ue_index(gnb_cu_ue_f1ap_id); meas_record_item_c meas_record_item; - if (last_ue_metrics[ue_idx].ul_delay_ms) { - meas_record_item.set_real().value = static_cast(last_ue_metrics[ue_idx].ul_delay_ms); + if (last_ue_metrics[ue_idx].crc_delay_ms) { + meas_record_item.set_real().value = static_cast(last_ue_metrics[ue_idx].crc_delay_ms); } else { meas_record_item.set_no_value(); } diff --git a/lib/scheduler/logging/scheduler_metric_handler.cpp b/lib/scheduler/logging/scheduler_metric_handler.cpp index 1a9c4c686e..1b78ea95d8 100644 --- a/lib/scheduler/logging/scheduler_metric_handler.cpp +++ b/lib/scheduler/logging/scheduler_metric_handler.cpp @@ -60,7 +60,9 @@ void cell_metrics_handler::handle_rach_indication(const rach_indication_message& } } -void cell_metrics_handler::handle_crc_indication(const ul_crc_pdu_indication& crc_pdu, units::bytes tbs) +void cell_metrics_handler::handle_crc_indication(slot_point sl_rx, + const ul_crc_pdu_indication& crc_pdu, + units::bytes tbs) { if (ues.contains(crc_pdu.ue_index)) { auto& u = ues[crc_pdu.ue_index]; @@ -81,6 +83,7 @@ void cell_metrics_handler::handle_crc_indication(const ul_crc_pdu_indication& cr u.data.ta.update(crc_pdu.time_advance_offset.value().to_seconds()); u.data.pusch_ta.update(crc_pdu.time_advance_offset.value().to_seconds()); } + u.data.sum_crc_delay_slots += last_slot_tx - sl_rx; } } @@ -314,13 +317,6 @@ void cell_metrics_handler::handle_slot_result(const sched_result& slot_res decision_latency_hist[bin_idx]++; } -void cell_metrics_handler::handle_ul_delay(du_ue_index_t ue_index, double delay_ms) -{ - if (ues.contains(ue_index)) { - ues[ue_index].data.sum_ul_delay_ms += delay_ms; - } -} - void cell_metrics_handler::push_result(slot_point sl_tx, const sched_result& slot_result, std::chrono::microseconds slot_decision_latency) @@ -370,10 +366,10 @@ cell_metrics_handler::ue_metric_context::compute_report(std::chrono::millisecond ret.pucch_snr_db = data.nof_pucch_snr_reports > 0 ? data.sum_pucch_snrs / data.nof_pucch_snr_reports : 0; ret.last_dl_olla = last_dl_olla; ret.last_ul_olla = last_ul_olla; - ret.ul_delay_ms = data.count_crc_pdus > 0 ? data.sum_ul_delay_ms / data.count_crc_pdus : 0; - ret.bsr = last_bsr; - ret.sr_count = data.count_sr; - ret.dl_bs = 0; + ret.crc_delay_ms = (data.count_crc_pdus > 0) ? data.sum_crc_delay_slots / (data.count_crc_pdus * slots_per_sf) : 0; + ret.bsr = last_bsr; + ret.sr_count = data.count_sr; + ret.dl_bs = 0; for (const unsigned value : last_dl_bs) { ret.dl_bs += value; } diff --git a/lib/scheduler/logging/scheduler_metrics_handler.h b/lib/scheduler/logging/scheduler_metrics_handler.h index 011af3954e..f6fac97797 100644 --- a/lib/scheduler/logging/scheduler_metrics_handler.h +++ b/lib/scheduler/logging/scheduler_metrics_handler.h @@ -46,7 +46,7 @@ class cell_metrics_handler final : public sched_metrics_ue_configurator double sum_pusch_snrs = 0; double sum_pucch_snrs = 0; double sum_pusch_rsrp = 0; - double sum_ul_delay_ms = 0; + unsigned sum_crc_delay_slots = 0; unsigned nof_pucch_snr_reports = 0; unsigned nof_pusch_snr_reports = 0; unsigned nof_pusch_rsrp_reports = 0; @@ -137,7 +137,7 @@ class cell_metrics_handler final : public sched_metrics_ue_configurator void handle_rach_indication(const rach_indication_message& msg); /// \brief Register CRC indication. - void handle_crc_indication(const ul_crc_pdu_indication& crc_pdu, units::bytes tbs); + void handle_crc_indication(slot_point sl_rx, const ul_crc_pdu_indication& crc_pdu, units::bytes tbs); /// \brief Handle SRS indication. void handle_srs_indication(const srs_indication::srs_indication_pdu& srs_pdu); @@ -166,8 +166,6 @@ class cell_metrics_handler final : public sched_metrics_ue_configurator /// \brief Handle Error Indication reported to the scheduler for a given cell. void handle_error_indication(); - void handle_ul_delay(du_ue_index_t ue_index, double delay_ms); - /// \brief Handle results stored in the scheduler result and push new entry. void push_result(slot_point sl_tx, const sched_result& slot_result, std::chrono::microseconds slot_decision_latency); diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.cpp b/lib/scheduler/ue_scheduling/ue_event_manager.cpp index ebfa898c9f..b98c83b4d6 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.cpp +++ b/lib/scheduler/ue_scheduling/ue_event_manager.cpp @@ -445,10 +445,7 @@ void ue_event_manager::handle_crc_indication(const ul_crc_indication& crc_ind) if (not cell_specific_events[crc_ind.cell_index].try_push(cell_event_t{ crc_ind.crcs[i].ue_index, [this, sl_rx = crc_ind.sl_rx, crc_ptr = ind_pdu_pool->create_crc(crc_ind.crcs[i])](ue_cell& ue_cc) { - const double delay_ms = - static_cast(last_sl - sl_rx) * - (static_cast(10) / static_cast(du_cells[ue_cc.cell_index].cfg->nof_slots_per_frame)); - + // Update HARQ. const int tbs = ue_cc.handle_crc_pdu(sl_rx, *crc_ptr); if (tbs < 0) { return; @@ -471,8 +468,7 @@ void ue_event_manager::handle_crc_indication(const ul_crc_indication& crc_ind) crc_ptr->ul_sinr_dB}); // Notify metrics handler. - du_cells[ue_cc.cell_index].metrics->handle_crc_indication(*crc_ptr, units::bytes{(unsigned)tbs}); - du_cells[ue_cc.cell_index].metrics->handle_ul_delay(crc_ptr->ue_index, delay_ms); + du_cells[ue_cc.cell_index].metrics->handle_crc_indication(sl_rx, *crc_ptr, units::bytes{(unsigned)tbs}); }, "CRC", true})) { diff --git a/tests/unittests/e2/e2sm_kpm_meas_provider_metrics_test.cpp b/tests/unittests/e2/e2sm_kpm_meas_provider_metrics_test.cpp index 370662fe08..51313ee873 100644 --- a/tests/unittests/e2/e2sm_kpm_meas_provider_metrics_test.cpp +++ b/tests/unittests/e2/e2sm_kpm_meas_provider_metrics_test.cpp @@ -85,7 +85,7 @@ scheduler_cell_metrics generate_non_zero_sched_metrics() ue_metrics.rnti = static_cast(0x1000 + 1); ue_metrics.tot_dl_prbs_used = 1200; ue_metrics.tot_ul_prbs_used = 1200; - ue_metrics.ul_delay_ms = 100; + ue_metrics.crc_delay_ms = 100; ue_metrics.pusch_snr_db = 10; for (auto i = 0; i < 10; i++) { ue_metrics.cqi_stats.update(i); diff --git a/tests/unittests/scheduler/scheduler_metrics_handler_test.cpp b/tests/unittests/scheduler/scheduler_metrics_handler_test.cpp index ef7b74cc80..173065c5eb 100644 --- a/tests/unittests/scheduler/scheduler_metrics_handler_test.cpp +++ b/tests/unittests/scheduler/scheduler_metrics_handler_test.cpp @@ -139,11 +139,11 @@ TEST_F(scheduler_metrics_handler_tester, compute_nof_ul_oks_and_noks) crc_pdu.ue_index = test_ue_index; crc_pdu.tb_crc_success = true; for (unsigned i = 0; i != nof_acks; ++i) { - metrics.handle_crc_indication(crc_pdu, units::bytes{1}); + metrics.handle_crc_indication(next_sl_tx - 1, crc_pdu, units::bytes{1}); } crc_pdu.tb_crc_success = false; for (unsigned i = 0; i != nof_nacks; ++i) { - metrics.handle_crc_indication(crc_pdu, units::bytes{1}); + metrics.handle_crc_indication(next_sl_tx - 1, crc_pdu, units::bytes{1}); } this->get_next_metric(); @@ -203,7 +203,7 @@ TEST_F(scheduler_metrics_handler_tester, compute_bitrate) crc_pdu.rnti = to_rnti(0x4601); crc_pdu.ue_index = test_ue_index; crc_pdu.tb_crc_success = true; - metrics.handle_crc_indication(crc_pdu, ul_tbs); + metrics.handle_crc_indication(next_sl_tx - 1, crc_pdu, ul_tbs); this->get_next_metric(); scheduler_ue_metrics ue_metrics = metrics_notif.last_report.ue_metrics[0]; @@ -224,7 +224,7 @@ TEST_F(scheduler_metrics_handler_tester, compute_bitrate) crc_pdu.rnti = to_rnti(0x4601); crc_pdu.ue_index = test_ue_index; crc_pdu.tb_crc_success = false; - metrics.handle_crc_indication(crc_pdu, ul_tbs); + metrics.handle_crc_indication(next_sl_tx - 1, crc_pdu, ul_tbs); this->get_next_metric(); ue_metrics = metrics_notif.last_report.ue_metrics[0]; From 572153953f6da44b904984651b61490e9cb6e8f5 Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 2 Jan 2025 22:28:35 +0100 Subject: [PATCH 063/107] sched: fix spurious KOs during UE creation --- .../ue_scheduling/ue_fallback_scheduler.cpp | 23 +++++++++++-------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp index 15aa057f37..b75588f7b1 100644 --- a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp +++ b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp @@ -594,17 +594,20 @@ ue_fallback_scheduler::alloc_grant(ue& u, } // Allocate PUCCH resources. - // NOTEs: - // - The dedicated resources are needed as, at this point, the UE object in the scheduler (not the actual terminal) - // could have a complete configuration; if so, (i) the UCI scheduler can start allocating SRs and CSIs in advance, as - // the scheduler doesn't know exactly when the UE will start transmitting SRs and CSIs; (ii) if the actual UE has - // received the RRCSetup (with configuration) but the GNB doesn't receive an ACK = 1, the UE can use the PUCCH - // dedicated resource to ack the RRCSetup retx (as per TS 38.213, Section 9.2.1, "If a UE has dedicated PUCCH resource - // configuration, the UE is provided by higher layers with one or more PUCCH resources [...]"). - // - If the UE object in the scheduler doesn't have a complete configuration (i.e., when SRB0 is for RRC Reject), - // don't use the PUCCH ded. resources. + // Note: In case of RRC Reconfiguration, common PUCCH is not required, as the UE already has a dedicated config. + // Note: In case the UE has no full config (RRC Reject), dedicated PUCCH is not required. + // Note: If the actual UE has received the RRCSetup (with config) but the gNB doesn't receive an ACK=1, the UE can use + // the PUCCH dedicated resource to ACK the RRCSetup Retx (as per ts 38.213, section 9.2.1, "if a ue has dedicated + // PUCCH resource configuration, the UE is provided by higher layers with one or more PUCCH resources [...]") + // Note: The confirmation of UE fallback exit coming from higher layers may be late. In such case, we err on the side + // of caution and allocate a dedicated PUCCH as well. We do not need to do this for the CON RES CE or SRB0 + // allocations. const bool use_common = not u.is_reconfig_ongoing(); - const bool use_dedicated = u.is_reconfig_ongoing() or (u.ue_cfg_dedicated()->is_ue_cfg_complete() and is_retx); + bool use_dedicated = not use_common; + if (u.ue_cfg_dedicated()->is_ue_cfg_complete()) { + use_dedicated |= + is_retx or (dci_type == dci_dl_rnti_config_type::c_rnti_f1_0 and u.has_pending_dl_newtx_bytes(LCID_SRB1)); + } std::optional uci = allocate_ue_fallback_pucch(u, res_alloc, pucch_alloc, From 3ecdf57297084cafa46a4eb27afbdbf964a7e58f Mon Sep 17 00:00:00 2001 From: frankist Date: Tue, 7 Jan 2025 16:24:30 +0100 Subject: [PATCH 064/107] sched: fix comment --- lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp index b75588f7b1..6b7d46a742 100644 --- a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp +++ b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp @@ -594,7 +594,8 @@ ue_fallback_scheduler::alloc_grant(ue& u, } // Allocate PUCCH resources. - // Note: In case of RRC Reconfiguration, common PUCCH is not required, as the UE already has a dedicated config. + // Note: In case of RRC Reconfiguration that is not after the RRC Reestablishment, common PUCCH is not required, as + // the UE already has a dedicated config. // Note: In case the UE has no full config (RRC Reject), dedicated PUCCH is not required. // Note: If the actual UE has received the RRCSetup (with config) but the gNB doesn't receive an ACK=1, the UE can use // the PUCCH dedicated resource to ACK the RRCSetup Retx (as per ts 38.213, section 9.2.1, "if a ue has dedicated From 5bcae7ac02ffba106cc65504322608095bd1dea2 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 20 Dec 2024 13:09:37 +0100 Subject: [PATCH 065/107] phy: PUSCH processor benchmark is not asynchronous by default --- .../channel_processors/pusch/pusch_processor_benchmark.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp index 482605c38d..7c5b887eaf 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp @@ -114,7 +114,7 @@ static dmrs_type dmrs = dmrs_typ static unsigned nof_cdm_groups_without_data = 2; static bounded_bitset dmrs_symbol_mask = {false, false, true, false, false, false, false, false, false, false, false, false, false, false}; -static unsigned nof_pusch_decoder_threads = 8; +static unsigned nof_pusch_decoder_threads = 0; static std::unique_ptr> worker_pool = nullptr; static std::unique_ptr> executor = nullptr; From c5d85cbad21d2cadb1ff02521cf52ed12352f6cc Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 20 Dec 2024 13:14:10 +0100 Subject: [PATCH 066/107] phy: reduce PUSCH processor benchmark sleep times --- .../channel_processors/pusch/pusch_processor_benchmark.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp index 7c5b887eaf..93332bdd34 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp @@ -53,7 +53,7 @@ class pusch_processor_result_notifier_adaptor : public pusch_processor_result_no void wait_for_completion() { while (!completed.load()) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); + std::this_thread::sleep_for(std::chrono::microseconds(1)); } } @@ -664,7 +664,7 @@ static void thread_process(pusch_processor& proc, // Wait for pending to non-negative. while (pending_count.load() <= 0) { // Sleep. - std::this_thread::sleep_for(std::chrono::microseconds(10)); + std::this_thread::sleep_for(std::chrono::microseconds(1)); // Quit if signaled. if (thread_quit) { @@ -817,7 +817,7 @@ int main(int argc, char** argv) // Wait for finish thread init. while (pending_count.load() != -static_cast(nof_threads)) { - std::this_thread::sleep_for(std::chrono::microseconds(10)); + std::this_thread::sleep_for(std::chrono::microseconds(1)); } // Calculate the peak throughput, considering that the number of bits is for a slot. From 6b68ef696660b5fc91ebf5c1c2c1882a34f3e04f Mon Sep 17 00:00:00 2001 From: frankist Date: Mon, 6 Jan 2025 11:21:54 +0100 Subject: [PATCH 067/107] sched: refactor logical channel config --- include/srsran/ran/qos/five_qi_qos_mapping.h | 10 +++++++ .../scheduler/config/logical_channel_config.h | 29 ++++++++++++++----- .../config/logical_channel_config_factory.h | 2 -- .../srsran/scheduler/scheduler_configurator.h | 14 --------- .../scheduler_configuration_helpers.cpp | 10 ++----- lib/scheduler/config/ue_configuration.cpp | 4 --- lib/scheduler/config/ue_configuration.h | 6 ---- lib/scheduler/policy/scheduler_time_pf.cpp | 20 +++++-------- lib/scheduler/slicing/slice_ue_repository.h | 2 +- .../ue_context/dl_logical_channel_manager.cpp | 15 ++++++++-- .../scheduler/scheduler_config_helper.cpp | 5 ---- .../scheduler/multi_slice_scheduler_test.cpp | 2 -- .../policy/scheduler_policy_test.cpp | 13 ++++----- .../ue_scheduling/ue_grid_allocator_test.cpp | 15 ++++------ 14 files changed, 68 insertions(+), 79 deletions(-) diff --git a/include/srsran/ran/qos/five_qi_qos_mapping.h b/include/srsran/ran/qos/five_qi_qos_mapping.h index c640ab30ac..5b3952b441 100644 --- a/include/srsran/ran/qos/five_qi_qos_mapping.h +++ b/include/srsran/ran/qos/five_qi_qos_mapping.h @@ -20,6 +20,9 @@ namespace srsran { /// non-GBR. See TS 23.501, clause 5.7.3.2 Resource Type. enum class qos_flow_resource_type { gbr, non_gbr, delay_critical_gbr }; +/// Maximum QoS Priority Level as per TS 38.471. +constexpr static uint8_t MAX_QOS_PRIORITY_LEVEL = 127; + /// \brief Represents 5G QoS characteristics associated with a standardized 5QI, as per TS 23.501 5.7.4-1. struct standardized_qos_characteristics { qos_flow_resource_type res_type; @@ -39,6 +42,13 @@ struct standardized_qos_characteristics { /// MDBV denotes the largest amount of data that the 5G-AN is required to serve within a period of 5G-AN PDB (i.e. /// 5G-AN part of the PDB). See TS 23.501, clause 5.7.3.7. std::optional max_data_burst_volume; + + bool operator==(const standardized_qos_characteristics& rhs) const + { + return res_type == rhs.res_type && qos_priority_level == rhs.qos_priority_level && + packet_delay_budget_ms == rhs.packet_delay_budget_ms && per == rhs.per && + average_window_ms == rhs.average_window_ms && max_data_burst_volume == rhs.max_data_burst_volume; + } }; /// \brief Returns the standardized 5QI to QoS characteristics mapping from TS 23.501, table 5.7.4-1 based on given 5QI. diff --git a/include/srsran/scheduler/config/logical_channel_config.h b/include/srsran/scheduler/config/logical_channel_config.h index 93b477209f..98eac79a7c 100644 --- a/include/srsran/scheduler/config/logical_channel_config.h +++ b/include/srsran/scheduler/config/logical_channel_config.h @@ -11,6 +11,8 @@ #pragma once #include "srsran/ran/logical_channel/lcid.h" +#include "srsran/ran/qos/five_qi_qos_mapping.h" +#include "srsran/ran/qos/qos_parameters.h" #include "srsran/ran/rrm.h" #include "srsran/ran/sr_configuration.h" #include "srsran/scheduler/config/logical_channel_group.h" @@ -18,21 +20,34 @@ namespace srsran { /// \c LogicalChannelConfig, TS 38.331. +/// Information relative to the scheduling of a logical channel in the MAC scheduler. struct logical_channel_config { - lcid_t lcid; - uint8_t priority; + /// QoS specific features associated with a logical channel (only used by DRBs). + struct qos_info { + /// QoS characteristics associated with the logical channel. + standardized_qos_characteristics qos; + /// QoS information present only for GBR QoS flows. + std::optional gbr_qos_info; + + bool operator==(const qos_info& rhs) const { return qos == rhs.qos && gbr_qos_info == rhs.gbr_qos_info; } + }; + + lcid_t lcid; + lcg_id_t lc_group; + /// Slice associated with this Bearer. + rrm_policy_member rrm_policy; + /// QoS information associated with this logical channel. + std::optional qos; // TODO: add remaining fields; - lcg_id_t lc_group; std::optional sr_id; bool lc_sr_mask; bool lc_sr_delay_timer_applied; - rrm_policy_member rrm_policy; bool operator==(const logical_channel_config& rhs) const { - return lcid == rhs.lcid and priority == rhs.priority and lc_group == rhs.lc_group and sr_id == rhs.sr_id and - lc_sr_mask == rhs.lc_sr_mask and lc_sr_delay_timer_applied == rhs.lc_sr_delay_timer_applied and - rrm_policy == rhs.rrm_policy; + return lcid == rhs.lcid and lc_group == rhs.lc_group and rrm_policy == rhs.rrm_policy and qos == rhs.qos and + sr_id == rhs.sr_id and lc_sr_mask == rhs.lc_sr_mask and + lc_sr_delay_timer_applied == rhs.lc_sr_delay_timer_applied; } }; diff --git a/include/srsran/scheduler/config/logical_channel_config_factory.h b/include/srsran/scheduler/config/logical_channel_config_factory.h index 2ca49943c4..ede6333c4f 100644 --- a/include/srsran/scheduler/config/logical_channel_config_factory.h +++ b/include/srsran/scheduler/config/logical_channel_config_factory.h @@ -22,10 +22,8 @@ constexpr logical_channel_config create_default_logical_channel_config(lcid_t lc lc_ch.lcid = lcid; // See TS 38.331, 9.2.1 Default SRB configurations. if (is_srb(lcid)) { - lc_ch.priority = lcid == LCID_SRB2 ? 3 : 1; lc_ch.lc_group = uint_to_lcg_id(0); } else { - lc_ch.priority = 5; lc_ch.lc_group = uint_to_lcg_id(2); } lc_ch.lc_sr_mask = false; diff --git a/include/srsran/scheduler/scheduler_configurator.h b/include/srsran/scheduler/scheduler_configurator.h index e816f988a6..496ab99f56 100644 --- a/include/srsran/scheduler/scheduler_configurator.h +++ b/include/srsran/scheduler/scheduler_configurator.h @@ -119,18 +119,6 @@ struct sched_ue_resource_alloc_config { rrm_policy_ratio_group rrm_policy_group; }; -/// QoS and slicing information associated with a DRB provided to the scheduler. -struct sched_drb_info { - /// Logical Channel ID. - lcid_t lcid; - /// Single Network Slice Selection Assistance Information (S-NSSAI). - s_nssai_t s_nssai; - /// QoS characteristics associated with the logical channel. - standardized_qos_characteristics qos_info; - /// QoS information present only for GBR QoS flows. - std::optional gbr_qos_info; -}; - /// Request for a new UE configuration provided to the scheduler during UE creation or reconfiguration. struct sched_ue_config_request { /// List of configured Logical Channels. See \c mac-LogicalChannelConfig, TS38.331. @@ -141,8 +129,6 @@ struct sched_ue_config_request { std::optional> cells; /// Resource allocation configuration for the given UE. std::optional res_alloc_cfg; - /// List of QoS and slicing information for DRBs. - std::vector drb_info_list; /// DRX-Config. std::optional drx_cfg; /// measGapConfig. diff --git a/lib/du/du_high/du_manager/converters/scheduler_configuration_helpers.cpp b/lib/du/du_high/du_manager/converters/scheduler_configuration_helpers.cpp index e634bd1739..35bcc6b462 100644 --- a/lib/du/du_high/du_manager/converters/scheduler_configuration_helpers.cpp +++ b/lib/du/du_high/du_manager/converters/scheduler_configuration_helpers.cpp @@ -94,7 +94,6 @@ sched_ue_config_request srsran::srs_du::create_scheduler_ue_config_request(const for (const auto& srb : ue_res_cfg.srbs) { auto& sched_lc_ch = sched_cfg.lc_config_list->emplace_back( config_helpers::create_default_logical_channel_config(srb_id_to_lcid(srb.srb_id))); - sched_lc_ch.priority = srb.mac_cfg.priority; sched_lc_ch.lc_group = srb.mac_cfg.lcg_id; sched_lc_ch.lc_sr_mask = srb.mac_cfg.lc_sr_mask; sched_lc_ch.lc_sr_delay_timer_applied = srb.mac_cfg.lc_sr_delay_applied; @@ -103,18 +102,15 @@ sched_ue_config_request srsran::srs_du::create_scheduler_ue_config_request(const for (const auto& drb : ue_res_cfg.drbs) { auto& sched_lc_ch = sched_cfg.lc_config_list->emplace_back(config_helpers::create_default_logical_channel_config(drb.lcid)); - sched_lc_ch.priority = drb.mac_cfg.priority; sched_lc_ch.lc_group = drb.mac_cfg.lcg_id; sched_lc_ch.lc_sr_mask = drb.mac_cfg.lc_sr_mask; sched_lc_ch.lc_sr_delay_timer_applied = drb.mac_cfg.lc_sr_delay_applied; sched_lc_ch.sr_id.emplace(drb.mac_cfg.sr_id); sched_lc_ch.rrm_policy.s_nssai = drb.s_nssai; sched_lc_ch.rrm_policy.plmn_id = ue_ctx.nr_cgi.plmn_id; - sched_cfg.drb_info_list.emplace_back( - sched_drb_info{.lcid = drb.lcid, - .s_nssai = drb.s_nssai, - .qos_info = *get_5qi_to_qos_characteristics_mapping(drb.qos.qos_desc.get_5qi()), - .gbr_qos_info = drb.qos.gbr_qos_info}); + sched_lc_ch.qos.emplace(); + sched_lc_ch.qos->qos = *get_5qi_to_qos_characteristics_mapping(drb.qos.qos_desc.get_5qi()); + sched_lc_ch.qos->gbr_qos_info = drb.qos.gbr_qos_info; } sched_cfg.drx_cfg = ue_res_cfg.cell_group.mcg_cfg.drx_cfg; sched_cfg.meas_gap_cfg = ue_res_cfg.meas_gap; diff --git a/lib/scheduler/config/ue_configuration.cpp b/lib/scheduler/config/ue_configuration.cpp index 40b1965481..3740c394ee 100644 --- a/lib/scheduler/config/ue_configuration.cpp +++ b/lib/scheduler/config/ue_configuration.cpp @@ -815,10 +815,6 @@ void ue_configuration::update(const cell_common_configuration_list& common_cells if (cfg_req.lc_config_list.has_value()) { lc_list = cfg_req.lc_config_list.value(); } - // Update QoS and slice information of DRBs. - if (not cfg_req.drb_info_list.empty()) { - drb_qos_list = cfg_req.drb_info_list; - } // Update DRX config ue_drx_cfg = cfg_req.drx_cfg; diff --git a/lib/scheduler/config/ue_configuration.h b/lib/scheduler/config/ue_configuration.h index 0e542eb8dc..21e81837cc 100644 --- a/lib/scheduler/config/ue_configuration.h +++ b/lib/scheduler/config/ue_configuration.h @@ -295,9 +295,6 @@ class ue_configuration /// \remark UE can be scheduled in fallback scheduler even if UE does not have a complete configuration. bool is_ue_cfg_complete() const; - /// Get QoS information of DRBs configured for the UE. - span drbs_qos_info() const { return drb_qos_list; } - /// Get DRX configuration for the UE cell group. const std::optional& drx_cfg() const { return ue_drx_cfg; } @@ -305,9 +302,6 @@ class ue_configuration // List of configured logical channels std::vector lc_list; - // List of QoS and slice information for DRBs. - std::vector drb_qos_list; - // List of cells configured for a UE. slotted_id_vector> du_cells; diff --git a/lib/scheduler/policy/scheduler_time_pf.cpp b/lib/scheduler/policy/scheduler_time_pf.cpp index 5ec66c6828..326dc33188 100644 --- a/lib/scheduler/policy/scheduler_time_pf.cpp +++ b/lib/scheduler/policy/scheduler_time_pf.cpp @@ -262,23 +262,20 @@ static double to_bytes_per_slot(uint64_t bitrate_bps, subcarrier_spacing bwp_scs /// \brief Computes DL rate weight used in computation of DL priority value for a UE in a slot. static double compute_dl_rate_weight(const slice_ue& u, span dl_avg_rate_per_lc, subcarrier_spacing bwp_scs) { - span drbs_qos_info = u.get_drbs_qos_info(); // [Implementation-defined] Rate weight to assign when average rate in all GBR bearers is zero or if the UE has only // non-GBR bearers. const double initial_rate_weight = 1; double rate_weight = 0; - for (const sched_drb_info& drb_qos_info : drbs_qos_info) { - lcid_t lcid = drb_qos_info.lcid; - + for (const logical_channel_config& lc : u.logical_channels()) { // LC not part of the slice or non-GBR flow. - if (not u.contains(drb_qos_info.lcid) or not drb_qos_info.gbr_qos_info.has_value()) { + if (not u.contains(lc.lcid) or not lc.qos.has_value() or not lc.qos->gbr_qos_info.has_value()) { continue; } // GBR flow. - if (dl_avg_rate_per_lc[lcid] != 0) { - rate_weight += (to_bytes_per_slot(drb_qos_info.gbr_qos_info->gbr_dl, bwp_scs) / dl_avg_rate_per_lc[lcid]); + if (dl_avg_rate_per_lc[lc.lcid] != 0) { + rate_weight += (to_bytes_per_slot(lc.qos->gbr_qos_info->gbr_dl, bwp_scs) / dl_avg_rate_per_lc[lc.lcid]); } } @@ -288,22 +285,21 @@ static double compute_dl_rate_weight(const slice_ue& u, span dl_avg_rate /// \brief Computes UL rate weight used in computation of UL priority value for a UE in a slot. static double compute_ul_rate_weight(const slice_ue& u, span ul_avg_rate_per_lcg, subcarrier_spacing bwp_scs) { - span drbs_qos_info = u.get_drbs_qos_info(); // [Implementation-defined] Rate weight to assign if the UE has only non-GBR bearers or average UL rate of UE is 0. const double initial_rate_weight = 1; double rate_weight = 0; // Compute sum of GBR rates of all LCs belonging to this slice. - for (const sched_drb_info& drb_qos_info : drbs_qos_info) { + for (const logical_channel_config& lc : u.logical_channels()) { // LC is not part of the slice or a non-GBR flow. - if (not u.contains(drb_qos_info.lcid) and not drb_qos_info.gbr_qos_info.has_value()) { + if (not u.contains(lc.lcid) or not lc.qos.has_value() or not lc.qos->gbr_qos_info.has_value()) { continue; } // GBR flow. - lcg_id_t lcg_id = u.get_lcg_id(drb_qos_info.lcid); + lcg_id_t lcg_id = u.get_lcg_id(lc.lcid); if (ul_avg_rate_per_lcg[lcg_id] != 0) { - rate_weight += (to_bytes_per_slot(drb_qos_info.gbr_qos_info->gbr_ul, bwp_scs) / ul_avg_rate_per_lcg[lcg_id]); + rate_weight += (to_bytes_per_slot(lc.qos->gbr_qos_info->gbr_ul, bwp_scs) / ul_avg_rate_per_lcg[lcg_id]); } } diff --git a/lib/scheduler/slicing/slice_ue_repository.h b/lib/scheduler/slicing/slice_ue_repository.h index 70c2bcd2ea..f19faff8f1 100644 --- a/lib/scheduler/slicing/slice_ue_repository.h +++ b/lib/scheduler/slicing/slice_ue_repository.h @@ -90,7 +90,7 @@ class slice_ue bool has_pending_sr() const; /// Get QoS information of DRBs configured for the UE. - span get_drbs_qos_info() const { return u.ue_cfg_dedicated()->drbs_qos_info(); }; + span logical_channels() const { return u.ue_cfg_dedicated()->logical_channels(); }; /// Get an estimation of how many UL bytes were allocated per LCG for a given grant. /// diff --git a/lib/scheduler/ue_context/dl_logical_channel_manager.cpp b/lib/scheduler/ue_context/dl_logical_channel_manager.cpp index 8697418aa8..c555777088 100644 --- a/lib/scheduler/ue_context/dl_logical_channel_manager.cpp +++ b/lib/scheduler/ue_context/dl_logical_channel_manager.cpp @@ -71,6 +71,17 @@ void dl_logical_channel_manager::set_fallback_state(bool enter_fallback) fallback_state = enter_fallback; } +static uint8_t get_lc_prio(const logical_channel_config& cfg) +{ + uint8_t prio = 0; + if (is_srb(cfg.lcid)) { + prio = cfg.lcid <= LCID_SRB1 ? 0 : 1; + } else { + prio = cfg.qos.has_value() ? cfg.qos->qos.qos_priority_level : MAX_QOS_PRIORITY_LEVEL; + } + return prio; +} + void dl_logical_channel_manager::set_status(lcid_t lcid, bool active) { srsran_sanity_check(lcid < MAX_NOF_RB_LCIDS, "Max LCID value 32 exceeded"); @@ -100,7 +111,7 @@ void dl_logical_channel_manager::set_status(lcid_t lcid, bool active) sorted_channels.push_back(lcid); } std::sort(sorted_channels.begin(), sorted_channels.end(), [this](lcid_t lhs, lcid_t rhs) { - return channels[lhs].cfg->priority < channels[rhs].cfg->priority; + return get_lc_prio(*channels[lhs].cfg) < get_lc_prio(*channels[rhs].cfg); }); } @@ -145,7 +156,7 @@ void dl_logical_channel_manager::configure(span lo } } std::sort(sorted_channels.begin(), sorted_channels.end(), [this](lcid_t lhs, lcid_t rhs) { - return channels[lhs].cfg->priority < channels[rhs].cfg->priority; + return get_lc_prio(*channels[lhs].cfg) < get_lc_prio(*channels[rhs].cfg); }); } diff --git a/tests/test_doubles/scheduler/scheduler_config_helper.cpp b/tests/test_doubles/scheduler/scheduler_config_helper.cpp index 0b6deba8d9..311915eb37 100644 --- a/tests/test_doubles/scheduler/scheduler_config_helper.cpp +++ b/tests/test_doubles/scheduler/scheduler_config_helper.cpp @@ -102,11 +102,6 @@ sched_config_helper::create_default_sched_ue_creation_request(const cell_config_ if (lcid >= lcid_t::LCID_SRB2) { msg.cfg.lc_config_list->push_back(config_helpers::create_default_logical_channel_config(lcid)); } - if (not is_srb(lcid)) { - sched_drb_info drb; - drb.lcid = lcid; - msg.cfg.drb_info_list.push_back(drb); - } } return msg; diff --git a/tests/unittests/scheduler/multi_slice_scheduler_test.cpp b/tests/unittests/scheduler/multi_slice_scheduler_test.cpp index 0357233adf..27b755fa38 100644 --- a/tests/unittests/scheduler/multi_slice_scheduler_test.cpp +++ b/tests/unittests/scheduler/multi_slice_scheduler_test.cpp @@ -49,8 +49,6 @@ class base_multi_slice_scheduler_tester : public scheduler_test_simulator std::vector lcid_list(only_lcid.begin(), only_lcid.end()); auto ue_cfg = sched_config_helper::create_default_sched_ue_creation_request(builder_params, lcid_list); for (unsigned int i = 0; i < lcid_list.size(); i++) { - ue_cfg.cfg.drb_info_list[i].s_nssai = lcid_to_cfg[i].second; - auto it = std::find_if(ue_cfg.cfg.lc_config_list->begin(), ue_cfg.cfg.lc_config_list->end(), [lcid = lcid_to_cfg[i].first](const auto& l) { return l.lcid == lcid; }); diff --git a/tests/unittests/scheduler/policy/scheduler_policy_test.cpp b/tests/unittests/scheduler/policy/scheduler_policy_test.cpp index 114634888a..295a42222a 100644 --- a/tests/unittests/scheduler/policy/scheduler_policy_test.cpp +++ b/tests/unittests/scheduler/policy/scheduler_policy_test.cpp @@ -749,17 +749,14 @@ TEST_P(scheduler_pf_qos_test, pf_upholds_qos_in_dl_gbr_flows) (*cfg_req.lc_config_list)[1] = config_helpers::create_default_logical_channel_config(lcid_t::LCID_SRB1); (*cfg_req.lc_config_list)[2] = config_helpers::create_default_logical_channel_config(non_gbr_bearer_lcid); (*cfg_req.lc_config_list)[2].lc_group = lcg_id; + (*cfg_req.lc_config_list)[2].qos.emplace(); + (*cfg_req.lc_config_list)[2].qos->qos = *get_5qi_to_qos_characteristics_mapping(uint_to_five_qi(9)); (*cfg_req.lc_config_list)[3] = config_helpers::create_default_logical_channel_config(gbr_bearer_lcid); - // Increase priority for GBR bearer. - (*cfg_req.lc_config_list)[3].priority -= 1; // Put GBR bearer in a different LCG than non-GBR bearer. (*cfg_req.lc_config_list)[3].lc_group = uint_to_lcg_id(lcg_id - 1); - cfg_req.drb_info_list.resize(2); - cfg_req.drb_info_list[0] = sched_drb_info{.lcid = non_gbr_bearer_lcid, - .qos_info = *get_5qi_to_qos_characteristics_mapping(uint_to_five_qi(9))}; - cfg_req.drb_info_list[1] = sched_drb_info{.lcid = gbr_bearer_lcid, - .qos_info = *get_5qi_to_qos_characteristics_mapping(uint_to_five_qi(1)), - .gbr_qos_info = gbr_qos_flow_information{brate, brate, brate, brate}}; + (*cfg_req.lc_config_list)[3].qos.emplace(); + (*cfg_req.lc_config_list)[3].qos->qos = *get_5qi_to_qos_characteristics_mapping(uint_to_five_qi(1)); + (*cfg_req.lc_config_list)[3].qos->gbr_qos_info = gbr_qos_flow_information{brate, brate, brate, brate}; ue_ded_cell_cfg_list[0]->update(cell_cfg_list, cfg_req); // Add UE with no GBR bearer. ue& ue_with_no_gbr = add_ue(make_ue_create_req(to_du_ue_index(1), to_rnti(0x4602), {non_gbr_bearer_lcid}, lcg_id)); diff --git a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp index 87cceedf68..6703843605 100644 --- a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp @@ -516,18 +516,15 @@ TEST_P(ue_grid_allocator_tester, successfully_allocates_pdsch_with_gbr_lc_priort (*cfg_req.lc_config_list)[1] = config_helpers::create_default_logical_channel_config(lcid_t::LCID_SRB1); (*cfg_req.lc_config_list)[2] = config_helpers::create_default_logical_channel_config(non_gbr_bearer_lcid); (*cfg_req.lc_config_list)[2].lc_group = lcg_id; + (*cfg_req.lc_config_list)[2].qos.emplace(); + (*cfg_req.lc_config_list)[2].qos->qos = *get_5qi_to_qos_characteristics_mapping(uint_to_five_qi(9)); (*cfg_req.lc_config_list)[3] = config_helpers::create_default_logical_channel_config(gbr_bearer_lcid); - // Increase priority for GBR bearer. - (*cfg_req.lc_config_list)[3].priority -= 1; // Put GBR bearer in a different LCG than non-GBR bearer. (*cfg_req.lc_config_list)[3].lc_group = uint_to_lcg_id(lcg_id - 1); - cfg_req.drb_info_list.resize(2); - cfg_req.drb_info_list[0] = sched_drb_info{.lcid = non_gbr_bearer_lcid, - .qos_info = *get_5qi_to_qos_characteristics_mapping(uint_to_five_qi(9))}; - cfg_req.drb_info_list[1] = sched_drb_info{.lcid = gbr_bearer_lcid, - .qos_info = *get_5qi_to_qos_characteristics_mapping(uint_to_five_qi(1)), - .gbr_qos_info = gbr_qos_flow_information{128000, 128000, 128000, 128000}}; - ue_config_update_event ev = cfg_mng.update_ue(reconf_msg); + (*cfg_req.lc_config_list)[3].qos.emplace(); + (*cfg_req.lc_config_list)[3].qos->qos = *get_5qi_to_qos_characteristics_mapping(uint_to_five_qi(1)); + (*cfg_req.lc_config_list)[3].qos->gbr_qos_info = gbr_qos_flow_information{128000, 128000, 128000, 128000}; + ue_config_update_event ev = cfg_mng.update_ue(reconf_msg); u1.handle_reconfiguration_request({ev.next_config()}); u1.handle_config_applied(); From 8e148e7df439c4dfddb6c51372d9a3506a38c2d4 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Tue, 7 Jan 2025 10:28:35 +0100 Subject: [PATCH 068/107] phy: add NEON modulation mapper Apply CLang-format phy: fix ARM compilation --- .../upper/channel_modulation/CMakeLists.txt | 3 + .../channel_modulation_factories.cpp | 8 + .../modulation_mapper_avx512_impl.cpp | 10 +- .../modulation_mapper_neon_impl.cpp | 200 ++++++++++++++++++ .../modulation_mapper_neon_impl.h | 47 ++++ 5 files changed, 263 insertions(+), 5 deletions(-) create mode 100644 lib/phy/upper/channel_modulation/modulation_mapper_neon_impl.cpp create mode 100644 lib/phy/upper/channel_modulation/modulation_mapper_neon_impl.h diff --git a/lib/phy/upper/channel_modulation/CMakeLists.txt b/lib/phy/upper/channel_modulation/CMakeLists.txt index 18bedd626b..e575dd0ff0 100644 --- a/lib/phy/upper/channel_modulation/CMakeLists.txt +++ b/lib/phy/upper/channel_modulation/CMakeLists.txt @@ -21,6 +21,9 @@ if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") set_source_files_properties(modulation_mapper_avx512_impl.cpp PROPERTIES COMPILE_OPTIONS "-mavx512f;-mavx512bw;-mavx512vbmi;") endif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") +if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") + list(APPEND SOURCES modulation_mapper_neon_impl.cpp) +endif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") add_library(srsran_channel_modulation STATIC ${SOURCES}) target_link_libraries(srsran_channel_modulation srsran_support log_likelihood_ratio) diff --git a/lib/phy/upper/channel_modulation/channel_modulation_factories.cpp b/lib/phy/upper/channel_modulation/channel_modulation_factories.cpp index ead3efc1db..b43efccd41 100644 --- a/lib/phy/upper/channel_modulation/channel_modulation_factories.cpp +++ b/lib/phy/upper/channel_modulation/channel_modulation_factories.cpp @@ -17,6 +17,9 @@ #ifdef __x86_64__ #include "modulation_mapper_avx512_impl.h" #endif // __x86_64__ +#ifdef __aarch64__ +#include "modulation_mapper_neon_impl.h" +#endif // __aarch64__ using namespace srsran; @@ -33,6 +36,11 @@ class channel_modulation_sw_factory : public channel_modulation_factory return std::make_unique(); } #endif // __x86_64__ +#ifdef __ARM_NEON + if (cpu_supports_feature(cpu_feature::neon)) { + return std::make_unique(); + } +#endif // __ARM_NEON return std::make_unique(); } diff --git a/lib/phy/upper/channel_modulation/modulation_mapper_avx512_impl.cpp b/lib/phy/upper/channel_modulation/modulation_mapper_avx512_impl.cpp index f58e21ad0a..32cef6f57e 100644 --- a/lib/phy/upper/channel_modulation/modulation_mapper_avx512_impl.cpp +++ b/lib/phy/upper/channel_modulation/modulation_mapper_avx512_impl.cpp @@ -25,18 +25,18 @@ using namespace srsran; template void generic_modulator(ci8_t* output, __m512i input) { - // Permute indexes for interleaving the first 64 resultant values. + // Permute indices for interleaving the first 64 resultant values. static constexpr int8_t idx_[64] = {0, 64, 1, 65, 2, 66, 3, 67, 4, 68, 5, 69, 6, 70, 7, 71, 8, 72, 9, 73, 10, 74, 11, 75, 12, 76, 13, 77, 14, 78, 15, 79, 16, 80, 17, 81, 18, 82, 19, 83, 20, 84, 21, 85, 22, 86, 23, 87, 24, 88, 25, 89, 26, 90, 27, 91, 28, 92, 29, 93, 30, 94, 31, 95}; - // Permute indexes for interleaving the last 64 resultant values. + // Permute indices for interleaving the last 64 resultant values. static constexpr int8_t idx2_[64] = {32, 96, 33, 97, 34, 98, 35, 99, 36, 100, 37, 101, 38, 102, 39, 103, 40, 104, 41, 105, 42, 106, 43, 107, 44, 108, 45, 109, 46, 110, 47, 111, 48, 112, 49, 113, 50, 114, 51, 115, 52, 116, 53, 117, 54, 118, 55, 119, 56, 120, 57, 121, 58, 122, 59, 123, 60, 124, 61, 125, 62, 126, 63, 127}; - // Load permute indexes into AVX512 registers. + // Load permute indices into AVX512 registers. __m512i idx = _mm512_loadu_si512(idx_); __m512i idx2 = _mm512_loadu_si512(idx2_); @@ -68,7 +68,7 @@ void generic_modulator(ci8_t* output, __m512i input) float modulation_mapper_avx512_impl::modulate_qam64(span symbols, const bit_buffer& input) { - // Permute indexes for the different permutations. + // Permute indices for the different permutations. static constexpr int8_t shuffle_idx0[64] = { 0, 65, 66, 66, 3, 69, 70, 69, 6, 73, 74, 72, 9, 77, 78, 75, 12, 81, 82, 78, 15, 85, 86, 81, 18, 89, 90, 84, 21, 93, 94, 87, 24, 97, 98, 90, 27, 101, 102, 93, 30, 105, 106, 96, @@ -90,7 +90,7 @@ float modulation_mapper_avx512_impl::modulate_qam64(span symbols, const b 50, 49, 53, 52, 56, 55, 59, 58, 62, 61, 65, 64, 68, 67, 71, 70, 74, 73, 77, 76, 80, 79, 83, 82, 86, 85, 89, 88, 92, 91, 95, 94}; - // Load permute indexes into AVX512 registers. + // Load permute indices into AVX512 registers. __m512i idx0 = _mm512_loadu_si512(shuffle_idx0); __m512i idx1 = _mm512_loadu_si512(shuffle_idx1); __m512i idx2 = _mm512_loadu_si512(shuffle_idx2); diff --git a/lib/phy/upper/channel_modulation/modulation_mapper_neon_impl.cpp b/lib/phy/upper/channel_modulation/modulation_mapper_neon_impl.cpp new file mode 100644 index 0000000000..62d712bf21 --- /dev/null +++ b/lib/phy/upper/channel_modulation/modulation_mapper_neon_impl.cpp @@ -0,0 +1,200 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "modulation_mapper_neon_impl.h" +#include + +using namespace srsran; + +/// \brief Implements a generic QAM modulator using neon instruction sets. +/// +/// It modulates 16 symbols in every call. Modulates the bits contained in \c input. Each byte in \c input corresponds +/// to a symbol. +/// +/// \tparam QM Modulation order. +/// \param[out] output Pointer to store the modulated symbols. +/// \param[in] input Data to modulate. Only the QM least significant bits contain information. +template +void generic_modulator(ci8_t* output, uint8x16_t input) +{ + int8x16_t offset = vdupq_n_s8(-1); + int8x16x2_t result; + result.val[0] = vdupq_n_s8(0); + result.val[1] = vdupq_n_s8(0); + + for (unsigned j = 0, j_end = QM / 2; j != j_end; ++j) { + result.val[0] = vaddq_s8(result.val[0], offset); + result.val[1] = vaddq_s8(result.val[1], offset); + offset = vaddq_s8(offset, offset); + + uint8x16_t real_mask = vceqq_u8(vandq_u8(vdupq_n_u8((1U << (2U * j + 1U))), input), vdupq_n_u8(0)); + uint8x16_t imag_mask = vceqq_u8(vandq_u8(vdupq_n_u8((1U << (2U * j + 0U))), input), vdupq_n_u8(0)); + + int8x16_t real_n = vnegq_s8(result.val[0]); + int8x16_t imag_n = vnegq_s8(result.val[1]); + result.val[0] = vbslq_s8(real_mask, real_n, result.val[0]); + result.val[1] = vbslq_s8(imag_mask, imag_n, result.val[1]); + } + + vst2q_s8(reinterpret_cast(output), result); +} + +float modulation_mapper_neon_impl::modulate_qpsk(span symbols, const bit_buffer& input) +{ + const uint8_t* input_ptr = input.get_buffer().data(); + ci8_t* symbols_ptr = symbols.data(); + unsigned i_symbol = 0; + + // Performs modulation mapping in batches of 64 symbols. + for (unsigned i_symbol_end = (symbols.size() / 64) * 64; i_symbol != i_symbol_end; i_symbol += 64) { + // Load a batch of 16 bytes. + uint8x16_t in_u8 = vld1q_u8(input_ptr); + input_ptr += 16; + + // Calculate 2-bit indices: + uint8x16x4_t indices_u8; + indices_u8.val[0] = vshrq_n_u8(in_u8, 6); + indices_u8.val[1] = vandq_u8(vshrq_n_u8(in_u8, 4), vdupq_n_u8(0b11)); + indices_u8.val[2] = vandq_u8(vshrq_n_u8(in_u8, 2), vdupq_n_u8(0b11)); + indices_u8.val[3] = vandq_u8(in_u8, vdupq_n_u8(0b11)); + + // Interleave indices. + std::array indices; + vst4q_u8(indices.data(), indices_u8); + + // Modulate in 4 batches of 16 symbols each. + for (unsigned i = 0; i != 4; ++i) { + generic_modulator<2>(symbols_ptr, vld1q_u8(&indices[16 * i])); + symbols_ptr += 16; + } + } + + // Process the remaining symbols using the LUT implementation. + unsigned remainder = symbols.size() - i_symbol; + return lut_modulator.modulate(symbols.last(remainder), input.last(2 * remainder), modulation_scheme::QPSK); +} + +float modulation_mapper_neon_impl::modulate_qam16(span symbols, const bit_buffer& input) +{ + const uint8_t* input_ptr = input.get_buffer().data(); + ci8_t* symbols_ptr = symbols.data(); + unsigned i_symbol = 0; + + // Performs modulation mapping in batches of 32 symbols. + for (unsigned i_symbol_end = (symbols.size() / 32) * 32; i_symbol != i_symbol_end; i_symbol += 32) { + // Load a batch of 16 bytes. + uint8x16_t in_u8 = vld1q_u8(input_ptr); + input_ptr += 16; + + // Calculate 4-bit indices: + uint8x16x2_t indices_u8; + indices_u8.val[0] = vshrq_n_u8(in_u8, 4); + indices_u8.val[1] = vandq_u8(in_u8, vdupq_n_u8(0xf)); + + // Interleave indices. + std::array indices; + vst2q_u8(indices.data(), indices_u8); + + // Modulate in 2 batches of 16 symbols each. + for (unsigned i = 0; i != 2; ++i) { + generic_modulator<4>(symbols_ptr, vld1q_u8(&indices[16 * i])); + symbols_ptr += 16; + } + } + + // Process the remaining symbols using the LUT implementation. + unsigned remainder = symbols.size() - i_symbol; + return lut_modulator.modulate(symbols.last(remainder), input.last(4 * remainder), modulation_scheme::QAM16); +} + +float modulation_mapper_neon_impl::modulate_qam64(span symbols, const bit_buffer& input) +{ + const uint8_t* input_ptr = input.get_buffer().data(); + ci8_t* symbols_ptr = symbols.data(); + unsigned i_symbol = 0; + + // Performs modulation mapping in batches of 64 symbols. + for (unsigned i_symbol_end = (symbols.size() / 64) * 64; i_symbol != i_symbol_end; i_symbol += 64) { + // Load and deinterleave 3 groups of 16 bytes. + uint8x16x3_t in_u8 = vld3q_u8(input_ptr); + input_ptr += 48; + + // Extract deinterleaved registers. + uint8x16_t byte0 = in_u8.val[0]; + uint8x16_t byte1 = in_u8.val[1]; + uint8x16_t byte2 = in_u8.val[2]; + + // Calculate 6-bit indices: + // index0 = byte0 >> 2U + // index1 = ((byte0 & 0b11U) << 4U) | (byte1 >> 4U) + // index2 = ((byte1 & 0b1111U) << 2U) | (byte2 >> 6U); + // index3 = byte2 & 0b111111U; + uint8x16x4_t indices_u8; + indices_u8.val[0] = vshrq_n_u8(byte0, 2); + indices_u8.val[1] = vorrq_u8(vshlq_n_u8(vandq_u8(byte0, vdupq_n_u8(0b11U)), 4U), vshrq_n_u8(byte1, 4)); + indices_u8.val[2] = vorrq_u8(vshlq_n_u8(vandq_u8(byte1, vdupq_n_u8(0b1111U)), 2U), vshrq_n_u8(byte2, 6)); + indices_u8.val[3] = vandq_u8(byte2, vdupq_n_u8(0b111111U)); + + // Interleave indices. + std::array indices; + vst4q_u8(indices.data(), indices_u8); + + // Modulate in 4 batches of 16 symbols each. + for (unsigned i = 0; i != 4; ++i) { + generic_modulator<6>(symbols_ptr, vld1q_u8(&indices[16 * i])); + symbols_ptr += 16; + } + } + + // Process the remaining symbols using the LUT implementation. + unsigned remainder = symbols.size() - i_symbol; + return lut_modulator.modulate(symbols.last(remainder), input.last(6 * remainder), modulation_scheme::QAM64); +} + +float modulation_mapper_neon_impl::modulate_qam256(span symbols, const bit_buffer& input) +{ + for (unsigned i_symbol = 0, i_symbol_end = (symbols.size() / 16) * 16; i_symbol != i_symbol_end; i_symbol += 16) { + // Load input. + uint8x16_t avx_in = vld1q_u8(input.get_buffer().data() + i_symbol); + + // Call generic QAM modulation mapper based on neon instruction sets. + generic_modulator<8>(symbols.data() + i_symbol, avx_in); + } + + // Process the remaining symbols using the LUT implementation. + unsigned remainder = symbols.size() % 64; + return lut_modulator.modulate(symbols.last(remainder), input.last(8 * remainder), modulation_scheme::QAM256); +} + +void modulation_mapper_neon_impl::modulate(span symbols, const bit_buffer& input, modulation_scheme scheme) +{ + return lut_modulator.modulate(symbols, input, scheme); +} + +float modulation_mapper_neon_impl::modulate(span symbols, const bit_buffer& input, modulation_scheme scheme) +{ + if (scheme == modulation_scheme::QAM256) { + return modulate_qam256(symbols, input); + } + + if (scheme == modulation_scheme::QAM64) { + return modulate_qam64(symbols, input); + } + + if (scheme == modulation_scheme::QAM16) { + return modulate_qam16(symbols, input); + } + + if (scheme == modulation_scheme::QPSK) { + return modulate_qpsk(symbols, input); + } + + return lut_modulator.modulate(symbols, input, scheme); +} diff --git a/lib/phy/upper/channel_modulation/modulation_mapper_neon_impl.h b/lib/phy/upper/channel_modulation/modulation_mapper_neon_impl.h new file mode 100644 index 0000000000..b20b700ad9 --- /dev/null +++ b/lib/phy/upper/channel_modulation/modulation_mapper_neon_impl.h @@ -0,0 +1,47 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#pragma once + +#include "modulation_mapper_lut_impl.h" +#include "srsran/phy/upper/channel_modulation/modulation_mapper.h" +#include "srsran/support/math/math_utils.h" +#include + +namespace srsran { + +/// Modulation mapper based on NEON instruction sets. +class modulation_mapper_neon_impl : public modulation_mapper +{ +public: + // See interface for the documentation. + void modulate(span symbols, const bit_buffer& input, modulation_scheme scheme) override; + + // See interface for the documentation. + float modulate(span symbols, const bit_buffer& input, modulation_scheme scheme) override; + +private: + /// Implements the modulation algorithm for QPSK. + float modulate_qpsk(span symbols, const bit_buffer& input); + + /// Implements the modulation algorithm for 16-QAM. + float modulate_qam16(span symbols, const bit_buffer& input); + + /// Implements the modulation algorithm for 64-QAM. + float modulate_qam64(span symbols, const bit_buffer& input); + + /// Implements the modulation algorithm for 256-QAM. + float modulate_qam256(span symbols, const bit_buffer& input); + + /// Modulation mapper based on LUT for modulations that are not implemented with the NEON instruction set. + modulation_mapper_lut_impl lut_modulator; +}; + +} // namespace srsran From 650b4130251d16bbf84aa40750f53e4ebcca6658 Mon Sep 17 00:00:00 2001 From: Carlo Galiotto Date: Tue, 7 Jan 2025 17:05:16 +0100 Subject: [PATCH 069/107] du: add missing option to enable PUSCH power control Signed-off-by: Carlo Galiotto --- .../o_du_high/du_high/du_high_config_cli11_schema.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/apps/units/flexible_o_du/o_du_high/du_high/du_high_config_cli11_schema.cpp b/apps/units/flexible_o_du/o_du_high/du_high/du_high_config_cli11_schema.cpp index 5f871fb14b..508255528d 100644 --- a/apps/units/flexible_o_du/o_du_high/du_high/du_high_config_cli11_schema.cpp +++ b/apps/units/flexible_o_du/o_du_high/du_high/du_high_config_cli11_schema.cpp @@ -811,6 +811,10 @@ static void configure_cli11_pusch_args(CLI::App& app, du_high_unit_pusch_config& app.add_option("--end_rb", pusch_params.end_rb, "End RB for resource allocation of UE PUSCHs") ->capture_default_str() ->check(CLI::Range(0U, (unsigned)MAX_NOF_PRBS)); + app.add_option("--enable_cl_loop_pw_control", + pusch_params.enable_closed_loop_pw_control, + "Enable closed-loop power control for PUSCH") + ->capture_default_str(); app.add_option("--target_sinr", pusch_params.target_pusch_sinr, "Target PUSCH SINR in dB") ->capture_default_str() ->check(CLI::Range(-5.0, 30.0)); From 5a37c36f0926409d0bdf737a2da62a087882e4e2 Mon Sep 17 00:00:00 2001 From: Fabian Eckermann Date: Thu, 5 Dec 2024 14:46:00 +0100 Subject: [PATCH 070/107] cu_cp,ngap: add check for invalid ip address config in pdu session resource setup item --- lib/ngap/ngap_asn1_helpers.h | 10 +++++++- lib/ngap/ngap_validators/ngap_validators.cpp | 25 ++++++++++++++++---- 2 files changed, 29 insertions(+), 6 deletions(-) diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index 0d27e6b0c2..4061ccc670 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -266,7 +266,15 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re asn1_setup_req_transfer->pdu_session_aggr_max_bit_rate.pdu_session_aggr_max_bit_rate_ul; } - // id-UL-NGU-UP-TNLInformation + // id-UL-NGU-UP-TNLInformation. + if (asn1_setup_req_transfer->ul_ngu_up_tnl_info.type() == + asn1::ngap::up_transport_layer_info_c::types_opts::gtp_tunnel && + asn1_setup_req_transfer->ul_ngu_up_tnl_info.gtp_tunnel().transport_layer_address.length() == 160) { + srslog::fetch_basic_logger("NGAP").error("Invalid PDU Session Resource Setup Request Transfer PDU. Cause: Combined " + "IPv4 and IPv6 addresses are currently not supported"); + return false; + } + setup_item.ul_ngu_up_tnl_info = asn1_to_up_transport_layer_info(asn1_setup_req_transfer->ul_ngu_up_tnl_info); // id-PDUSessionType diff --git a/lib/ngap/ngap_validators/ngap_validators.cpp b/lib/ngap/ngap_validators/ngap_validators.cpp index 1773b749bd..85a463481c 100644 --- a/lib/ngap/ngap_validators/ngap_validators.cpp +++ b/lib/ngap/ngap_validators/ngap_validators.cpp @@ -9,6 +9,7 @@ */ #include "ngap_validators.h" +#include "srsran/ran/cause/common.h" #include using namespace srsran; @@ -23,9 +24,9 @@ pdu_session_resource_setup_validation_outcome srsran::srs_cu_cp::verify_pdu_sess std::unordered_set psis; std::unordered_set failed_psis; - for (const auto& pdu_session_item : asn1_request->pdu_session_res_setup_list_su_req) { - pdu_session_id_t psi = uint_to_pdu_session_id(pdu_session_item.pdu_session_id); - // Check for duplicate PDU Session IDs + for (const auto& asn1_pdu_session_item : asn1_request->pdu_session_res_setup_list_su_req) { + pdu_session_id_t psi = uint_to_pdu_session_id(asn1_pdu_session_item.pdu_session_id); + // Check for duplicate PDU Session IDs. if (!psis.emplace(psi).second) { ue_logger.log_warning("Duplicate {} in PduSessionResourceSetupRequest", psi); // Make sure to only add each duplicate psi once @@ -39,6 +40,20 @@ pdu_session_resource_setup_validation_outcome srsran::srs_cu_cp::verify_pdu_sess } } + // Check for unsupported PDU Session Types. + for (const auto& pdu_session_item : request.pdu_session_res_setup_items) { + if (pdu_session_item.pdu_session_type == "ipv4v6") { + ue_logger.log_warning("Unsupported PDU Session Type: ipv4v6"); + failed_psis.emplace(pdu_session_item.pdu_session_id); + // Add failed psi to response. + cu_cp_pdu_session_res_setup_failed_item failed_item; + failed_item.pdu_session_id = pdu_session_item.pdu_session_id; + failed_item.unsuccessful_transfer.cause = cause_protocol_t::unspecified; + verification_outcome.response.pdu_session_res_failed_to_setup_items.emplace(pdu_session_item.pdu_session_id, + failed_item); + } + } + // Remove failed psis from psis for (const auto& failed_psi : failed_psis) { psis.erase(failed_psi); @@ -49,8 +64,8 @@ pdu_session_resource_setup_validation_outcome srsran::srs_cu_cp::verify_pdu_sess return verification_outcome; } - // If Non-GBR QoS flow present then PDU Session Aggregate Maximum Bit Rate must be present - for (auto& psi : psis) { + // If Non-GBR QoS flow present then PDU Session Aggregate Maximum Bit Rate must be present. + for (const auto& psi : psis) { for (const auto& qos_flow_item : request.pdu_session_res_setup_items[psi].qos_flow_setup_request_items) { if (qos_flow_item.qos_flow_level_qos_params.reflective_qos_attribute_subject_to || qos_flow_item.qos_flow_level_qos_params.add_qos_flow_info) { From 7456a2004e282948f3c1cee8b659d8f9f1d06c75 Mon Sep 17 00:00:00 2001 From: Fabian Eckermann Date: Thu, 5 Dec 2024 14:47:47 +0100 Subject: [PATCH 071/107] cu_cp,ngap: add unit test for pdu session resource setup request with invalid ip config --- ..._session_resource_setup_procedure_test.cpp | 21 ++++++++++++++++++- tests/unittests/ngap/ngap_test_messages.cpp | 18 ++++++++++++++++ tests/unittests/ngap/ngap_test_messages.h | 6 ++++++ tests/unittests/ngap/ngap_validators_test.cpp | 4 ++-- 4 files changed, 46 insertions(+), 3 deletions(-) diff --git a/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp b/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp index 0a43943e05..9d5b8a8cf4 100644 --- a/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp +++ b/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp @@ -164,7 +164,26 @@ TEST_F(ngap_pdu_session_resource_setup_procedure_test, ASSERT_TRUE(was_pdu_session_resource_setup_request_invalid()); } -/// Test invalid PDU Session Resource Setup Request +/// Test invalid PDU Session Resource Setup Request. +TEST_F(ngap_pdu_session_resource_setup_procedure_test, + when_pdu_session_resource_setup_request_with_invalid_tpl_received_then_pdu_session_setup_failed) +{ + // Test preamble. + ue_index_t ue_index = this->start_procedure(); + + auto& ue = test_ues.at(ue_index); + + // Inject invalid PDU Session Resource Setup Request. + ngap_message pdu_session_resource_setup_request = + generate_pdu_session_resource_setup_request_with_pdu_session_type_ipv4_and_ipv4v6_transport_layer_address( + ue.amf_ue_id.value(), ue.ran_ue_id.value()); + ngap->handle_message(pdu_session_resource_setup_request); + + // Check that Error Indication has been sent to AMF. + ASSERT_TRUE(was_error_indication_sent()); +} + +/// Test invalid PDU Session Resource Setup Request. TEST_F(ngap_pdu_session_resource_setup_procedure_test, when_security_not_enabled_then_pdu_session_setup_failed) { // Test preamble diff --git a/tests/unittests/ngap/ngap_test_messages.cpp b/tests/unittests/ngap/ngap_test_messages.cpp index 3075847a73..6dd991d433 100644 --- a/tests/unittests/ngap/ngap_test_messages.cpp +++ b/tests/unittests/ngap/ngap_test_messages.cpp @@ -487,6 +487,24 @@ ngap_message srsran::srs_cu_cp::generate_invalid_pdu_session_resource_setup_requ return ngap_msg; } +ngap_message srsran::srs_cu_cp:: + generate_pdu_session_resource_setup_request_with_pdu_session_type_ipv4_and_ipv4v6_transport_layer_address( + amf_ue_id_t amf_ue_id, + ran_ue_id_t ran_ue_id) +{ + ngap_message ngap_msg = generate_valid_pdu_session_resource_setup_request_message( + amf_ue_id, ran_ue_id, {{uint_to_pdu_session_id(1), {{uint_to_qos_flow_id(1), 9}}}}); + + // Add invalid PDU Session Resource Setup Request Transfer. + auto& pdu_session_res_setup_req = ngap_msg.pdu.init_msg().value.pdu_session_res_setup_request(); + pdu_session_res_setup_req->pdu_session_res_setup_list_su_req.begin() + ->pdu_session_res_setup_request_transfer.from_string("0000040082000a0c1dcd6500301dcd6500008b001a09f00a0c01bbfdf66" + "cf3768500000000000000000bf1010000010086000100008800" + "0700010000093800"); + + return ngap_msg; +} + cu_cp_pdu_session_resource_setup_response srsran::srs_cu_cp::generate_cu_cp_pdu_session_resource_setup_response(pdu_session_id_t pdu_session_id) { diff --git a/tests/unittests/ngap/ngap_test_messages.h b/tests/unittests/ngap/ngap_test_messages.h index 5c17286e53..6d19b26ff8 100644 --- a/tests/unittests/ngap/ngap_test_messages.h +++ b/tests/unittests/ngap/ngap_test_messages.h @@ -156,6 +156,12 @@ ngap_message generate_valid_pdu_session_resource_setup_request_message( /// \brief Generate an invalid dummy PDU Session Resource Setup Request Message. ngap_message generate_invalid_pdu_session_resource_setup_request_message(amf_ue_id_t amf_ue_id, ran_ue_id_t ran_ue_id); +/// \brief Generate a dummy PDU Session Resource Setup Request with IPv4 PDUSessionType but IPv4v6 transport layer +/// address. +ngap_message generate_pdu_session_resource_setup_request_with_pdu_session_type_ipv4_and_ipv4v6_transport_layer_address( + amf_ue_id_t amf_ue_id, + ran_ue_id_t ran_ue_id); + /// \brief Generate a dummy PDU Session Resource Setup Response. cu_cp_pdu_session_resource_setup_response generate_cu_cp_pdu_session_resource_setup_response(pdu_session_id_t pdu_session_id); diff --git a/tests/unittests/ngap/ngap_validators_test.cpp b/tests/unittests/ngap/ngap_validators_test.cpp index 51c4992a6f..c053b90c4c 100644 --- a/tests/unittests/ngap/ngap_validators_test.cpp +++ b/tests/unittests/ngap/ngap_validators_test.cpp @@ -266,14 +266,14 @@ TEST_F( fill_cu_cp_pdu_session_resource_setup_request(request, asn1_request->pdu_session_res_setup_list_su_req); ngap_ue_logger ue_logger{"NGAP", {ue_index, ran_ue_id}}; - // Verify PDU Session Resource Setup Request + // Verify PDU Session Resource Setup Request. auto verification_outcome = verify_pdu_session_resource_setup_request(request, asn1_request, ue_logger); ASSERT_TRUE(verification_outcome.request.pdu_session_res_setup_items.empty()); ASSERT_EQ(verification_outcome.response.pdu_session_res_failed_to_setup_items.size(), 1U); } -// Test handling of valid PDU Session Resource Modification Request +// Test handling of valid PDU Session Resource Modification Request. TEST_F(ngap_validator_test, when_valid_request_received_then_pdu_session_modify_succeeds) { pdu_session_id_t psi = uint_to_pdu_session_id(1); From 39ab079ed3b77651df7ec773702b52fedac0613a Mon Sep 17 00:00:00 2001 From: Fabian Eckermann Date: Fri, 13 Dec 2024 11:02:28 +0100 Subject: [PATCH 072/107] cu_cp,ngap: fix documentation --- lib/ngap/ngap_asn1_helpers.h | 227 +++++++++--------- lib/ngap/ngap_validators/ngap_validators.cpp | 26 +- ..._session_resource_setup_procedure_test.cpp | 50 ++-- tests/unittests/ngap/ngap_test_messages.cpp | 124 +++++----- tests/unittests/ngap/ngap_validators_test.cpp | 47 ++-- 5 files changed, 234 insertions(+), 240 deletions(-) diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index 4061ccc670..ce0d3e8211 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -38,34 +38,34 @@ namespace srs_cu_cp { /// \param[in] ngap_ctxt The NGAP context. inline void fill_asn1_ng_setup_request(asn1::ngap::ng_setup_request_s& asn1_request, const ngap_context_t& ngap_ctxt) { - // fill global ran node id + // Fill global RAN node id. auto& global_gnb = asn1_request->global_ran_node_id.set_global_gnb_id(); global_gnb.gnb_id.set_gnb_id(); global_gnb.gnb_id.gnb_id().from_number(ngap_ctxt.gnb_id.id, ngap_ctxt.gnb_id.bit_length); // TODO: Which PLMN do we need to use here? global_gnb.plmn_id = ngap_ctxt.supported_tas.front().plmn_list.front().plmn_id.to_bytes(); - // fill ran node name + // Fill RAN node name. asn1_request->ran_node_name_present = true; asn1_request->ran_node_name.from_string(ngap_ctxt.ran_node_name); - // fill supported ta list + // Fill supported TA list. for (const auto& supported_ta_item : ngap_ctxt.supported_tas) { asn1::ngap::supported_ta_item_s asn1_supported_ta_item = {}; - // fill tac + // Fill TAC. asn1_supported_ta_item.tac.from_number(supported_ta_item.tac); - // fill broadcast plmn list + // Fill broadcast PLMN list. for (const auto& plmn_item : supported_ta_item.plmn_list) { asn1::ngap::broadcast_plmn_item_s asn1_broadcast_plmn_item = {}; - // fill plmn id + // Fill PLMN id. asn1_broadcast_plmn_item.plmn_id = plmn_item.plmn_id.to_bytes(); - // fill tai slice support list + // Fill TAI slice support list. for (const auto& slice_support_item : plmn_item.slice_support_list) { - // fill s_nssai + // Fill s_nssai. asn1::ngap::slice_support_item_s asn1_slice_support_item = {}; asn1_slice_support_item.s_nssai = s_nssai_to_asn1(slice_support_item); @@ -77,7 +77,7 @@ inline void fill_asn1_ng_setup_request(asn1::ngap::ng_setup_request_s& asn1_requ asn1_request->supported_ta_list.push_back(asn1_supported_ta_item); } - // fill paging drx + // Fill paging DRX. asn1::number_to_enum(asn1_request->default_paging_drx, ngap_ctxt.default_paging_drx); } @@ -88,10 +88,10 @@ inline void fill_ngap_ng_setup_result(ngap_ng_setup_result& result, const asn1:: { ngap_ng_setup_response response; - // fill amf name + // Fill AMF name response.amf_name = asn1_response->amf_name.to_string(); - // fill served guami list + // Fill served GUAMI list. for (const auto& asn1_served_guami_item : asn1_response->served_guami_list) { ngap_served_guami_item served_guami_item = {}; served_guami_item.guami = asn1_to_guami(asn1_served_guami_item.guami); @@ -101,10 +101,10 @@ inline void fill_ngap_ng_setup_result(ngap_ng_setup_result& result, const asn1:: response.served_guami_list.push_back(served_guami_item); } - // fill relative amf capacity + // Fill relative AMF capacity. response.relative_amf_capacity = asn1_response->relative_amf_capacity; - // fill plmn support list + // Fill PLMN support list. for (const auto& asn1_plmn_support_item : asn1_response->plmn_support_list) { ngap_plmn_support_item plmn_support_item = {}; plmn_support_item.plmn_id = asn1_plmn_support_item.plmn_id.to_string(); @@ -121,9 +121,9 @@ inline void fill_ngap_ng_setup_result(ngap_ng_setup_result& result, const asn1:: response.plmn_support_list.push_back(plmn_support_item); } - // TODO: Fill crit diagnostics + // TODO: Fill crit diagnostics. - // TODO: Add missing optional values + // TODO: Add missing optional values. result = response; } @@ -137,7 +137,7 @@ inline void fill_ngap_ng_setup_result(ngap_ng_setup_result& result, const asn1:: fail.cause = asn1_to_cause(asn1_fail->cause); if (asn1_fail->crit_diagnostics_present) { - // TODO: Add crit diagnostics + // TODO: Add crit diagnostics. } result = fail; @@ -213,7 +213,7 @@ inline void fill_asn1_initial_ue_message(asn1::ngap::init_ue_msg_s& asn1_ms asn1_msg->amf_set_id.from_number(msg.amf_set_id.value()); } - // TODO: Add missing optional values + // TODO: Add missing optional values. } /// \brief Convert common type UL NAS Transport message to NGAP UL NAS Transport message. @@ -238,18 +238,18 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re const template_asn1_item& asn1_session_item, byte_buffer asn1_request_transfer) { - // pDUSessionID + // Fill PDU session id. setup_item.pdu_session_id = uint_to_pdu_session_id(asn1_session_item.pdu_session_id); - // pDUSessionNAS-PDU / NAS-PDU will be added in a separate function + // pDUSessionNAS-PDU / NAS-PDU will be added in a separate function. - // s-NSSAI + // Fill s-NSSAI. if (asn1_session_item.s_nssai.sd_present) { setup_item.s_nssai.sd = slice_differentiator::create(asn1_session_item.s_nssai.sd.to_number()).value(); } setup_item.s_nssai.sst = slice_service_type{(uint8_t)asn1_session_item.s_nssai.sst.to_number()}; - // pDUSessionResourceSetupRequestTransfer + // Fill PDU session resource setup request transfer. asn1::ngap::pdu_session_res_setup_request_transfer_s asn1_setup_req_transfer; asn1::cbit_ref bref({asn1_request_transfer.begin(), asn1_request_transfer.end()}); @@ -259,7 +259,7 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re } if (asn1_setup_req_transfer->pdu_session_aggr_max_bit_rate_present) { - // id-PDUSessionAggregateMaximumBitRate + // Fill PDU session aggregate maximum bit rate. setup_item.pdu_session_aggregate_maximum_bit_rate_dl = asn1_setup_req_transfer->pdu_session_aggr_max_bit_rate.pdu_session_aggr_max_bit_rate_dl; setup_item.pdu_session_aggregate_maximum_bit_rate_ul = @@ -277,24 +277,24 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re setup_item.ul_ngu_up_tnl_info = asn1_to_up_transport_layer_info(asn1_setup_req_transfer->ul_ngu_up_tnl_info); - // id-PDUSessionType + // Fill PDU session type. setup_item.pdu_session_type = asn1_setup_req_transfer->pdu_session_type.to_string(); - // id-SecurityIndication + // Fill security indication. if (asn1_setup_req_transfer->security_ind_present) { security_indication_t ind = {}; asn1_to_security_indication(ind, asn1_setup_req_transfer->security_ind); setup_item.security_ind = ind; } - // id-QosFlowSetupRequestList + // Fill Qos flow setup request list. for (const auto& asn1_flow_item : asn1_setup_req_transfer->qos_flow_setup_request_list) { qos_flow_setup_request_item qos_flow_setup_req_item; - // qosFlowIdentifier + // Fill QoS flow identifier. qos_flow_setup_req_item.qos_flow_id = uint_to_qos_flow_id(asn1_flow_item.qos_flow_id); - // qosFlowLevelQosParameters + // Fill QoS flow level QoS parameters. if (asn1_flow_item.qos_flow_level_qos_params.qos_characteristics.type() == asn1::ngap::qos_characteristics_c::types::dyn5qi) { dyn_5qi_descriptor dyn_5qi = {}; @@ -302,11 +302,11 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re dyn_5qi.five_qi = uint_to_five_qi(asn1_flow_item.qos_flow_level_qos_params.qos_characteristics.dyn5qi().five_qi); } - // TODO: Add optional values + // TODO: Add optional values. qos_flow_setup_req_item.qos_flow_level_qos_params.qos_desc = dyn_5qi; - // TODO: Add optional values + // TODO: Add optional values. } else if (asn1_flow_item.qos_flow_level_qos_params.qos_characteristics.type() == asn1::ngap::qos_characteristics_c::types::non_dyn5qi) { @@ -315,10 +315,10 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re uint_to_five_qi(asn1_flow_item.qos_flow_level_qos_params.qos_characteristics.non_dyn5qi().five_qi); qos_flow_setup_req_item.qos_flow_level_qos_params.qos_desc = non_dyn_5qi; - // TODO: Add optional values + // TODO: Add optional values. } - // allocationAndRetentionPriority + // Fill allocation and retention priority. qos_flow_setup_req_item.qos_flow_level_qos_params.alloc_retention_prio.prio_level_arp = asn1_flow_item.qos_flow_level_qos_params.alloc_and_retention_prio.prio_level_arp; qos_flow_setup_req_item.qos_flow_level_qos_params.alloc_retention_prio.may_trigger_preemption = @@ -328,14 +328,14 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re asn1_flow_item.qos_flow_level_qos_params.alloc_and_retention_prio.pre_emption_vulnerability == asn1::ngap::pre_emption_vulnerability_opts::pre_emptable; - // Optional Parameters + // Optional parameters. if (asn1_flow_item.qos_flow_level_qos_params.add_qos_flow_info_present) { qos_flow_setup_req_item.qos_flow_level_qos_params.add_qos_flow_info = asn1_flow_item.qos_flow_level_qos_params.add_qos_flow_info.to_string(); } if (asn1_flow_item.qos_flow_level_qos_params.gbr_qos_info_present) { - // TODO: Add to common type + // TODO: Add to common type. } if (asn1_flow_item.qos_flow_level_qos_params.reflective_qos_attribute_present) { @@ -372,7 +372,7 @@ inline bool fill_cu_cp_pdu_session_resource_setup_request( return false; } - // pDUSessionNAS-PDU + // Fill PDU session NAS-PDU. if (!asn1_session_item.pdu_session_nas_pdu.empty()) { if (!setup_item.pdu_session_nas_pdu.resize(asn1_session_item.pdu_session_nas_pdu.size())) { return false; @@ -406,7 +406,7 @@ inline bool fill_cu_cp_pdu_session_resource_setup_request( return false; } - // NAS-PDU + // Fill NAS-PDU. if (!asn1_session_item.nas_pdu.empty()) { if (!setup_item.pdu_session_nas_pdu.resize(asn1_session_item.nas_pdu.size())) { return false; @@ -427,12 +427,12 @@ inline bool fill_cu_cp_pdu_session_resource_setup_request( inline bool fill_ngap_initial_context_setup_request(ngap_init_context_setup_request& request, const asn1::ngap::init_context_setup_request_s& asn1_request) { - // old_amf + // Fill old_amf. if (asn1_request->old_amf_present) { request.old_amf = asn1_request->old_amf.to_string(); } - // ue_aggr_max_bit_rate + // Fill UE aggregated max bit rate. if (asn1_request->ue_aggr_max_bit_rate_present) { request.ue_aggr_max_bit_rate.emplace(); request.ue_aggr_max_bit_rate.value().ue_aggr_max_bit_rate_dl = @@ -441,10 +441,10 @@ inline bool fill_ngap_initial_context_setup_request(ngap_init_context_setup_requ asn1_request->ue_aggr_max_bit_rate.ue_aggr_max_bit_rate_ul; } - // guami + // Fill GUAMI. request.guami = asn1_to_guami(asn1_request->guami); - // pdu_session_res_setup_list_cxt_req + // Fill PDU session resource setup list context request. if (asn1_request->pdu_session_res_setup_list_cxt_req_present) { request.pdu_session_res_setup_list_cxt_req.emplace(); if (!fill_cu_cp_pdu_session_resource_setup_request(request.pdu_session_res_setup_list_cxt_req.value(), @@ -453,39 +453,39 @@ inline bool fill_ngap_initial_context_setup_request(ngap_init_context_setup_requ } } - // allowed_nssai + // Fill allowed S-NSSAI. for (const auto& asn1_s_nssai : asn1_request->allowed_nssai) { request.allowed_nssai.push_back(ngap_asn1_to_s_nssai(asn1_s_nssai.s_nssai)); } - // security_context + // Fill security context. copy_asn1_key(request.security_context.k, asn1_request->security_key); fill_supported_algorithms(request.security_context.supported_int_algos, asn1_request->ue_security_cap.nr_integrity_protection_algorithms); fill_supported_algorithms(request.security_context.supported_enc_algos, asn1_request->ue_security_cap.nr_encryption_algorithms); - // ue_radio_cap + // Fill UE radio capabilities. if (asn1_request->ue_radio_cap_present) { request.ue_radio_cap = asn1_request->ue_radio_cap.copy(); } - // idx_to_rfsp + // Fill idx to RFSP. if (asn1_request->idx_to_rfsp_present) { request.idx_to_rfsp = asn1_request->idx_to_rfsp; } - // masked_imeisv + // Fill masked IMEISV. if (asn1_request->masked_imeisv_present) { request.masked_imeisv = asn1_request->masked_imeisv.to_number(); } - // nas_pdu + // Fill NAS-PDU. if (asn1_request->nas_pdu_present) { request.nas_pdu = asn1_request->nas_pdu.copy(); } - // ue_radio_cap_for_paging + // Fill UE radio capabilities for paging. if (asn1_request->ue_radio_cap_for_paging_present) { cu_cp_ue_radio_cap_for_paging ue_radio_cap_for_paging; ue_radio_cap_for_paging.ue_radio_cap_for_paging_of_nr = @@ -494,7 +494,7 @@ inline bool fill_ngap_initial_context_setup_request(ngap_init_context_setup_requ request.ue_radio_cap_for_paging = ue_radio_cap_for_paging; } - // TODO: Add missing optional values + // TODO: Add missing optional values. return true; } @@ -521,7 +521,7 @@ inline bool fill_asn1_initial_context_setup_response(asn1::ngap::init_context_se } } - // Fill PDU Session Resource Failed to Setup List + // Fill PDU session resource failed to setup list. if (!resp.pdu_session_res_failed_to_setup_items.empty()) { asn1_resp->pdu_session_res_failed_to_setup_list_cxt_res_present = true; for (const auto& setup_failed_item : resp.pdu_session_res_failed_to_setup_items) { @@ -535,9 +535,9 @@ inline bool fill_asn1_initial_context_setup_response(asn1::ngap::init_context_se } } - // Fill Criticality Diagnostics + // Fill criticality diagnostics. if (resp.crit_diagnostics.has_value()) { - // TODO: Add crit diagnostics + // TODO: Add crit diagnostics. } return true; @@ -550,10 +550,10 @@ inline bool fill_asn1_initial_context_setup_response(asn1::ngap::init_context_se inline void fill_asn1_initial_context_setup_failure(asn1::ngap::init_context_setup_fail_s& asn1_fail, const ngap_init_context_setup_failure& fail) { - // Fill cause + // Fill cause. asn1_fail->cause = cause_to_asn1(fail.cause); - // Fill PDU Session Resource Failed to Setup List + // Fill PDU session resource failed to setup list. if (!fail.pdu_session_res_failed_to_setup_items.empty()) { asn1_fail->pdu_session_res_failed_to_setup_list_cxt_fail_present = true; for (const auto& setup_failed_item : fail.pdu_session_res_failed_to_setup_items) { @@ -565,9 +565,9 @@ inline void fill_asn1_initial_context_setup_failure(asn1::ngap::init_context_set } } - // Fill Criticality Diagnostics + // Fill criticality diagnostics. if (fail.crit_diagnostics.has_value()) { - // TODO: Add crit diagnostics + // TODO: Add crit diagnostics. } } @@ -592,10 +592,10 @@ inline bool fill_cu_cp_pdu_session_resource_modify_item_base( for (const auto& asn1_flow_item : asn1_modify_req_transfer->qos_flow_add_or_modify_request_list) { cu_cp_qos_flow_add_or_mod_item qos_flow_add_item; - // qosFlowIdentifier + // Fill QoS flow identifier. qos_flow_add_item.qos_flow_id = uint_to_qos_flow_id(asn1_flow_item.qos_flow_id); - // qosFlowLevelQosParameters + // Fill QoS flow level QoS parameters. if (asn1_flow_item.qos_flow_level_qos_params_present) { if (asn1_flow_item.qos_flow_level_qos_params.qos_characteristics.type() == asn1::ngap::qos_characteristics_c::types::dyn5qi) { @@ -604,11 +604,11 @@ inline bool fill_cu_cp_pdu_session_resource_modify_item_base( dyn_5qi.five_qi = uint_to_five_qi(asn1_flow_item.qos_flow_level_qos_params.qos_characteristics.dyn5qi().five_qi); } - // TODO: Add optional values + // TODO: Add optional values. qos_flow_add_item.qos_flow_level_qos_params.qos_desc = dyn_5qi; - // TODO: Add optional values + // TODO: Add optional values. } else if (asn1_flow_item.qos_flow_level_qos_params.qos_characteristics.type() == asn1::ngap::qos_characteristics_c::types::non_dyn5qi) { @@ -617,10 +617,10 @@ inline bool fill_cu_cp_pdu_session_resource_modify_item_base( uint_to_five_qi(asn1_flow_item.qos_flow_level_qos_params.qos_characteristics.non_dyn5qi().five_qi); qos_flow_add_item.qos_flow_level_qos_params.qos_desc = non_dyn_5qi; - // TODO: Add optional values + // TODO: Add optional values. } - // allocationAndRetentionPriority + // Fill allocation and retention priority. qos_flow_add_item.qos_flow_level_qos_params.alloc_retention_prio.prio_level_arp = asn1_flow_item.qos_flow_level_qos_params.alloc_and_retention_prio.prio_level_arp; qos_flow_add_item.qos_flow_level_qos_params.alloc_retention_prio.may_trigger_preemption = @@ -683,7 +683,7 @@ inline bool fill_cu_cp_pdu_session_resource_modify_request( inline void fill_asn1_pdu_session_res_setup_response(asn1::ngap::pdu_session_res_setup_resp_s& resp, const cu_cp_pdu_session_resource_setup_response& cu_cp_resp) { - // Fill PDU Session Resource Setup Response List + // Fill PDU session resource setup response list. if (!cu_cp_resp.pdu_session_res_setup_response_items.empty()) { resp->pdu_session_res_setup_list_su_res_present = true; @@ -696,7 +696,7 @@ inline void fill_asn1_pdu_session_res_setup_response(asn1::ngap::pdu_session_res } } - // Fill PDU Session Resource Failed to Setup List + // Fill PDU session resource failed to setup list. if (!cu_cp_resp.pdu_session_res_failed_to_setup_items.empty()) { resp->pdu_session_res_failed_to_setup_list_su_res_present = true; for (const auto& cu_cp_setup_failed_item : cu_cp_resp.pdu_session_res_failed_to_setup_items) { @@ -717,7 +717,7 @@ inline void fill_asn1_pdu_session_res_setup_response(asn1::ngap::pdu_session_res inline void fill_asn1_ue_context_release_request(asn1::ngap::ue_context_release_request_s& asn1_msg, const cu_cp_ue_context_release_request& msg) { - // Add PDU Session IDs + // Fill PDU session ids. if (!msg.pdu_session_res_list_cxt_rel_req.empty()) { asn1_msg->pdu_session_res_list_cxt_rel_req_present = true; for (const auto& session_id : msg.pdu_session_res_list_cxt_rel_req) { @@ -784,14 +784,14 @@ fill_cu_cp_ue_context_release_command(cu_cp_ue_context_release_command& inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release_complete_s& asn1_resp, const cu_cp_ue_context_release_complete& cu_cp_resp) { - // add user location info + // Fill user location info. if (cu_cp_resp.user_location_info.has_value()) { asn1_resp->user_location_info_present = true; asn1_resp->user_location_info.set_user_location_info_nr() = cu_cp_user_location_info_to_asn1(cu_cp_resp.user_location_info.value()); } - // add info on recommended cells and ran nodes for paging + // Fill info on recommended cells and RAN nodes for paging. if (cu_cp_resp.info_on_recommended_cells_and_ran_nodes_for_paging.has_value()) { asn1_resp->info_on_recommended_cells_and_ran_nodes_for_paging_present = true; @@ -799,13 +799,13 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release .recommended_cells_for_paging.recommended_cell_list) { asn1::ngap::recommended_cell_item_s asn1_recommended_cell_item; - // add ngran cgi + // Fill NG RAN CGI. asn1_recommended_cell_item.ngran_cgi.set_nr_cgi().nr_cell_id.from_number( cu_cp_recommended_cell_item.ngran_cgi.nci.value()); asn1_recommended_cell_item.ngran_cgi.set_nr_cgi().plmn_id = cu_cp_recommended_cell_item.ngran_cgi.plmn_id.to_bytes(); - // add time stayed in cell + // Fill time stayed in cell. if (cu_cp_recommended_cell_item.time_stayed_in_cell.has_value()) { asn1_recommended_cell_item.time_stayed_in_cell_present = true; asn1_recommended_cell_item.time_stayed_in_cell = cu_cp_recommended_cell_item.time_stayed_in_cell.value(); @@ -819,9 +819,9 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release .recommended_ran_nodes_for_paging.recommended_ran_node_list) { asn1::ngap::recommended_ran_node_item_s asn1_recommended_ran_node_item; - // add amf paging target + // Fill AMF paging target. if (cu_cp_recommended_ran_node_item.amf_paging_target.is_global_ran_node_id) { - // add global gnb id + // Fill global GNB id. auto& asn1_global_ran_node_id = asn1_recommended_ran_node_item.amf_paging_target.set_global_ran_node_id(); auto& global_gnb = asn1_global_ran_node_id.set_global_gnb_id(); global_gnb.plmn_id = @@ -830,7 +830,7 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release cu_cp_recommended_ran_node_item.amf_paging_target.global_ran_node_id.value().gnb_id.id, cu_cp_recommended_ran_node_item.amf_paging_target.global_ran_node_id.value().gnb_id.bit_length); } else if (cu_cp_recommended_ran_node_item.amf_paging_target.is_tai) { - // add tai + // Fill TAI. auto& asn1_tai = asn1_recommended_ran_node_item.amf_paging_target.set_tai(); asn1_tai.plmn_id = cu_cp_recommended_ran_node_item.amf_paging_target.tai.value().plmn_id.to_bytes(); asn1_tai.tac.from_number(cu_cp_recommended_ran_node_item.amf_paging_target.tai.value().tac); @@ -843,7 +843,7 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release } } - // add pdu session res list context release complete + // Fill PDU session res list context release complete. if (!cu_cp_resp.pdu_session_res_list_cxt_rel_cpl.empty()) { asn1_resp->pdu_session_res_list_cxt_rel_cpl_present = true; @@ -854,9 +854,9 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release } } - // add crit diagnostics + // Fill crit diagnostics. if (cu_cp_resp.crit_diagnostics.has_value()) { - // TODO: Add crit diagnostics + // TODO: Add crit diagnostics. } } @@ -865,15 +865,15 @@ inline void fill_asn1_ue_context_release_complete(asn1::ngap::ue_context_release /// \param[in] asn1_paging The Paging ASN1 struct. inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1::ngap::paging_s& asn1_paging) { - // add ue paging id + // Fill UE paging id. paging.ue_paging_id = ngap_asn1_to_ue_paging_id(asn1_paging->ue_paging_id); - // add paging drx + // Fill paging DRX. if (asn1_paging->paging_drx_present) { paging.paging_drx = asn1_paging->paging_drx.to_number(); } - // add tai list for paging + // Fill TAI list for paging. for (const auto& asn1_tai_item : asn1_paging->tai_list_for_paging) { cu_cp_tai_list_for_paging_item tai_item; tai_item.tai.plmn_id = plmn_identity::from_bytes(asn1_tai_item.tai.plmn_id.to_bytes()).value(); @@ -882,12 +882,12 @@ inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1:: paging.tai_list_for_paging.push_back(tai_item); } - // add paging prio + // Fill paging prio. if (asn1_paging->paging_prio_present) { paging.paging_prio = asn1_paging->paging_prio.to_number(); } - // add ue radio cap for paging + // Fill UE radio capabilities for paging. if (asn1_paging->ue_radio_cap_for_paging_present) { cu_cp_ue_radio_cap_for_paging ue_radio_cap_for_paging; ue_radio_cap_for_paging.ue_radio_cap_for_paging_of_nr = @@ -896,16 +896,16 @@ inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1:: paging.ue_radio_cap_for_paging = ue_radio_cap_for_paging; } - // add paging origin + // Fill paging origin. if (asn1_paging->paging_origin_present) { paging.paging_origin = asn1_paging->paging_origin.to_string(); } - // add assist data for paging + // Fill assist data for paging. if (asn1_paging->assist_data_for_paging_present) { cu_cp_assist_data_for_paging assist_data_for_paging; - // add assist data for recommended cells + // Fill assist data for recommended cells. if (asn1_paging->assist_data_for_paging.assist_data_for_recommended_cells_present) { cu_cp_assist_data_for_recommended_cells assist_data_for_recommended_cells; @@ -913,13 +913,13 @@ inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1:: .recommended_cells_for_paging.recommended_cell_list) { cu_cp_recommended_cell_item recommended_cell_item; - // add ngran cgi + // Fill NG RAN CGI. recommended_cell_item.ngran_cgi.nci = nr_cell_identity::create(asn1_recommended_cell.ngran_cgi.nr_cgi().nr_cell_id.to_number()).value(); recommended_cell_item.ngran_cgi.plmn_id = plmn_identity::from_bytes(asn1_recommended_cell.ngran_cgi.nr_cgi().plmn_id.to_bytes()).value(); - // add time stayed in cell + // Fill time stayed in cell. if (asn1_recommended_cell.time_stayed_in_cell_present) { recommended_cell_item.time_stayed_in_cell = asn1_recommended_cell.time_stayed_in_cell; } @@ -931,7 +931,7 @@ inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1:: assist_data_for_paging.assist_data_for_recommended_cells = assist_data_for_recommended_cells; } - // add paging attempt info + // Fill paging attempt info. if (asn1_paging->assist_data_for_paging.paging_attempt_info_present) { cu_cp_paging_attempt_info paging_attempt_info; @@ -958,33 +958,32 @@ inline void fill_cu_cp_paging_message(cu_cp_paging_message& paging, const asn1:: /// \returns True if the conversion was successful, false otherwise. inline bool fill_ngap_handover_request(ngap_handover_request& request, const asn1::ngap::ho_request_s& asn1_request) { - // handov type + // Fill handover type. asn1_to_handov_type(request.handov_type, asn1_request->handov_type); - // cause + // Fill cause. request.cause = asn1_to_cause(asn1_request->cause); - // ue aggr max bit rate + // Fill UE aggregated max bit rate. request.ue_aggr_max_bit_rate.ue_aggr_max_bit_rate_dl = asn1_request->ue_aggr_max_bit_rate.ue_aggr_max_bit_rate_dl; request.ue_aggr_max_bit_rate.ue_aggr_max_bit_rate_ul = asn1_request->ue_aggr_max_bit_rate.ue_aggr_max_bit_rate_ul; - // core network assist info for inactive - // TODO + // TODO: Add core network assist info for inactive. - // ue security cap & security context + // Fill UE security cap & security context. asn1_to_security_context(request.security_context, asn1_request->ue_security_cap, asn1_request->security_context); - // new security context ind + // Fill new security context indication. if (asn1_request->new_security_context_ind_present) { request.new_security_context_ind = asn1::enum_to_bool(asn1_request->new_security_context_ind); } - // nasc + // Fill NASC. if (asn1_request->nasc_present) { request.nasc = asn1_request->nasc.copy(); } - // pdu session res setup list ho req + // Fill PDU session resource setup list HO request. for (const auto& asn1_pdu_session_res_setup_item : asn1_request->pdu_session_res_setup_list_ho_req) { cu_cp_pdu_session_res_setup_item pdu_session_res_setup_item; if (!fill_cu_cp_pdu_session_resource_setup_item_base(pdu_session_res_setup_item, @@ -998,20 +997,19 @@ inline bool fill_ngap_handover_request(ngap_handover_request& request, const asn pdu_session_res_setup_item); } - // allowed nssai + // Fill allowed S-NSSAI. for (const auto& asn1_s_nssai : asn1_request->allowed_nssai) { request.allowed_nssai.push_back(ngap_asn1_to_s_nssai(asn1_s_nssai.s_nssai)); } - // trace activation - // TODO + // TODO: Add trace activation. - // masked imeisv + // Fill masked IMEISV. if (asn1_request->masked_imeisv_present) { request.masked_imeisv = asn1_request->masked_imeisv.to_number(); } - // source to target transparent container + // Fill source to target transparent container. asn1::cbit_ref bref(asn1_request->source_to_target_transparent_container); asn1::ngap::source_ngran_node_to_target_ngran_node_transparent_container_s asn1_transparent_container; if (asn1_transparent_container.unpack(bref) != asn1::SRSASN_SUCCESS) { @@ -1021,23 +1019,18 @@ inline bool fill_ngap_handover_request(ngap_handover_request& request, const asn asn1_to_source_to_target_transport_container(request.source_to_target_transparent_container, asn1_transparent_container); - // mob restrict list - // TODO + // TODO: Add Mob restrict list. - // location report request type - // TODO + // TODO: Add Location report request type. - // rrc inactive transition report request - // TODO + // TODO: Add RRC inactive transition report request. - // guami + // Fill GUAMI. request.guami = asn1_to_guami(asn1_request->guami); - // redirection voice fallback - // TODO + // TODO: Add redirection voice fallback. - // cn assisted ran tuning - // TODO + // TODO: Add CN assisted RAN tuning. return true; } @@ -1051,7 +1044,7 @@ fill_asn1_handover_resource_allocation_response(asn1::ngap::ho_request_ack_s& const ngap_handover_resource_allocation_response& ho_response) { if (ho_response.success) { - // pdu session res admitted list + // Fill PDU session resource admitted list. for (const auto& admitted_item : ho_response.pdu_session_res_admitted_list) { asn1::ngap::pdu_session_res_admitted_item_s asn1_admitted_item; if (!pdu_session_res_admitted_item_to_asn1(asn1_admitted_item, admitted_item)) { @@ -1060,7 +1053,7 @@ fill_asn1_handover_resource_allocation_response(asn1::ngap::ho_request_ack_s& asn1_ho_request_ack->pdu_session_res_admitted_list.push_back(asn1_admitted_item); } - // pdu session res failed to setup list ho ack + // Fill PDU session resource failed to setup list HO ACK. if (!ho_response.pdu_session_res_failed_to_setup_list_ho_ack.empty()) { asn1_ho_request_ack->pdu_session_res_failed_to_setup_list_ho_ack_present = true; for (const auto& failed_item : ho_response.pdu_session_res_failed_to_setup_list_ho_ack) { @@ -1072,15 +1065,15 @@ fill_asn1_handover_resource_allocation_response(asn1::ngap::ho_request_ack_s& } } - // target to source transparent container + // Fill target to source transparent container. if (!target_to_source_transport_container_to_asn1(asn1_ho_request_ack->target_to_source_transparent_container, ho_response.target_to_source_transparent_container)) { return false; } - // crit diagnostics + // Fill criticality diagnostics. if (ho_response.crit_diagnostics.has_value()) { - // TODO: Add crit diagnostics + // TODO: Add crit diagnostics. } } else { return false; @@ -1098,12 +1091,12 @@ fill_asn1_handover_resource_allocation_response(asn1::ngap::ho_fail_s& const ngap_handover_resource_allocation_response& ho_response) { if (!ho_response.success) { - // cause + // Fill cause. asn1_ho_failure->cause = cause_to_asn1(ho_response.cause); - // crit diagnostics + // Fill criticality diagnostics. if (ho_response.crit_diagnostics.has_value()) { - // TODO: Add crit diagnostics + // TODO: Add crit diagnostics. } } else { return false; diff --git a/lib/ngap/ngap_validators/ngap_validators.cpp b/lib/ngap/ngap_validators/ngap_validators.cpp index 85a463481c..7bc4a2ca9f 100644 --- a/lib/ngap/ngap_validators/ngap_validators.cpp +++ b/lib/ngap/ngap_validators/ngap_validators.cpp @@ -29,9 +29,9 @@ pdu_session_resource_setup_validation_outcome srsran::srs_cu_cp::verify_pdu_sess // Check for duplicate PDU Session IDs. if (!psis.emplace(psi).second) { ue_logger.log_warning("Duplicate {} in PduSessionResourceSetupRequest", psi); - // Make sure to only add each duplicate psi once + // Make sure to only add each duplicate psi once. if (failed_psis.emplace(psi).second) { - // Add failed psi to response + // Add failed psi to response. cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = psi; failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::multiple_pdu_session_id_instances; @@ -54,12 +54,12 @@ pdu_session_resource_setup_validation_outcome srsran::srs_cu_cp::verify_pdu_sess } } - // Remove failed psis from psis + // Remove failed psis from psis. for (const auto& failed_psi : failed_psis) { psis.erase(failed_psi); } - // If only duplicate PDU session IDs are present, return + // If only duplicate PDU session IDs are present, return. if (psis.empty()) { return verification_outcome; } @@ -72,24 +72,24 @@ pdu_session_resource_setup_validation_outcome srsran::srs_cu_cp::verify_pdu_sess if (!asn1_request->ue_aggr_max_bit_rate_present) { ue_logger.log_warning("Non-GBR QoS flow for {} present but PduSessionAggregateMaximumBitRate not set", psi); failed_psis.emplace(psi); - // Add failed psi to response + // Add failed psi to response. cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = psi; failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::invalid_qos_combination; verification_outcome.response.pdu_session_res_failed_to_setup_items.emplace(psi, failed_item); - // If single QoS flow fails, then the whole PDU session fails + // If single QoS flow fails, then the whole PDU session fails. break; } } } } - // Remove failed psis from psis + // Remove failed psis from psis. for (const auto& failed_psi : failed_psis) { psis.erase(failed_psi); } - // Add remaining PDU sessions to verified request + // Add remaining PDU sessions to verified request. for (const auto& psi : psis) { verification_outcome.request.pdu_session_res_setup_items.emplace(psi, request.pdu_session_res_setup_items[psi]); } @@ -111,12 +111,12 @@ pdu_session_resource_modify_validation_outcome srsran::srs_cu_cp::verify_pdu_ses std::unordered_set failed_psis; for (const auto& pdu_session_item : asn1_request->pdu_session_res_modify_list_mod_req) { pdu_session_id_t psi = uint_to_pdu_session_id(pdu_session_item.pdu_session_id); - // Check for duplicate PDU Session IDs + // Check for duplicate PDU Session IDs. if (!psis.emplace(psi).second) { ue_logger.log_warning("Duplicate {} in PduSessionResourceModifyRequest", psi); - // Make sure to only add each duplicate psi once + // Make sure to only add each duplicate psi once. if (failed_psis.emplace(psi).second) { - // Add failed psi to response + // Add failed psi to response. cu_cp_pdu_session_res_setup_failed_item failed_item; failed_item.pdu_session_id = psi; failed_item.unsuccessful_transfer.cause = ngap_cause_radio_network_t::multiple_pdu_session_id_instances; @@ -125,12 +125,12 @@ pdu_session_resource_modify_validation_outcome srsran::srs_cu_cp::verify_pdu_ses } } - // Remove failed psis from psis + // Remove failed psis from psis. for (const auto& failed_psi : failed_psis) { psis.erase(failed_psi); } - // Add remaining PDU sessions to verified request + // Add remaining PDU sessions to verified request. for (const auto& psi : psis) { verification_outcome.request.pdu_session_res_modify_items.emplace(psi, request.pdu_session_res_modify_items[psi]); } diff --git a/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp b/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp index 9d5b8a8cf4..ef7acd86c1 100644 --- a/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp +++ b/tests/unittests/ngap/ngap_pdu_session_resource_setup_procedure_test.cpp @@ -23,17 +23,17 @@ class ngap_pdu_session_resource_setup_procedure_test : public ngap_test { ue_index_t ue_index = create_ue(); - // Inject DL NAS transport message from AMF + // Inject DL NAS transport message from AMF. run_dl_nas_transport(ue_index); - // Inject UL NAS transport message from RRC + // Inject UL NAS transport message from RRC. run_ul_nas_transport(ue_index); - // Inject Initial Context Setup Request + // Inject Initial Context Setup Request. run_initial_context_setup(ue_index); if (enable_security) { - // Mark security as enabled + // Mark security as enabled. ue_mng.find_ue(ue_index)->get_security_manager().enable_security(); } @@ -54,11 +54,11 @@ class ngap_pdu_session_resource_setup_procedure_test : public ngap_test bool was_pdu_session_resource_setup_request_valid() const { - // Check that AMF notifier was called with right type + // Check that AMF notifier was called with right type. bool test_1 = n2_gw.last_ngap_msgs.back().pdu.successful_outcome().value.type() == asn1::ngap::ngap_elem_procs_o::successful_outcome_c::types_opts::pdu_session_res_setup_resp; - // Check that response contains PDU Session Resource Setup List + // Check that response contains PDU Session Resource Setup List. bool test_2 = n2_gw.last_ngap_msgs.back() .pdu.successful_outcome() .value.pdu_session_res_setup_resp() @@ -73,13 +73,13 @@ class ngap_pdu_session_resource_setup_procedure_test : public ngap_test bool test_1 = n2_gw.last_ngap_msgs.back().pdu.successful_outcome().value.type() == asn1::ngap::ngap_elem_procs_o::successful_outcome_c::types_opts::pdu_session_res_setup_resp; - // Check that response doesn't contain PDU Session Resource Setup List + // Check that response doesn't contain PDU Session Resource Setup List. bool test_2 = !n2_gw.last_ngap_msgs.back() .pdu.successful_outcome() .value.pdu_session_res_setup_resp() ->pdu_session_res_setup_list_su_res_present; - // Check that response contains PDU Session Resource Failed to Setup List + // Check that response contains PDU Session Resource Failed to Setup List. bool test_3 = n2_gw.last_ngap_msgs.back() .pdu.successful_outcome() .value.pdu_session_res_setup_resp() @@ -95,41 +95,41 @@ class ngap_pdu_session_resource_setup_procedure_test : public ngap_test } }; -/// Test missing PDU Session Resource Setup Request +/// Test missing PDU Session Resource Setup Request. TEST_F(ngap_pdu_session_resource_setup_procedure_test, when_pdu_session_resource_setup_request_is_not_received_then_ue_release_is_requested) { ASSERT_EQ(ngap->get_nof_ues(), 0); - // Test preamble + // Test preamble. this->start_procedure(); - // check that initial context setup request was received to the AMF and that UE object has been created + // Check that initial context setup request was received to the AMF and that UE object has been created. ASSERT_EQ(n2_gw.last_ngap_msgs.back().pdu.type().value, asn1::ngap::ngap_pdu_c::types_opts::successful_outcome); ASSERT_EQ(n2_gw.last_ngap_msgs.back().pdu.successful_outcome().value.type(), asn1::ngap::ngap_elem_procs_o::successful_outcome_c::types_opts::init_context_setup_resp); ASSERT_EQ(ngap->get_nof_ues(), 1); - // tick timers + // Tick timers. // Status: NGAP does not receive new PDU Session Resource Setup Request until request_pdu_session_timer has ended. for (unsigned msec_elapsed = 0; msec_elapsed < cu_cp_cfg.ue.request_pdu_session_timeout.count() * 1000; ++msec_elapsed) { this->tick(); } - // check that UE release was requested + // Check that UE release was requested. ASSERT_EQ(n2_gw.last_ngap_msgs.back().pdu.init_msg().value.type(), asn1::ngap::ngap_elem_procs_o::init_msg_c::types_opts::ue_context_release_request); } -/// Test valid PDU Session Resource Setup Request +/// Test valid PDU Session Resource Setup Request. TEST_F(ngap_pdu_session_resource_setup_procedure_test, when_valid_pdu_session_resource_setup_request_received_then_pdu_session_setup_succeeds) { - // Test preamble + // Test preamble. ue_index_t ue_index = this->start_procedure(); - // Inject PDU Session Resource Setup Request + // Inject PDU Session Resource Setup Request. pdu_session_id_t pdu_session_id = uint_to_pdu_session_id(test_rgen::uniform_int( pdu_session_id_to_uint(pdu_session_id_t::min), pdu_session_id_to_uint(pdu_session_id_t::max))); @@ -139,28 +139,28 @@ TEST_F(ngap_pdu_session_resource_setup_procedure_test, ue.amf_ue_id.value(), ue.ran_ue_id.value(), {{pdu_session_id, {{uint_to_qos_flow_id(1), 9}}}}); ngap->handle_message(pdu_session_resource_setup_request); - // Check conversion in adapter + // Check conversion in adapter. ASSERT_TRUE(was_conversion_successful(pdu_session_resource_setup_request, pdu_session_id)); - // Check that PDU Session Resource Setup Request was valid + // Check that PDU Session Resource Setup Request was valid. ASSERT_TRUE(was_pdu_session_resource_setup_request_valid()); } -/// Test invalid PDU Session Resource Setup Request +/// Test invalid PDU Session Resource Setup Request. TEST_F(ngap_pdu_session_resource_setup_procedure_test, when_invalid_pdu_session_resource_setup_request_received_then_pdu_session_setup_failed) { - // Test preamble + // Test preamble. ue_index_t ue_index = this->start_procedure(); auto& ue = test_ues.at(ue_index); - // Inject invalid PDU Session Resource Setup Request + // Inject invalid PDU Session Resource Setup Request. ngap_message pdu_session_resource_setup_request = generate_invalid_pdu_session_resource_setup_request_message(ue.amf_ue_id.value(), ue.ran_ue_id.value()); ngap->handle_message(pdu_session_resource_setup_request); - // Check that PDU Session Resource Setup Request was invalid + // Check that PDU Session Resource Setup Request was invalid. ASSERT_TRUE(was_pdu_session_resource_setup_request_invalid()); } @@ -186,11 +186,11 @@ TEST_F(ngap_pdu_session_resource_setup_procedure_test, /// Test invalid PDU Session Resource Setup Request. TEST_F(ngap_pdu_session_resource_setup_procedure_test, when_security_not_enabled_then_pdu_session_setup_failed) { - // Test preamble + // Test preamble. ue_index_t ue_index = this->start_procedure(false); auto& ue = test_ues.at(ue_index); - // Inject PDU Session Resource Setup Request + // Inject PDU Session Resource Setup Request. pdu_session_id_t pdu_session_id = uint_to_pdu_session_id(test_rgen::uniform_int( pdu_session_id_to_uint(pdu_session_id_t::min), pdu_session_id_to_uint(pdu_session_id_t::max))); @@ -198,6 +198,6 @@ TEST_F(ngap_pdu_session_resource_setup_procedure_test, when_security_not_enabled ue.amf_ue_id.value(), ue.ran_ue_id.value(), {{pdu_session_id, {{uint_to_qos_flow_id(1), 9}}}}); ngap->handle_message(pdu_session_resource_setup_request); - // Check that Error Indication has been sent to AMF + // Check that Error Indication has been sent to AMF. ASSERT_TRUE(was_error_indication_sent()); } diff --git a/tests/unittests/ngap/ngap_test_messages.cpp b/tests/unittests/ngap/ngap_test_messages.cpp index 6dd991d433..c1d8f3f3dc 100644 --- a/tests/unittests/ngap/ngap_test_messages.cpp +++ b/tests/unittests/ngap/ngap_test_messages.cpp @@ -103,7 +103,7 @@ ngap_message srsran::srs_cu_cp::generate_ng_setup_failure() setup_fail->cause.radio_network() = cause_radio_network_opts::options::unspecified; setup_fail->time_to_wait_present = false; setup_fail->crit_diagnostics_present = false; - // add critical diagnostics + // TODO: Add critical diagnostics. return ng_setup_failure; } @@ -121,7 +121,7 @@ ngap_message srsran::srs_cu_cp::generate_ng_setup_failure_with_bad_plmn(time_to_ setup_fail->time_to_wait_present = true; setup_fail->time_to_wait.value = time_to_wait; setup_fail->crit_diagnostics_present = false; - // add critical diagnostics + // Fill critical diagnostics return ng_setup_failure; } @@ -331,7 +331,7 @@ ngap_message srsran::srs_cu_cp::generate_invalid_initial_context_setup_request_m { ngap_message ngap_msg = generate_initial_context_setup_request_base(amf_ue_id, ran_ue_id); - // NIA0 is not allowed + // NIA0 is not allowed. auto& init_context_setup_req = ngap_msg.pdu.init_msg().value.init_context_setup_request(); init_context_setup_req->ue_security_cap.nr_encryption_algorithms.from_number(0); init_context_setup_req->ue_security_cap.nr_integrity_protection_algorithms.from_number(0); @@ -395,46 +395,46 @@ ngap_message srsran::srs_cu_cp::generate_valid_pdu_session_resource_setup_reques pdu_session_res_item.pdu_session_id = pdu_session_id_to_uint(pdu_session_id); - // Add PDU Session NAS PDU + // Fill PDU session NAS PDU. pdu_session_res_item.pdu_session_nas_pdu.from_string("7e02e9b0a23c027e006801006e2e0115c211000901000631310101ff08060" "6014a06014a2905010c02010c2204010027db79000608204101" "01087b002080802110030000108106ac1503648306ac150364000d04ac150" "364001002054e251c036f61690469707634066d6e6330393906" "6d636332303804677072731201"); - // Add S-NSSAI + // Fill S-NSSAI. pdu_session_res_item.s_nssai.sst.from_number(1); pdu_session_res_item.s_nssai.sd_present = true; pdu_session_res_item.s_nssai.sd.from_string("0027db"); - // Add PDU Session Resource Setup Request Transfer + // Fill PDU session resource setup request transfer. asn1::ngap::pdu_session_res_setup_request_transfer_s asn1_setup_req_transfer; { - // Add PDU Session Aggregate Maximum Bit Rate + // Fill PDU session aggregate maximum bit rate. asn1_setup_req_transfer->pdu_session_aggr_max_bit_rate_present = true; asn1_setup_req_transfer->pdu_session_aggr_max_bit_rate.pdu_session_aggr_max_bit_rate_dl = 1000000000U; asn1_setup_req_transfer->pdu_session_aggr_max_bit_rate.pdu_session_aggr_max_bit_rate_ul = 1000000000U; - // Add UL NGU UP TNL Info + // Fill UL NGU UP TNL Info. asn1_setup_req_transfer->ul_ngu_up_tnl_info.set_gtp_tunnel(); auto addr = transport_layer_address::create_from_string("127.0.0.1"); asn1_setup_req_transfer->ul_ngu_up_tnl_info.gtp_tunnel().transport_layer_address.from_string(addr.to_bitstring()); asn1_setup_req_transfer->ul_ngu_up_tnl_info.gtp_tunnel().gtp_teid.from_number(0x00005e6c); - // Add PDU Session Type + // Fill PDU session type. asn1_setup_req_transfer->pdu_session_type = asn1::ngap::pdu_session_type_opts::ipv4; - // Add QoS Flow Setup Request List + // Fill QoS flow setup request list. for (const auto& qos_flow_test_item : qos_flows) { qos_flow_setup_request_item_s qos_flow_setup_req_item; qos_flow_setup_req_item.qos_flow_id = qos_flow_id_to_uint(qos_flow_test_item.qos_flow_id); - // Add QoS Characteristics + // Fill QoS characteristics. qos_flow_setup_req_item.qos_flow_level_qos_params.qos_characteristics.set_non_dyn5qi(); qos_flow_setup_req_item.qos_flow_level_qos_params.qos_characteristics.non_dyn5qi().five_qi = qos_flow_test_item.five_qi; - // Add Allocation and Retention Priority + // Fill allocation and retention priority. qos_flow_setup_req_item.qos_flow_level_qos_params.alloc_and_retention_prio.prio_level_arp = 8; qos_flow_setup_req_item.qos_flow_level_qos_params.alloc_and_retention_prio.pre_emption_cap = pre_emption_cap_opts::shall_not_trigger_pre_emption; @@ -459,25 +459,25 @@ ngap_message srsran::srs_cu_cp::generate_invalid_pdu_session_resource_setup_requ auto& pdu_session_res_setup_req = ngap_msg.pdu.init_msg().value.pdu_session_res_setup_request(); - // Add pdu sessions with duplicate IDs + // Fill PDU sessions with duplicate ids. for (auto it = 0; it < 2; ++it) { pdu_session_res_setup_item_su_req_s pdu_session_res_item; pdu_session_res_item.pdu_session_id = 1; - // Add PDU Session NAS PDU + // Fill PDU session NAS PDU. pdu_session_res_item.pdu_session_nas_pdu.from_string("7e02e9b0a23c027e006801006e2e0115c211000901000631310101ff08060" "6014a06014a2905010c02010c2204010027db79000608204101" "01087b002080802110030000108106ac1503648306ac150364000d04ac150" "364001002054e251c036f61690469707634066d6e6330393906" "6d636332303804677072731201"); - // Add S-NSSAI + // Fill S-NSSAI. pdu_session_res_item.s_nssai.sst.from_number(1); pdu_session_res_item.s_nssai.sd_present = true; pdu_session_res_item.s_nssai.sd.from_string("0027db"); - // Add PDU Session Resource Setup Request Transfer + // Fill PDU session resource setup request transfer. pdu_session_res_item.pdu_session_res_setup_request_transfer.from_string( "0000040082000a0c400000003040000000008b000a01f00a321302000028d600860001000088000700010000091c00"); @@ -495,7 +495,7 @@ ngap_message srsran::srs_cu_cp:: ngap_message ngap_msg = generate_valid_pdu_session_resource_setup_request_message( amf_ue_id, ran_ue_id, {{uint_to_pdu_session_id(1), {{uint_to_qos_flow_id(1), 9}}}}); - // Add invalid PDU Session Resource Setup Request Transfer. + // Fill invalid PDU session resource setup request transfer. auto& pdu_session_res_setup_req = ngap_msg.pdu.init_msg().value.pdu_session_res_setup_request(); pdu_session_res_setup_req->pdu_session_res_setup_list_su_req.begin() ->pdu_session_res_setup_request_transfer.from_string("0000040082000a0c1dcd6500301dcd6500008b001a09f00a0c01bbfdf66" @@ -550,11 +550,11 @@ ngap_message srsran::srs_cu_cp::generate_valid_pdu_session_resource_release_comm auto& pdu_session_res_release_cmd = ngap_msg.pdu.init_msg().value.pdu_session_res_release_cmd(); - // Add PDU Session NAS PDU + // Fill PDU session NAS PDU. pdu_session_res_release_cmd->nas_pdu_present = true; pdu_session_res_release_cmd->nas_pdu = make_byte_buffer("7e02bcb47dc1137e00680100052e01b3d3241201").value(); - // Add PDU session resource to release list + // Fill PDU session resource to release list. asn1::ngap::pdu_session_res_to_release_item_rel_cmd_s pdu_session_res_to_release_item_rel_cmd; pdu_session_res_to_release_item_rel_cmd.pdu_session_id = pdu_session_id_to_uint(pdu_session_id); pdu_session_res_to_release_item_rel_cmd.pdu_session_res_release_cmd_transfer = make_byte_buffer("10").value(); @@ -616,26 +616,26 @@ ngap_message srsran::srs_cu_cp::generate_valid_pdu_session_resource_modify_reque pdu_session_res_item.pdu_session_id = pdu_session_id_to_uint(pdu_session_id); - // Add PDU Session Resource Modify Request Transfer + // Fill PDU session resource modify request transfer. asn1::ngap::pdu_session_res_modify_request_transfer_s pdu_session_res_modify_request_transfer; { - // Add QoS flow add or modify + // Fill QoS flow add or modify. pdu_session_res_modify_request_transfer->qos_flow_add_or_modify_request_list_present = !qos_flow_add_or_modify_list.empty(); for (const auto& qos_flow_id : qos_flow_add_or_modify_list) { - // Add QoS Flow Modify Request List + // Fill QoS flow modify request list. asn1::ngap::qos_flow_add_or_modify_request_item_s qos_flow_add_item; - // qosFlowIdentifier + // Fill QoS flow identifier. qos_flow_add_item.qos_flow_id = qos_flow_id_to_uint(qos_flow_id); - // Add QoS Characteristics + // Fill QoS characteristics. qos_flow_add_item.qos_flow_level_qos_params_present = true; qos_flow_add_item.qos_flow_level_qos_params.qos_characteristics.set_non_dyn5qi(); qos_flow_add_item.qos_flow_level_qos_params.qos_characteristics.non_dyn5qi().five_qi = 9; - // Add Allocation and Retention Priority + // Fill allocation and retention priority. qos_flow_add_item.qos_flow_level_qos_params.alloc_and_retention_prio.prio_level_arp = 8; qos_flow_add_item.qos_flow_level_qos_params.alloc_and_retention_prio.pre_emption_cap = asn1::ngap::pre_emption_cap_opts::shall_not_trigger_pre_emption; @@ -645,15 +645,15 @@ ngap_message srsran::srs_cu_cp::generate_valid_pdu_session_resource_modify_reque pdu_session_res_modify_request_transfer->qos_flow_add_or_modify_request_list.push_back(qos_flow_add_item); } - // Add QoS flow to release + // Fill QoS flow to release. pdu_session_res_modify_request_transfer->qos_flow_to_release_list_present = !qos_flow_to_release_list.empty(); for (const auto& qos_flow_id : qos_flow_to_release_list) { asn1::ngap::qos_flow_with_cause_item_s qos_flow_release_item; - // qosFlowIdentifier + // Fill QoS flow identifier. qos_flow_release_item.qos_flow_id = qos_flow_id_to_uint(qos_flow_id); - // cause + // Fill cause. qos_flow_release_item.cause.set_radio_network(); qos_flow_release_item.cause.radio_network() = asn1::ngap::cause_radio_network_opts::options::unspecified; @@ -677,20 +677,20 @@ srsran::srs_cu_cp::generate_invalid_pdu_session_resource_modify_request_message( auto& pdu_session_res_modify_req = ngap_msg.pdu.init_msg().value.pdu_session_res_modify_request(); - // Add pdu sessions with duplicate IDs + // Fill pdu sessions with duplicate IDs. for (auto it = 0; it < 2; ++it) { pdu_session_res_modify_item_mod_req_s pdu_session_res_item; pdu_session_res_item.pdu_session_id = pdu_session_id_to_uint(pdu_session_id); - // Add PDU Session Resource Modify Request Transfer + // Fill PDU session resource modify request transfer. pdu_session_res_modify_request_transfer_s pdu_session_res_modify_request_transfer; - // Add qos flow add or modify request item + // Fill QoS flow add or modify request item. pdu_session_res_modify_request_transfer->qos_flow_add_or_modify_request_list_present = true; qos_flow_add_or_modify_request_item_s qos_flow_add_item; - // qosFlowIdentifier + // Fill QoS flow identifier. qos_flow_add_item.qos_flow_id = 1; pdu_session_res_modify_request_transfer->qos_flow_add_or_modify_request_list.push_back(qos_flow_add_item); @@ -737,13 +737,13 @@ ngap_message srsran::srs_cu_cp::generate_valid_minimal_paging_message() ngap_msg.pdu.init_msg().load_info_obj(ASN1_NGAP_ID_PAGING); auto& paging = ngap_msg.pdu.init_msg().value.paging(); - // add ue paging id + // Fill UE paging id. auto& five_g_s_tmsi = paging->ue_paging_id.set_five_g_s_tmsi(); five_g_s_tmsi.amf_set_id.from_number(1); five_g_s_tmsi.amf_pointer.from_number(0); five_g_s_tmsi.five_g_tmsi.from_number(4211117727); - // add tai list for paging + // Fill TAI list for paging. asn1::ngap::tai_list_for_paging_item_s paging_item; paging_item.tai.plmn_id.from_string("00f110"); paging_item.tai.tac.from_number(7); @@ -760,35 +760,35 @@ ngap_message srsran::srs_cu_cp::generate_valid_paging_message() ngap_msg.pdu.init_msg().load_info_obj(ASN1_NGAP_ID_PAGING); auto& paging = ngap_msg.pdu.init_msg().value.paging(); - // add ue paging id + // Fill UE paging id. auto& five_g_s_tmsi = paging->ue_paging_id.set_five_g_s_tmsi(); five_g_s_tmsi.amf_set_id.from_number(1); five_g_s_tmsi.amf_pointer.from_number(0); five_g_s_tmsi.five_g_tmsi.from_number(4211117727); - // add paging drx + // Fill paging DRX. paging->paging_drx_present = true; paging->paging_drx.value = asn1::ngap::paging_drx_opts::options::v64; - // add tai list for paging + // Fill TAI list for paging. asn1::ngap::tai_list_for_paging_item_s paging_item; paging_item.tai.plmn_id.from_string("00f110"); paging_item.tai.tac.from_number(7); paging->tai_list_for_paging.push_back(paging_item); - // add paging prio + // Fill paging prio. paging->paging_prio_present = true; paging->paging_prio.value = asn1::ngap::paging_prio_opts::options::priolevel5; - // add ue radio cap for paging + // Fill UE radio capabilities for paging. paging->ue_radio_cap_for_paging_present = true; paging->ue_radio_cap_for_paging.ue_radio_cap_for_paging_of_nr = make_byte_buffer("deadbeef").value(); - // add paging origin + // Fill paging origin. paging->paging_origin_present = true; paging->paging_origin.value = asn1::ngap::paging_origin_opts::options::non_neg3gpp; - // add assist data for paging + // Fill assist data for paging. paging->assist_data_for_paging_present = true; paging->assist_data_for_paging.assist_data_for_recommended_cells_present = true; @@ -820,7 +820,7 @@ ngap_message srsran::srs_cu_cp::generate_invalid_paging_message() ngap_msg.pdu.set_init_msg(); ngap_msg.pdu.init_msg().load_info_obj(ASN1_NGAP_ID_PAGING); - // add ue paging id + // Fill UE paging id. auto& paging = ngap_msg.pdu.init_msg().value.paging(); paging->ue_paging_id.set_choice_exts(); return ngap_msg; @@ -858,25 +858,25 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_request(amf_ue_id_t amf_ auto& ho_request = ngap_msg.pdu.init_msg().value.ho_request(); ho_request->amf_ue_ngap_id = amf_ue_id_to_uint(amf_ue_id); - // handov type + // Fill Handover type. ho_request->handov_type = asn1::ngap::handov_type_opts::options::intra5gs; - // cause + // Fill cause. ho_request->cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::options::ho_desirable_for_radio_reason; - // ue aggr max bit rate + // Fill UE aggregated max bit rate. ho_request->ue_aggr_max_bit_rate.ue_aggr_max_bit_rate_dl = 1073741824; ho_request->ue_aggr_max_bit_rate.ue_aggr_max_bit_rate_ul = 1073741824; - // ue security cap + // Fill UE security capabilities. ho_request->ue_security_cap.nr_encryption_algorithms.from_number(49152); ho_request->ue_security_cap.nr_integrity_protection_algorithms.from_number(49152); ho_request->ue_security_cap.eutr_aencryption_algorithms.from_number(0); ho_request->ue_security_cap.eutr_aintegrity_protection_algorithms.from_number(0); - // security context + // Fill security context ho_request->security_context.next_hop_chaining_count = 2; ho_request->security_context.next_hop_nh.from_string( "1000100100100011110101001110000001001000000001111000010110011110001010011100101010110010000001010110110000100111" "0110101111001100000001100111100010001011111000111111101000100110011101110111100010101101000101000010100001000001" "00000101010000111100001010001001"); // 8923d4e04807859e29cab2056c276bcc06788be3fa267778ad1428410543c289 - // pdu session resource setup list ho req + // Fill PDU session resource setup list HO req. asn1::ngap::pdu_session_res_setup_item_ho_req_s setup_item; setup_item.pdu_session_id = 1; setup_item.s_nssai.sst.from_number(1); @@ -885,34 +885,34 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_request(amf_ue_id_t amf_ "0000050082000a0c400000003040000000008b000a01f00a32130200001b13007f00010000860001000088000700010000091c00") .value(); ho_request->pdu_session_res_setup_list_ho_req.push_back(setup_item); - // allowed nssai + // Fill allowed S-NSSAI. asn1::ngap::allowed_nssai_item_s allowed_nssai; allowed_nssai.s_nssai.sst.from_number(1); ho_request->allowed_nssai.push_back(allowed_nssai); - // masked imeisv + // Fill masked IMEISV ho_request->masked_imeisv_present = true; ho_request->masked_imeisv.from_number(81985526923525889); // 0123456700ffff01 - // source to target transparent container + // Fill source to target transparent container. asn1::ngap::source_ngran_node_to_target_ngran_node_transparent_container_s transparent_container; - // rrc container + // Fill RRC container. transparent_container.rrc_container = make_byte_buffer("0021930680ce811d1968097e340e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007a071e" "439f0000240400e03" "00000000100186c0000700809df0000000000000103a0002000012cb2800281c50f0007000f00000004008010240a0") .value(); - // pdu session res info list + // Fill PDU session resource info list. asn1::ngap::pdu_session_res_info_item_s session_item; session_item.pdu_session_id = 1; asn1::ngap::qos_flow_info_item_s flow_item; flow_item.qos_flow_id = 0; session_item.qos_flow_info_list.push_back(flow_item); transparent_container.pdu_session_res_info_list.push_back(session_item); - // cgi + // Fill NR CGI. auto& cgi = transparent_container.target_cell_id.set_nr_cgi(); cgi.plmn_id.from_string("00f110"); nr_cell_identity nci = nr_cell_identity::create(gnb_id_t{411, 22}, 0).value(); cgi.nr_cell_id.from_number(nci.value()); - // ue history info + // Fill UE history info. asn1::ngap::last_visited_cell_item_s cell_item; auto& cell = cell_item.last_visited_cell_info.set_ngran_cell(); auto& ngran_cell = cell.global_cell_id.set_nr_cgi(); @@ -929,7 +929,7 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_request(amf_ue_id_t amf_ } ho_request->source_to_target_transparent_container = pdu.copy(); - // guami + // Fill GUAMI. ho_request->guami.plmn_id.from_string("00f110"); ho_request->guami.amf_region_id.from_number(2); ho_request->guami.amf_set_id.from_number(1); @@ -950,7 +950,7 @@ ngap_message srsran::srs_cu_cp::generate_handover_preparation_failure(amf_ue_id_ ho_prep_fail->amf_ue_ngap_id = amf_ue_id_to_uint(amf_ue_id); ho_prep_fail->ran_ue_ngap_id = ran_ue_id_to_uint(ran_ue_id); - // cause + // Fill cause. ho_prep_fail->cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::options::unspecified; return ngap_msg; @@ -967,19 +967,19 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_command(amf_ue_id_t amf_ ho_cmd->amf_ue_ngap_id = amf_ue_id_to_uint(amf_ue_id); ho_cmd->ran_ue_ngap_id = ran_ue_id_to_uint(ran_ue_id); - // handov type + // Fill handover type. ho_cmd->handov_type = asn1::ngap::handov_type_opts::options::intra5gs; - // pdu session resource ho list + // Fill PDU session resource HO list. ho_cmd->pdu_session_res_ho_list_present = true; asn1::ngap::pdu_session_res_ho_item_s ho_item; ho_item.pdu_session_id = 1; ho_item.ho_cmd_transfer = make_byte_buffer("00").value(); ho_cmd->pdu_session_res_ho_list.push_back(ho_item); - // target to source transparent container + // Fill target to source transparent container. asn1::ngap::target_ngran_node_to_source_ngran_node_transparent_container_s transparent_container; - // rrc container + // Fill RRC container. transparent_container.rrc_container = make_byte_buffer("08190115200204d00f00102f1f852020605701ac00445ebb1c041878002c0" "0445ebb1c041878002c24445ebb1c041878002c700d3133b414" "831f0203e0102341e0400024a771002900000000c000140000034ec00187c" @@ -1011,7 +1011,7 @@ ngap_handover_preparation_request srsran::srs_cu_cp::generate_handover_preparati request.ue_index = ue_index; request.gnb_id = nci.gnb_id(gnb_id_bit_length); request.nci = nci; - // create a map of all PDU sessions and their associated QoS flows + // Fill create a map of all PDU sessions and their associated QoS flows. for (const auto& pdu_session : pdu_sessions) { std::vector qos_flows; for (const auto& drb : pdu_session.second.drbs) { diff --git a/tests/unittests/ngap/ngap_validators_test.cpp b/tests/unittests/ngap/ngap_validators_test.cpp index c053b90c4c..17985a9240 100644 --- a/tests/unittests/ngap/ngap_validators_test.cpp +++ b/tests/unittests/ngap/ngap_validators_test.cpp @@ -30,19 +30,19 @@ class ngap_validator_test : public ngap_test pdu_session_res_item.pdu_session_id = pdu_session_id_to_uint(psi); - // Add PDU Session NAS PDU + // Fill PDU Session NAS PDU. pdu_session_res_item.pdu_session_nas_pdu.from_string("7e02e9b0a23c027e006801006e2e0115c211000901000631310101ff08060" "6014a06014a2905010c02010c2204010027db79000608204101" "01087b002080802110030000108106ac1503648306ac150364000d04ac150" "364001002054e251c036f61690469707634066d6e6330393906" "6d636332303804677072731201"); - // Add S-NSSAI + // Fill S-NSSAI. pdu_session_res_item.s_nssai.sst.from_number(1); pdu_session_res_item.s_nssai.sd_present = true; pdu_session_res_item.s_nssai.sd.from_string("0027db"); - // Add PDU Session Resource Setup Request Transfer + // Fill PDU Session Resource Setup Request Transfer. pdu_session_res_item.pdu_session_res_setup_request_transfer.from_string( "0000040082000a0c400000003040000000008b000a01f00a321302000028d600860001000088000700010000091c00"); @@ -57,7 +57,7 @@ class ngap_validator_test : public ngap_test auto& pdu_session_res_setup_req = ngap_msg.pdu.init_msg().value.pdu_session_res_setup_request(); - // Add unique PDU session + // Fill unique PDU session. pdu_session_id_t unique_psi = uint_to_pdu_session_id(2); pdu_session_res_setup_req->pdu_session_res_setup_list_su_req.push_back( generate_pdu_session_resource_setup_item(unique_psi)); @@ -75,7 +75,7 @@ class ngap_validator_test : public ngap_test auto& pdu_session_res_setup_req = ngap_msg.pdu.init_msg().value.pdu_session_res_setup_request(); - // Add unique PDU session + // Fill unique PDU session. pdu_session_id_t new_psi = uint_to_pdu_session_id(2); pdu_session_res_setup_req->pdu_session_res_setup_list_su_req.push_back( generate_pdu_session_resource_setup_item(new_psi)); @@ -94,7 +94,7 @@ class ngap_validator_test : public ngap_test auto& asn1_request = ngap_msg.pdu.init_msg().value.pdu_session_res_setup_request(); asn1_request->ue_aggr_max_bit_rate_present = false; - // Add PDU Session Resource Setup Request Transfer + // Fill PDU Session Resource Setup Request Transfer. asn1::ngap::pdu_session_res_setup_request_transfer_s asn1_setup_req_transfer; asn1_setup_req_transfer->ul_ngu_up_tnl_info.set_gtp_tunnel(); @@ -128,7 +128,7 @@ class ngap_validator_test : public ngap_test asn1_setup_req_transfer->qos_flow_setup_request_list.push_back(asn1_qos_flow_item); - // Pack pdu_session_res_release_resp_transfer_s + // Pack pdu_session_res_release_resp_transfer_s. asn1_request->pdu_session_res_setup_list_su_req.begin()->pdu_session_res_setup_request_transfer = pack_into_pdu(asn1_setup_req_transfer); @@ -141,14 +141,14 @@ class ngap_validator_test : public ngap_test pdu_session_res_item.pdu_session_id = pdu_session_id_to_uint(psi); - // Add PDU Session Resource Modify Request Transfer + // Fill PDU session resource modify request transfer. asn1::ngap::pdu_session_res_modify_request_transfer_s pdu_session_res_modify_request_transfer; - // Add qos flow add or modify request item + // Fill QoS flow add or modify request item. pdu_session_res_modify_request_transfer->qos_flow_add_or_modify_request_list_present = true; asn1::ngap::qos_flow_add_or_modify_request_item_s qos_flow_add_item; - // qosFlowIdentifier + // Fill QoS flow identifier. qos_flow_add_item.qos_flow_id = 1; pdu_session_res_modify_request_transfer->qos_flow_add_or_modify_request_list.push_back(qos_flow_add_item); @@ -170,7 +170,7 @@ class ngap_validator_test : public ngap_test auto& pdu_session_res_modify_req = ngap_msg.pdu.init_msg().value.pdu_session_res_modify_request(); - // Add unique PDU session + // Fill unique PDU session. pdu_session_res_modify_req->pdu_session_res_modify_list_mod_req.push_back( generate_pdu_session_resource_modify_item(unique_psi)); @@ -178,7 +178,7 @@ class ngap_validator_test : public ngap_test } }; -// Test handling of valid PDU Session Resource Modification Request +// Test handling of valid PDU session resource modification request. TEST_F(ngap_validator_test, when_valid_request_received_then_pdu_session_setup_succeeds) { ue_index_t ue_index = uint_to_ue_index(0); @@ -194,7 +194,7 @@ TEST_F(ngap_validator_test, when_valid_request_received_then_pdu_session_setup_s fill_cu_cp_pdu_session_resource_setup_request(request, asn1_request->pdu_session_res_setup_list_su_req); ngap_ue_logger ue_logger{"NGAP", {ue_index, ran_ue_id}}; - // Verify PDU Session Resource Setup Request + // Verify PDU session resource setup request. auto verification_outcome = verify_pdu_session_resource_setup_request(request, asn1_request, ue_logger); ASSERT_EQ(verification_outcome.request.pdu_session_res_setup_items.size(), 1U); @@ -202,7 +202,7 @@ TEST_F(ngap_validator_test, when_valid_request_received_then_pdu_session_setup_s ASSERT_EQ(verification_outcome.response.pdu_session_res_failed_to_setup_items.size(), 0U); } -// Test handling of duplicate PDU Session IDs in PDU Session Resource Setup Request +// Test handling of duplicate PDU session ids in PDU session resource setup request. TEST_F(ngap_validator_test, when_duplicate_pdu_session_id_then_pdu_session_setup_fails) { ue_index_t ue_index = uint_to_ue_index(0); @@ -217,7 +217,7 @@ TEST_F(ngap_validator_test, when_duplicate_pdu_session_id_then_pdu_session_setup fill_cu_cp_pdu_session_resource_setup_request(request, asn1_request->pdu_session_res_setup_list_su_req); ngap_ue_logger ue_logger{"NGAP", {ue_index, ran_ue_id}}; - // Verify PDU Session Resource Setup Request + // Verify PDU session resource setup request. auto verification_outcome = verify_pdu_session_resource_setup_request(request, asn1_request, ue_logger); ASSERT_TRUE(verification_outcome.request.pdu_session_res_setup_items.empty()); @@ -239,14 +239,15 @@ TEST_F(ngap_validator_test, when_unique_and_duplicate_pdu_session_id_then_pdu_se fill_cu_cp_pdu_session_resource_setup_request(request, asn1_request->pdu_session_res_setup_list_su_req); ngap_ue_logger ue_logger{"NGAP", {ue_index, ran_ue_id}}; - // Verify PDU Session Resource Setup Request + // Verify PDU session resource setup request. auto verification_outcome = verify_pdu_session_resource_setup_request(request, asn1_request, ue_logger); ASSERT_EQ(verification_outcome.request.pdu_session_res_setup_items.size(), 1U); ASSERT_EQ(verification_outcome.response.pdu_session_res_failed_to_setup_items.size(), 1U); } -// Test handling of PduSessionResourceSetupRequest with non-GBR QoS flows but no PDU Session Aggregate Maximum Bit Rate +// Test handling of PDU session resource setup request with non-GBR QoS flows but no PDU session aggregate maximum bit +// rate. TEST_F( ngap_validator_test, when_request_pdu_session_contains_non_gbr_qos_flows_but_no_aggregate_maximum_bitrate_then_pdu_session_setup_fails) @@ -266,14 +267,14 @@ TEST_F( fill_cu_cp_pdu_session_resource_setup_request(request, asn1_request->pdu_session_res_setup_list_su_req); ngap_ue_logger ue_logger{"NGAP", {ue_index, ran_ue_id}}; - // Verify PDU Session Resource Setup Request. + // Verify PDU session resource setup request. auto verification_outcome = verify_pdu_session_resource_setup_request(request, asn1_request, ue_logger); ASSERT_TRUE(verification_outcome.request.pdu_session_res_setup_items.empty()); ASSERT_EQ(verification_outcome.response.pdu_session_res_failed_to_setup_items.size(), 1U); } -// Test handling of valid PDU Session Resource Modification Request. +// Test handling of valid PDU session resource modification request. TEST_F(ngap_validator_test, when_valid_request_received_then_pdu_session_modify_succeeds) { pdu_session_id_t psi = uint_to_pdu_session_id(1); @@ -289,7 +290,7 @@ TEST_F(ngap_validator_test, when_valid_request_received_then_pdu_session_modify_ fill_cu_cp_pdu_session_resource_modify_request(request, asn1_request->pdu_session_res_modify_list_mod_req); ngap_ue_logger ue_logger{"NGAP", {ue_index, ran_ue_id}}; - // Verify PDU Session Resource Modify Request + // Verify PDU session resource modify request. auto verification_outcome = verify_pdu_session_resource_modify_request(request, asn1_request, ue_logger); ASSERT_EQ(verification_outcome.request.pdu_session_res_modify_items.size(), 1U); @@ -297,7 +298,7 @@ TEST_F(ngap_validator_test, when_valid_request_received_then_pdu_session_modify_ ASSERT_EQ(verification_outcome.response.pdu_session_res_failed_to_modify_list.size(), 0U); } -// Test handling of duplicate PDU Session IDs in PDU Session Resource Modification Request +// Test handling of duplicate PDU session ids in PDU session resource modification request. TEST_F(ngap_validator_test, when_duplicate_pdu_session_id_then_pdu_session_modification_fails) { ue_index_t ue_index = uint_to_ue_index(0); @@ -313,7 +314,7 @@ TEST_F(ngap_validator_test, when_duplicate_pdu_session_id_then_pdu_session_modif fill_cu_cp_pdu_session_resource_modify_request(request, asn1_request->pdu_session_res_modify_list_mod_req); ngap_ue_logger ue_logger{"NGAP", {ue_index, ran_ue_id}}; - // Verify PDU Session Resource Modify Request + // Verify PDU session resource modify request. auto verification_outcome = verify_pdu_session_resource_modify_request(request, asn1_request, ue_logger); ASSERT_TRUE(verification_outcome.request.pdu_session_res_modify_items.empty()); @@ -337,7 +338,7 @@ TEST_F(ngap_validator_test, when_unique_and_duplicate_pdu_session_id_then_pdu_se fill_cu_cp_pdu_session_resource_modify_request(request, asn1_request->pdu_session_res_modify_list_mod_req); ngap_ue_logger ue_logger{"NGAP", {ue_index, ran_ue_id}}; - // Verify PDU Session Resource Modify Request + // Verify PDU session resource modify request. auto verification_outcome = verify_pdu_session_resource_modify_request(request, asn1_request, ue_logger); ASSERT_EQ(verification_outcome.request.pdu_session_res_modify_items.size(), 1U); From abdd283b17ad8b4f1d3251a0431bca0ab2d10427 Mon Sep 17 00:00:00 2001 From: frankist Date: Wed, 8 Jan 2025 16:02:09 +0100 Subject: [PATCH 073/107] sched: remove assert outside sched thread that causes assertion --- lib/scheduler/ue_scheduling/ue_event_manager.cpp | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.cpp b/lib/scheduler/ue_scheduling/ue_event_manager.cpp index b98c83b4d6..ddd8ed6021 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.cpp +++ b/lib/scheduler/ue_scheduling/ue_event_manager.cpp @@ -369,9 +369,13 @@ void ue_event_manager::handle_ue_config_applied(du_ue_index_t ue_idx) void ue_event_manager::handle_ul_bsr_indication(const ul_bsr_indication_message& bsr_ind) { - srsran_sanity_check(cell_exists(bsr_ind.cell_index), "Invalid cell index"); - auto handle_ul_bsr_ind_impl = [this, bsr_ind = ind_pdu_pool->create_bsr(bsr_ind)]() { + if (not cell_exists(bsr_ind->cell_index)) { + logger.warning("ue={}: Detected invalide cell index={} in BSR", + fmt::underlying(bsr_ind->ue_index), + fmt::underlying(bsr_ind->cell_index)); + } + if (not ue_db.contains(bsr_ind->ue_index)) { log_invalid_ue_index(bsr_ind->ue_index, "BSR"); return; From 6c98b8470fc48d8392a2a24b9c3ab15dfc74de65 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 8 Jan 2025 15:56:35 +0100 Subject: [PATCH 074/107] mac: fix multicell test mode --- lib/du/du_high/test_mode/mac_test_mode_adapter.cpp | 4 ++-- lib/du/du_high/test_mode/mac_test_mode_helpers.cpp | 8 +++++--- lib/du/du_high/test_mode/mac_test_mode_helpers.h | 3 ++- 3 files changed, 9 insertions(+), 6 deletions(-) diff --git a/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp b/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp index 04f62b5616..b78851843b 100644 --- a/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp +++ b/lib/du/du_high/test_mode/mac_test_mode_adapter.cpp @@ -246,7 +246,7 @@ void mac_test_mode_cell_adapter::forward_crc_ind_to_mac(const mac_crc_indication continue; } - auto rx_pdu = create_test_pdu_with_bsr(crc_msg.sl_rx, pdu.rnti, to_harq_id(pdu.harq_id)); + auto rx_pdu = create_test_pdu_with_bsr(cell_index, crc_msg.sl_rx, pdu.rnti, to_harq_id(pdu.harq_id)); if (not rx_pdu.has_value()) { logger.warning("TEST_MODE c-rnti={}: Unable to create test PDU with BSR", pdu.rnti); continue; @@ -379,7 +379,7 @@ void mac_test_mode_cell_adapter::on_new_uplink_scheduler_results(const mac_ul_sc } if (test_ue_cfg.pusch_active) { - auto rx_pdu = create_test_pdu_with_bsr(ul_res.slot, pucch.crnti, to_harq_id(0)); + auto rx_pdu = create_test_pdu_with_bsr(cell_index, ul_res.slot, pucch.crnti, to_harq_id(0)); if (not rx_pdu.has_value()) { logger.warning("TEST_MODE c-rnti={}: Unable to create test PDU with BSR", pucch.crnti); continue; diff --git a/lib/du/du_high/test_mode/mac_test_mode_helpers.cpp b/lib/du/du_high/test_mode/mac_test_mode_helpers.cpp index f965ccf4b4..2695037710 100644 --- a/lib/du/du_high/test_mode/mac_test_mode_helpers.cpp +++ b/lib/du/du_high/test_mode/mac_test_mode_helpers.cpp @@ -15,8 +15,10 @@ using namespace srsran; using namespace srs_du; -expected -srsran::srs_du::create_test_pdu_with_bsr(slot_point sl_rx, rnti_t test_rnti, harq_id_t harq_id) +expected srsran::srs_du::create_test_pdu_with_bsr(du_cell_index_t cell_index, + slot_point sl_rx, + rnti_t test_rnti, + harq_id_t harq_id) { // - 8-bit R/LCID MAC subheader. // - MAC CE with Long BSR. @@ -33,7 +35,7 @@ srsran::srs_du::create_test_pdu_with_bsr(slot_point sl_rx, rnti_t test_rnti, har return make_unexpected(default_error_t{}); } return mac_rx_data_indication{ - sl_rx, to_du_cell_index(0), mac_rx_pdu_list{mac_rx_pdu{test_rnti, 0, harq_id, std::move(buf.value())}}}; + sl_rx, cell_index, mac_rx_pdu_list{mac_rx_pdu{test_rnti, 0, harq_id, std::move(buf.value())}}}; } static void fill_csi_bits(bounded_bitset& payload, diff --git a/lib/du/du_high/test_mode/mac_test_mode_helpers.h b/lib/du/du_high/test_mode/mac_test_mode_helpers.h index a4004b109f..9d2f68b50d 100644 --- a/lib/du/du_high/test_mode/mac_test_mode_helpers.h +++ b/lib/du/du_high/test_mode/mac_test_mode_helpers.h @@ -24,7 +24,8 @@ struct ul_sched_info; namespace srs_du { /// Create dummy PDU with BSR. -expected create_test_pdu_with_bsr(slot_point sl_rx, rnti_t test_rnti, harq_id_t harq_id); +expected +create_test_pdu_with_bsr(du_cell_index_t cell_index, slot_point sl_rx, rnti_t test_rnti, harq_id_t harq_id); /// Setters for UCI PDUs based on config values. mac_uci_pdu create_uci_pdu(const pucch_info& pucch, const du_test_mode_config::test_mode_ue_config& test_ue_cfg); From dcbc156e21dd7ffbb0c2b9a8864bd9e7431e634e Mon Sep 17 00:00:00 2001 From: frankist Date: Tue, 7 Jan 2025 12:40:19 +0100 Subject: [PATCH 075/107] sched: add function to postprocess scheduling slot decisions --- lib/scheduler/ue_context/ue_cell.h | 3 ++- lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp | 5 +++++ lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h | 6 +++++- lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp | 2 ++ .../scheduler/ue_scheduling/ue_grid_allocator_test.cpp | 2 ++ 5 files changed, 16 insertions(+), 2 deletions(-) diff --git a/lib/scheduler/ue_context/ue_cell.h b/lib/scheduler/ue_context/ue_cell.h index 09ceb51e0a..b8b76867dc 100644 --- a/lib/scheduler/ue_context/ue_cell.h +++ b/lib/scheduler/ue_context/ue_cell.h @@ -129,7 +129,8 @@ class ue_cell const ue_link_adaptation_controller& link_adaptation_controller() const { return ue_mcs_calculator; } - ul_power_controller& get_ul_power_controller() { return ul_pwr_controller; } + ul_power_controller& get_ul_power_controller() { return ul_pwr_controller; } + const ul_power_controller& get_ul_power_controller() const { return ul_pwr_controller; } /// \brief Returns an estimated DL rate in bytes per slot based on the given input parameters. double get_estimated_dl_rate(const pdsch_config_params& pdsch_cfg, sch_mcs_index mcs, unsigned nof_prbs) const; diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 3b72ecd4e4..92c6450a7e 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -1048,3 +1048,8 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice // No candidates for PUSCH allocation. return {alloc_status::invalid_params}; } + +void ue_cell_grid_allocator::post_process_results() +{ + // TODO +} diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h index 50efcf7cc1..ac7af4454a 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h @@ -13,7 +13,6 @@ #include "../pdcch_scheduling/pdcch_resource_allocator.h" #include "../policy/ue_allocator.h" #include "../slicing/ran_slice_candidate.h" -#include "../uci_scheduling/uci_scheduler.h" #include "ue_repository.h" #include "srsran/scheduler/config/scheduler_expert_config.h" @@ -42,6 +41,11 @@ class ue_cell_grid_allocator ul_alloc_result allocate_ul_grant(const ue_pusch_grant& grant, ran_slice_id_t slice_id, slot_point pusch_slot); + /// \brief Called at the end of a slot to process the allocations that took place and make some final adjustments. + /// + /// In particular, this function can redimension the existing grants to fill the remaining RBs if it deems necessary. + void post_process_results(); + private: struct cell_t { du_cell_index_t cell_index; diff --git a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp index 413195b442..dd0d2b19d2 100644 --- a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp +++ b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp @@ -72,6 +72,8 @@ void ue_scheduler_impl::run_sched_strategy(slot_point slot_tx, du_cell_index_t c slice_pusch_alloc, ue_res_grid_view, *ul_slice_candidate, cells[cell_index].cell_harqs.pending_ul_retxs()); ul_slice_candidate = cells[cell_index].slice_sched.get_next_ul_candidate(); } + + ue_alloc.post_process_results(); } void ue_scheduler_impl::update_harq_pucch_counter(cell_resource_allocator& cell_alloc) diff --git a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp index 6703843605..257c5fa0ce 100644 --- a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp @@ -79,6 +79,8 @@ class ue_grid_allocator_tester : public ::testing::TestWithParam on_each_slot(); + alloc.post_process_results(); + // Log scheduler results. res_logger.on_scheduler_result(res_grid[0].result); } From 52dab7823c7adfb7fb191533c889bd1261edc697 Mon Sep 17 00:00:00 2001 From: frankist Date: Tue, 7 Jan 2025 16:46:26 +0100 Subject: [PATCH 076/107] sched: minor fixes in UL grant allocator --- .../ue_scheduling/ue_cell_grid_allocator.cpp | 76 +++++++++++++------ 1 file changed, 51 insertions(+), 25 deletions(-) diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 92c6450a7e..41420e6864 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -510,6 +510,41 @@ dl_alloc_result ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& return {alloc_status::invalid_params}; } +static crb_interval get_ul_rb_limits(const scheduler_ue_expert_config& expert_cfg, const search_space_info& ss_info) +{ + const unsigned start_rb = std::max(expert_cfg.pusch_crb_limits.start(), ss_info.ul_crb_lims.start()); + const unsigned end_rb = std::min(expert_cfg.pusch_crb_limits.stop(), ss_info.ul_crb_lims.stop()); + return {start_rb, std::max(start_rb, end_rb)}; +} + +static unsigned +adjust_nof_rbs_to_transform_precoding(unsigned rbs, const ue_cell& ue_cc, dci_ul_rnti_config_type dci_type) +{ + // Ensure the number of PRB is valid if the transform precoder is used. The condition the PUSCH bandwidth with + // transform precoder is defined in TS 38.211 Section 6.1.3. The number of PRB must be lower than or equal to + // current number of PRB. + bool use_transform_precoding = dci_type == dci_ul_rnti_config_type::c_rnti_f0_1 + ? ue_cc.cfg().use_pusch_transform_precoding_dci_0_1() + : ue_cc.cfg().cell_cfg_common.use_msg3_transform_precoder(); + if (use_transform_precoding) { + rbs = get_transform_precoding_nearest_lower_nof_prb_valid(rbs).value_or(rbs); + } + return rbs; +} + +static unsigned adjust_ue_max_ul_nof_rbs(const scheduler_ue_expert_config& expert_cfg, + const ue_cell& ue_cc, + dci_ul_rnti_config_type dci_type, + unsigned max_rbs) +{ + max_rbs = std::min(expert_cfg.pusch_nof_rbs.stop(), max_rbs); + max_rbs = std::min(max_rbs, ue_cc.cfg().rrm_cfg().pusch_grant_size_limits.stop()); + max_rbs = ue_cc.get_ul_power_controller().adapt_pusch_prbs_to_phr(max_rbs); + max_rbs = adjust_nof_rbs_to_transform_precoding(max_rbs, ue_cc, dci_type); + + return max_rbs; +} + ul_alloc_result ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice_id_t slice_id, slot_point pusch_slot) { @@ -677,31 +712,30 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice } // Apply RB allocation limits. - const unsigned start_rb = std::max(expert_cfg.pusch_crb_limits.start(), ss_info.ul_crb_lims.start()); - const unsigned end_rb = std::min(expert_cfg.pusch_crb_limits.stop(), ss_info.ul_crb_lims.stop()); - if (start_rb >= end_rb) { - logger.debug("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Invalid RB allocation range [{}, {})", + crb_interval ul_crb_lims = get_ul_rb_limits(expert_cfg, ss_info); + if (ul_crb_lims.empty()) { + logger.debug("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Invalid RB allocation range {}", fmt::underlying(u.ue_index), u.crnti, pusch_alloc.slot, - start_rb, - end_rb); + ul_crb_lims); return {alloc_status::skip_slot}; } - const crb_interval ul_crb_lims = {start_rb, end_rb}; - const prb_bitmap used_crbs = pusch_alloc.ul_res_grid.used_crbs(scs, ul_crb_lims, pusch_td_cfg.symbols); + const prb_bitmap used_crbs = pusch_alloc.ul_res_grid.used_crbs(scs, ul_crb_lims, pusch_td_cfg.symbols); if (used_crbs.all()) { slots_with_no_pusch_space.push_back(pusch_alloc.slot); return {alloc_status::skip_slot}; } // Compute the MCS and the number of PRBs, depending on the pending bytes to transmit. - grant_prbs_mcs mcs_prbs = - is_retx ? grant_prbs_mcs{h_ul->get_grant_params().mcs, h_ul->get_grant_params().rbs.type1().length()} - : ue_cc->required_ul_prbs(pusch_td_cfg, grant.recommended_nof_bytes.value(), dci_type); - // Try to limit the grant PRBs. - if (not is_retx) { + grant_prbs_mcs mcs_prbs; + if (is_retx) { + mcs_prbs = grant_prbs_mcs{h_ul->get_grant_params().mcs, h_ul->get_grant_params().rbs.type1().length()}; + } else { + // Compute MCS and PRBs based on grant parameters. + mcs_prbs = ue_cc->required_ul_prbs(pusch_td_cfg, grant.recommended_nof_bytes.value(), dci_type); + // [Implementation-defined] Check whether max. UL grants per slot is reached if PUSCH for current UE succeeds. If // so, allocate remaining RBs to the current UE only if it's a new Tx. if (pusch_pdu_rem_space == 1 and not u.has_pending_sr()) { @@ -718,6 +752,7 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice if (mcs_prbs.mcs < min_mcs_for_1_prb and mcs_prbs.n_prbs <= min_allocable_prbs) { ++mcs_prbs.n_prbs; } + // [Implementation-defined] // Check whether to allocate all remaining RBs or not. This is done to ensure we allocate only X nof. UEs per slot // and not X+1 nof. UEs. One way is by checking if the emtpy interval is less than 2 times the required RBs. If @@ -731,20 +766,11 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice if (grant.max_nof_rbs.has_value()) { mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, grant.max_nof_rbs.value()); } - // Re-apply nof. PUSCH RBs to allocate limits. + + // Apply nof. PUSCH RBs to allocate limits. mcs_prbs.n_prbs = std::max(mcs_prbs.n_prbs, expert_cfg.pusch_nof_rbs.start()); - mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, expert_cfg.pusch_nof_rbs.stop()); mcs_prbs.n_prbs = std::max(mcs_prbs.n_prbs, ue_cell_cfg.rrm_cfg().pusch_grant_size_limits.start()); - mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, ue_cell_cfg.rrm_cfg().pusch_grant_size_limits.stop()); - // Ensure the number of PRB is valid if the transform precoder is used. The condition the PUSCH bandwidth with - // transform precoder is defined in TS 38.211 Section 6.1.3. The number of PRB must be lower than or equal to - // current number of PRB. - if ((dci_type == dci_ul_rnti_config_type::c_rnti_f0_1) - ? ue_cell_cfg.use_pusch_transform_precoding_dci_0_1() - : ue_cell_cfg.cell_cfg_common.use_msg3_transform_precoder()) { - mcs_prbs.n_prbs = - get_transform_precoding_nearest_lower_nof_prb_valid(mcs_prbs.n_prbs).value_or(mcs_prbs.n_prbs); - } + mcs_prbs.n_prbs = adjust_ue_max_ul_nof_rbs(expert_cfg, *ue_cc, dci_type, mcs_prbs.n_prbs); } // NOTE: This should never happen, but it's safe not to proceed if we get n_prbs == 0. From 93cb73f32f6c2a26eb69c4d72c227c86db820c65 Mon Sep 17 00:00:00 2001 From: frankist Date: Tue, 7 Jan 2025 16:48:53 +0100 Subject: [PATCH 077/107] sched: add check to avoid remaining 1-4 RBs to be left free during UL grant allocation --- lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 41420e6864..a803dbb91b 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -232,6 +232,14 @@ dl_alloc_result ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& if (grant.max_nof_rbs.has_value()) { mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, grant.max_nof_rbs.value()); } + // [Implementation-defined] + // Sometimes just a few 1-4 RBs are left in the grid, and the scheduler policy will try to fit a tiny PUSCH in it. + // We want to avoid this, by ensuring this grant fills the remaining RBs. + unsigned nof_rbs_left = (~used_crbs).count(); + nof_rbs_left -= std::min(nof_rbs_left, mcs_prbs.n_prbs); + if (nof_rbs_left > 0 and nof_rbs_left < 5) { + mcs_prbs.n_prbs += nof_rbs_left; + } // Re-apply nof. PDSCH RBs to allocate limits. mcs_prbs.n_prbs = std::max(mcs_prbs.n_prbs, expert_cfg.pdsch_nof_rbs.start()); mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, expert_cfg.pdsch_nof_rbs.stop()); From 58327a3fd4101bf88f8356df9f3fc48e53957895 Mon Sep 17 00:00:00 2001 From: frankist Date: Tue, 7 Jan 2025 17:25:41 +0100 Subject: [PATCH 078/107] sched: add extension of last pusch grant when space is left empty at the right --- .../ue_scheduling/ue_cell_grid_allocator.cpp | 171 +++++++++++++++++- .../ue_scheduling/ue_cell_grid_allocator.h | 2 + 2 files changed, 172 insertions(+), 1 deletion(-) diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index a803dbb91b..57ca24c2bf 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -1085,5 +1085,174 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice void ue_cell_grid_allocator::post_process_results() { - // TODO + for (const cell_t& cell : cells) { + auto& pdcch_alloc = get_res_alloc(cell.cell_index)[0]; + + // In case PUSCHs allocations have been made, we try to ensure that RBs are not left empty. + auto& ul_pdcchs = pdcch_alloc.result.dl.ul_pdcchs; + if (ul_pdcchs.empty()) { + continue; + } + + bounded_bitset traversed_k2s(SCHEDULER_MAX_K2); + for (auto& pdcch : ul_pdcchs) { + if (pdcch.dci.type != dci_ul_rnti_config_type::c_rnti_f0_1) { + continue; + } + const auto& u = *ues.find_by_rnti(pdcch.ctx.rnti); + const ue_cell& ue_cell = *u.find_cell(cell.cell_index); + const uint8_t pusch_td_idx = pdcch.dci.c_rnti_f0_1.time_resource; + const search_space_info& ss = ue_cell.cfg().search_space(pdcch.ctx.context.ss_id); + const uint8_t k2 = ss.pusch_time_domain_list[pusch_td_idx].k2; + if (not traversed_k2s.test(k2 - 1)) { + post_process_ul_results(cell.cell_index, pdcch_alloc.slot + k2); + traversed_k2s.set(k2 - 1); + } + } + } +} + +void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, slot_point pusch_slot) +{ + // Note: For now, we just expand the last allocation to fill empty RBs. + // TODO: Fairer algorithm to fill remaining RBs that accounts for the UE buffer states as well. + + const cell_t& cell = cells[cell_idx]; + cell_resource_allocator& cell_alloc = get_res_alloc(cell.cell_index); + auto& pusch_alloc = cell_alloc[pusch_slot]; + if (pusch_alloc.result.ul.puschs.empty()) { + // There are no UL allocations for this slot. Move on to next cell. + return; + } + const cell_configuration& cell_cfg = cell_alloc.cfg; + const subcarrier_spacing scs = cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.scs; + + // Use last PUSCH to get reference UE config for this candidate. + ul_sched_info* last_pusch = nullptr; + for (auto& pusch : pusch_alloc.result.ul.puschs) { + if (not pusch.pusch_cfg.new_data or not pusch.pusch_cfg.rbs.is_type1()) { + // Type 0 not supported yet. + continue; + } + if (last_pusch == nullptr or last_pusch->pusch_cfg.rbs.type1().stop() < pusch.pusch_cfg.rbs.type1().stop()) { + last_pusch = &pusch; + } + } + if (last_pusch == nullptr) { + // There were no candidates for extension. + return; + } + if (not last_pusch->pusch_cfg.new_data) { + // It is a retx. We cannot resize it. + return; + } + + const vrb_interval& vrbs = last_pusch->pusch_cfg.rbs.type1(); + if (vrbs.length() >= expert_cfg.pusch_nof_rbs.length()) { + // The last UE reached max grant size. + return; + } + + ue& ue_ref = ues[last_pusch->context.ue_index]; + ue_cell& ue_cc = *ue_ref.find_cell(cell_cfg.cell_index); + + if (ue_ref.pending_ul_newtx_bytes() == 0) { + // No point in expanding UE grant if it has no more bytes to transmit. + return; + } + + const search_space_info& ss_info = ue_cc.cfg().search_space(last_pusch->context.ss_id); + const crb_interval ul_crb_lims = get_ul_rb_limits(expert_cfg, ss_info); + + const prb_bitmap used_crbs = pusch_alloc.ul_res_grid.used_crbs(scs, ul_crb_lims, last_pusch->pusch_cfg.symbols); + if (used_crbs.all()) { + // All CRBs were filled. + return; + } + + std::optional h_ul = ue_cc.harqs.ul_harq(to_harq_id(last_pusch->pusch_cfg.harq_id)); + if (not h_ul.has_value()) { + logger.error("Could not find HARQ id for existing PUSCH"); + return; + } + const dci_ul_rnti_config_type dci_type = h_ul->get_grant_params().dci_cfg_type; + if (dci_type != dci_ul_rnti_config_type::c_rnti_f0_1) { + // Only expansion for C-RNTI f0_1 supported. + return; + } + + // Check if there is a gap at the right of the last UE. + const bwp_configuration& active_bwp = *last_pusch->pusch_cfg.bwp_cfg; + crb_interval crbs = rb_helper::vrb_to_crb_ul_non_interleaved(vrbs, active_bwp.crbs.start()); + // Account for limits in number of RBs that can be allocated. + const unsigned max_crbs = adjust_ue_max_ul_nof_rbs(expert_cfg, ue_cc, dci_type, active_bwp.crbs.stop()); + if (max_crbs <= crbs.length()) { + return; + } + + crb_interval empty_crbs = rb_helper::find_empty_interval_of_length(used_crbs, max_crbs - crbs.stop(), crbs.stop()); + if (empty_crbs.empty() or empty_crbs.start() != crbs.stop()) { + // Could not extend existing PUSCH. + return; + } + // There are RBs empty to the left of the last allocation. Let's expand the allocation. + + crb_interval old_crbs = crbs; + crbs = {old_crbs.start(), empty_crbs.stop()}; + crbs.resize(adjust_nof_rbs_to_transform_precoding(crbs.length(), ue_cc, dci_type)); + + // Find respective PDCCH. + auto& pdcch_alloc = cell_alloc[pusch_slot - last_pusch->context.k2]; + auto it = + std::find_if(pdcch_alloc.result.dl.ul_pdcchs.begin(), + pdcch_alloc.result.dl.ul_pdcchs.end(), + [&](const pdcch_ul_information& pdcch) { return pdcch.ctx.rnti == last_pusch->pusch_cfg.rnti; }); + if (it == pdcch_alloc.result.dl.ul_pdcchs.end()) { + logger.error("rnti={}: Cannot find PDCCH associated with the given PUSCH at slot={}", + last_pusch->pusch_cfg.rnti, + pusch_slot); + return; + } + pdcch_ul_information& pdcch = *it; + + vrb_interval new_vrbs = rb_helper::crb_to_vrb_ul_non_interleaved(crbs, active_bwp.crbs.start()); + + // Mark resources as occupied in the ResourceGrid. + pusch_alloc.ul_res_grid.fill(grant_info{scs, last_pusch->pusch_cfg.symbols, empty_crbs}); + + // Recompute MCS and TBS. + const search_space_info& ss = ue_cc.cfg().search_space(pdcch.ctx.context.ss_id); + const auto& pusch_td_cfg = ss.pusch_time_domain_list[pdcch.dci.c_rnti_f0_1.time_resource]; + const unsigned nof_harq_bits = + last_pusch->uci.has_value() and last_pusch->uci->harq.has_value() ? last_pusch->uci->harq->harq_ack_nof_bits : 0; + const unsigned is_csi_rep = last_pusch->uci.has_value() and last_pusch->uci->csi.has_value() + ? last_pusch->uci->csi.value().csi_part1_nof_bits > 0 + : 0; + auto pusch_cfg = get_pusch_config_f0_1_c_rnti( + ue_cc.cfg(), pusch_td_cfg, last_pusch->pusch_cfg.nof_layers, nof_harq_bits, is_csi_rep); + bool contains_dc = + dc_offset_helper::is_contained(cell_cfg.expert_cfg.ue.initial_ul_dc_offset, cell_cfg.nof_ul_prbs, crbs); + std::optional mcs_tbs_info = + compute_ul_mcs_tbs(pusch_cfg, &ue_cc.cfg(), last_pusch->pusch_cfg.mcs_index, crbs.length(), contains_dc); + if (not mcs_tbs_info.has_value()) { + return; + } + + // Update DCI. + pdcch.dci.c_rnti_f0_1.frequency_resource = ra_frequency_type1_get_riv( + ra_frequency_type1_configuration{active_bwp.crbs.length(), vrbs.start(), vrbs.length()}); + + // Update PUSCH. + last_pusch->pusch_cfg.rbs = new_vrbs; + last_pusch->pusch_cfg.tb_size_bytes = mcs_tbs_info->tbs; + + // Update the number of PRBs used in the PUSCH allocation. + ue_cc.get_ul_power_controller().update_pusch_pw_ctrl_state(pusch_alloc.slot, crbs.length()); + + // Update HARQ. + ul_harq_alloc_context pusch_sched_ctx; + pusch_sched_ctx.dci_cfg_type = dci_type; + pusch_sched_ctx.olla_mcs = h_ul->get_grant_params().olla_mcs; + pusch_sched_ctx.slice_id = h_ul->get_grant_params().slice_id; + h_ul->save_grant_params(pusch_sched_ctx, last_pusch->pusch_cfg); } diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h index ac7af4454a..eef77de1e3 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h @@ -66,6 +66,8 @@ class ue_cell_grid_allocator return *cells[cell_index].cell_alloc; } + void post_process_ul_results(du_cell_index_t cell_idx, slot_point pusch_slot); + const scheduler_ue_expert_config& expert_cfg; ue_repository& ues; From 5bad9e3124027603ef3d193ecab0aa5b22866761 Mon Sep 17 00:00:00 2001 From: frankist Date: Tue, 7 Jan 2025 17:46:25 +0100 Subject: [PATCH 079/107] sched: minor fixes in the resizing of the last UL grant --- .../ue_scheduling/ue_cell_grid_allocator.cpp | 58 +++++-------------- 1 file changed, 15 insertions(+), 43 deletions(-) diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 57ca24c2bf..26ccdd65c5 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -673,28 +673,6 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice return {alloc_status::skip_slot}; } - // When checking the number of remaining grants for PUSCH, take into account that the PUCCH grants for this UE will - // be removed when multiplexing the UCI on PUSCH. - unsigned pusch_pdu_rem_space = get_space_left_for_pusch_pdus(pusch_alloc.result, u.crnti, expert_cfg); - if (pusch_pdu_rem_space == 0) { - if (pusch_alloc.result.ul.puschs.size() >= expert_cfg.max_puschs_per_slot) { - logger.info( - "ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Max number of PUSCHs per slot {} was reached.", - fmt::underlying(u.ue_index), - u.crnti, - pusch_alloc.slot, - expert_cfg.max_puschs_per_slot); - } else { - logger.info("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Max number of UL grants per slot {} " - "was reached.", - fmt::underlying(u.ue_index), - u.crnti, - pusch_alloc.slot, - expert_cfg.max_puschs_per_slot); - } - return {alloc_status::skip_slot}; - } - // [Implementation-defined] We skip allocation of PUSCH if there is already a PUCCH grant scheduled using common // PUCCH resources. if (get_uci_alloc(grant.cell_index).has_uci_harq_on_common_pucch_res(u.crnti, pusch_alloc.slot)) { @@ -744,11 +722,6 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice // Compute MCS and PRBs based on grant parameters. mcs_prbs = ue_cc->required_ul_prbs(pusch_td_cfg, grant.recommended_nof_bytes.value(), dci_type); - // [Implementation-defined] Check whether max. UL grants per slot is reached if PUSCH for current UE succeeds. If - // so, allocate remaining RBs to the current UE only if it's a new Tx. - if (pusch_pdu_rem_space == 1 and not u.has_pending_sr()) { - mcs_prbs.n_prbs = rb_helper::find_empty_interval_of_length(used_crbs, used_crbs.size(), 0).length(); - } // Due to the pre-allocated UCI bits, MCS 0 and PRB 1 would not leave any space for the payload on the TBS, as // all the space would be taken by the UCI bits. As a result of this, the effective code rate would be 0 and the // allocation would fail and be postponed to the next slot. @@ -761,15 +734,6 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice ++mcs_prbs.n_prbs; } - // [Implementation-defined] - // Check whether to allocate all remaining RBs or not. This is done to ensure we allocate only X nof. UEs per slot - // and not X+1 nof. UEs. One way is by checking if the emtpy interval is less than 2 times the required RBs. If - // so, allocate all remaining RBs. NOTE: This approach won't hold good in case of low traffic scenario. - const unsigned twice_grant_crbs_length = - rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs * 2, 0).length(); - if (twice_grant_crbs_length < (mcs_prbs.n_prbs * 2)) { - mcs_prbs.n_prbs = twice_grant_crbs_length; - } // Limit nof. RBs to allocate to maximum RBs provided in grant. if (grant.max_nof_rbs.has_value()) { mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, grant.max_nof_rbs.value()); @@ -1147,7 +1111,7 @@ void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, s return; } - const vrb_interval& vrbs = last_pusch->pusch_cfg.rbs.type1(); + vrb_interval vrbs = last_pusch->pusch_cfg.rbs.type1(); if (vrbs.length() >= expert_cfg.pusch_nof_rbs.length()) { // The last UE reached max grant size. return; @@ -1185,7 +1149,8 @@ void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, s const bwp_configuration& active_bwp = *last_pusch->pusch_cfg.bwp_cfg; crb_interval crbs = rb_helper::vrb_to_crb_ul_non_interleaved(vrbs, active_bwp.crbs.start()); // Account for limits in number of RBs that can be allocated. - const unsigned max_crbs = adjust_ue_max_ul_nof_rbs(expert_cfg, ue_cc, dci_type, active_bwp.crbs.stop()); + const unsigned max_crbs = + adjust_ue_max_ul_nof_rbs(expert_cfg, ue_cc, dci_type, crbs.length() + (active_bwp.crbs.stop() - crbs.stop())); if (max_crbs <= crbs.length()) { return; } @@ -1215,10 +1180,8 @@ void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, s } pdcch_ul_information& pdcch = *it; - vrb_interval new_vrbs = rb_helper::crb_to_vrb_ul_non_interleaved(crbs, active_bwp.crbs.start()); - - // Mark resources as occupied in the ResourceGrid. - pusch_alloc.ul_res_grid.fill(grant_info{scs, last_pusch->pusch_cfg.symbols, empty_crbs}); + // Derive new VRBs + vrbs = rb_helper::crb_to_vrb_ul_non_interleaved(crbs, active_bwp.crbs.start()); // Recompute MCS and TBS. const search_space_info& ss = ue_cc.cfg().search_space(pdcch.ctx.context.ss_id); @@ -1238,12 +1201,15 @@ void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, s return; } + // Mark resources as occupied in the ResourceGrid. + pusch_alloc.ul_res_grid.fill(grant_info{scs, last_pusch->pusch_cfg.symbols, empty_crbs}); + // Update DCI. pdcch.dci.c_rnti_f0_1.frequency_resource = ra_frequency_type1_get_riv( ra_frequency_type1_configuration{active_bwp.crbs.length(), vrbs.start(), vrbs.length()}); // Update PUSCH. - last_pusch->pusch_cfg.rbs = new_vrbs; + last_pusch->pusch_cfg.rbs = vrbs; last_pusch->pusch_cfg.tb_size_bytes = mcs_tbs_info->tbs; // Update the number of PRBs used in the PUSCH allocation. @@ -1255,4 +1221,10 @@ void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, s pusch_sched_ctx.olla_mcs = h_ul->get_grant_params().olla_mcs; pusch_sched_ctx.slice_id = h_ul->get_grant_params().slice_id; h_ul->save_grant_params(pusch_sched_ctx, last_pusch->pusch_cfg); + + logger.debug("rnti={} h_id={}: Extended PUSCH grant from {} to {} CRBs", + ue_ref.crnti, + fmt::underlying(h_ul->id()), + old_crbs, + crbs); } From 8b593556f63d24121a9d80eeb882f12baf45d3de Mon Sep 17 00:00:00 2001 From: frankist Date: Wed, 8 Jan 2025 00:22:09 +0100 Subject: [PATCH 080/107] sched: small fix in CRB extension in UL grants --- lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 26ccdd65c5..4fdea1380d 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -1155,7 +1155,7 @@ void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, s return; } - crb_interval empty_crbs = rb_helper::find_empty_interval_of_length(used_crbs, max_crbs - crbs.stop(), crbs.stop()); + crb_interval empty_crbs = rb_helper::find_empty_interval_of_length(used_crbs, max_crbs - crbs.length(), crbs.stop()); if (empty_crbs.empty() or empty_crbs.start() != crbs.stop()) { // Could not extend existing PUSCH. return; From 3490eb523e84f0e3e153915256abec98ad9f4a4d Mon Sep 17 00:00:00 2001 From: frankist Date: Wed, 8 Jan 2025 15:38:42 +0100 Subject: [PATCH 081/107] sched: improve comment for post process ul result --- .../ue_scheduling/ue_cell_grid_allocator.cpp | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 4fdea1380d..218cba87ab 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -232,14 +232,6 @@ dl_alloc_result ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& if (grant.max_nof_rbs.has_value()) { mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, grant.max_nof_rbs.value()); } - // [Implementation-defined] - // Sometimes just a few 1-4 RBs are left in the grid, and the scheduler policy will try to fit a tiny PUSCH in it. - // We want to avoid this, by ensuring this grant fills the remaining RBs. - unsigned nof_rbs_left = (~used_crbs).count(); - nof_rbs_left -= std::min(nof_rbs_left, mcs_prbs.n_prbs); - if (nof_rbs_left > 0 and nof_rbs_left < 5) { - mcs_prbs.n_prbs += nof_rbs_left; - } // Re-apply nof. PDSCH RBs to allocate limits. mcs_prbs.n_prbs = std::max(mcs_prbs.n_prbs, expert_cfg.pdsch_nof_rbs.start()); mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, expert_cfg.pdsch_nof_rbs.stop()); @@ -734,6 +726,16 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice ++mcs_prbs.n_prbs; } + // [Implementation-defined] + // Sometimes just a few 1-4 RBs are left in the grid, and the scheduler policy will try to fit a tiny PUSCH in it. + // This will lead to a waste of PDCCHs that could be used for another k2 value. + // We want to avoid this, by ensuring this grant fills the remaining RBs. + unsigned nof_rbs_left = (~used_crbs).count(); + nof_rbs_left -= std::min(nof_rbs_left, mcs_prbs.n_prbs); + if (nof_rbs_left > 0 and nof_rbs_left < 5) { + mcs_prbs.n_prbs += nof_rbs_left; + } + // Limit nof. RBs to allocate to maximum RBs provided in grant. if (grant.max_nof_rbs.has_value()) { mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, grant.max_nof_rbs.value()); @@ -1058,6 +1060,8 @@ void ue_cell_grid_allocator::post_process_results() continue; } + // For the same slot, PDCCHs with different k2 values could have been scheduled. We will go through allocated + // PUSCH slots and see if there are any RBs remaining to be scheduled. bounded_bitset traversed_k2s(SCHEDULER_MAX_K2); for (auto& pdcch : ul_pdcchs) { if (pdcch.dci.type != dci_ul_rnti_config_type::c_rnti_f0_1) { From dc79824feeb7c8d1c6e7e5a239d8bd405dbf20c6 Mon Sep 17 00:00:00 2001 From: frankist Date: Wed, 8 Jan 2025 23:21:44 +0100 Subject: [PATCH 082/107] sched: fix assertion due to access to invalid ue_index --- lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 218cba87ab..e96998c793 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -1098,7 +1098,7 @@ void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, s // Use last PUSCH to get reference UE config for this candidate. ul_sched_info* last_pusch = nullptr; for (auto& pusch : pusch_alloc.result.ul.puschs) { - if (not pusch.pusch_cfg.new_data or not pusch.pusch_cfg.rbs.is_type1()) { + if (not pusch.pusch_cfg.rbs.is_type1()) { // Type 0 not supported yet. continue; } @@ -1120,7 +1120,10 @@ void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, s // The last UE reached max grant size. return; } - + if (not ues.contains(last_pusch->context.ue_index)) { + // In case of TC-RNTI, the UE index might not yet exist. + return; + } ue& ue_ref = ues[last_pusch->context.ue_index]; ue_cell& ue_cc = *ue_ref.find_cell(cell_cfg.cell_index); From 8fcd3111924656712a81e6751919b9db41f48bf3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Alfredo=20S=C3=A1ez?= Date: Thu, 9 Jan 2025 09:29:43 +0100 Subject: [PATCH 083/107] ci,e2e: fix ru dummy not crash criteria --- tests/e2e/tests/test_mode.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/tests/e2e/tests/test_mode.py b/tests/e2e/tests/test_mode.py index acad668ee2..91060309c6 100644 --- a/tests/e2e/tests/test_mode.py +++ b/tests/e2e/tests/test_mode.py @@ -239,7 +239,15 @@ def test_ru_16cell_50ue_not_crash( Run gnb with sanitizers in test mode ru dummy. It ignores warnings and KOs, so it will fail if the gnb+sanitizer fails """ - _test_ru(retina_manager, retina_data, gnb, ru_config="config_ru_16cell_50ue.yml") + _test_ru( + retina_manager, + retina_data, + gnb, + ru_config="config_ru_16cell_50ue.yml", + gnb_stop_timeout=150, + warning_as_errors=False, + fail_if_kos=False, + ) # pylint: disable=too-many-arguments,too-many-positional-arguments From 097539ea405dbb6b09fbd968a479ad0b43d975a6 Mon Sep 17 00:00:00 2001 From: Andre Puschmann Date: Thu, 9 Jan 2025 08:46:21 +0100 Subject: [PATCH 084/107] changelog: add entry for new version --- CHANGELOG | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG b/CHANGELOG index 8e039793ef..9c43ceed8f 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,9 @@ Change Log for Releases ======================= +## 24.10.1 +- Fix PUSCH benchmark + ## 24.10 - CU/DU split - RAN slicing From 790b98e1ddd56ef1c7306c745fcb41ed2947182f Mon Sep 17 00:00:00 2001 From: frankist Date: Wed, 8 Jan 2025 23:13:04 +0100 Subject: [PATCH 085/107] sched: fix DL GBR QoS handling --- lib/scheduler/policy/scheduler_time_pf.cpp | 45 ++++-- lib/scheduler/policy/scheduler_time_pf.h | 11 +- tests/unittests/scheduler/CMakeLists.txt | 1 + .../scheduler/scheduler_qos_test.cpp | 130 ++++++++++++++++++ .../test_utils/indication_generators.cpp | 2 +- 5 files changed, 178 insertions(+), 11 deletions(-) create mode 100644 tests/unittests/scheduler/scheduler_qos_test.cpp diff --git a/lib/scheduler/policy/scheduler_time_pf.cpp b/lib/scheduler/policy/scheduler_time_pf.cpp index 326dc33188..30b6d9baeb 100644 --- a/lib/scheduler/policy/scheduler_time_pf.cpp +++ b/lib/scheduler/policy/scheduler_time_pf.cpp @@ -99,10 +99,13 @@ void scheduler_time_pf::dl_sched(ue_pdsch_allocator& pdsch_alloc, // Update DL priority queue. dl_queue.clear(); + slot_point pdsch_slot = slice_candidate.get_slot_tx(); + unsigned nof_slots_elapsed = last_pdsch_slot.valid() ? pdsch_slot - last_pdsch_slot : 1; + last_pdsch_slot = pdsch_slot; for (const auto& u : ues) { ue_ctxt& ctxt = ue_history_db[u.ue_index()]; ctxt.compute_dl_prio( - u, slice_candidate.id(), res_grid.get_pdcch_slot(u.get_pcell().cell_index), slice_candidate.get_slot_tx()); + u, slice_candidate.id(), res_grid.get_pdcch_slot(u.get_pcell().cell_index), pdsch_slot, nof_slots_elapsed); dl_queue.push(&ctxt); } @@ -309,12 +312,13 @@ static double compute_ul_rate_weight(const slice_ue& u, span ul_avg_rate void scheduler_time_pf::ue_ctxt::compute_dl_prio(const slice_ue& u, ran_slice_id_t slice_id, slot_point pdcch_slot, - slot_point pdsch_slot) + slot_point pdsch_slot, + unsigned nof_slots_elapsed) { dl_prio = forbid_prio; // Process previous slot allocated bytes and compute average. - compute_dl_avg_rate(u); + compute_dl_avg_rate(u, nof_slots_elapsed); const ue_cell* ue_cc = u.find_cell(cell_index); if (ue_cc == nullptr) { @@ -480,22 +484,47 @@ void scheduler_time_pf::ue_ctxt::compute_ul_prio(const slice_ue& u, sr_ind_received = u.has_pending_sr(); } -void scheduler_time_pf::ue_ctxt::compute_dl_avg_rate(const slice_ue& u) +void scheduler_time_pf::ue_ctxt::compute_dl_avg_rate(const slice_ue& u, unsigned nof_slots_elapsed) { // Redimension LCID arrays to the UE configured bearers. dl_alloc_bytes_per_lc.resize(u.get_bearers().size(), 0); dl_avg_rate_per_lc.resize(dl_alloc_bytes_per_lc.size(), 0); + // In case more than one slot elapsed. + for (unsigned s = 0; s != nof_slots_elapsed - 1; ++s) { + for (unsigned i = 0; i != dl_alloc_bytes_per_lc.size(); ++i) { + if (not u.contains(uint_to_lcid(i))) { + // Skip LCIDs that are not configured. + dl_alloc_bytes_per_lc[i] = 0; + dl_avg_rate_per_lc[i] = 0; + continue; + } + if (dl_nof_samples < 1 / parent->exp_avg_alpha) { + // Fast start before transitioning to exponential average. + dl_avg_rate_per_lc[i] -= dl_avg_rate_per_lc[i] / (dl_nof_samples + 1); + } else { + dl_avg_rate_per_lc[i] -= parent->exp_avg_alpha * dl_avg_rate_per_lc[i]; + } + } + if (dl_nof_samples < 1 / parent->exp_avg_alpha) { + // Fast start before transitioning to exponential average. + total_dl_avg_rate_ -= total_dl_avg_rate_ / (dl_nof_samples + 1); + } else { + total_dl_avg_rate_ -= parent->exp_avg_alpha * total_dl_avg_rate_; + } + dl_nof_samples++; + } + // Compute DL average rate on a per-logical channel basis. for (unsigned i = 0; i != dl_alloc_bytes_per_lc.size(); ++i) { if (not u.contains(uint_to_lcid(i))) { // Skip LCIDs that are not configured. dl_alloc_bytes_per_lc[i] = 0; dl_avg_rate_per_lc[i] = 0; + continue; } unsigned sched_bytes = dl_alloc_bytes_per_lc[i]; - if (dl_nof_samples < 1 / parent->exp_avg_alpha) { // Fast start before transitioning to exponential average. dl_avg_rate_per_lc[i] += (sched_bytes - dl_avg_rate_per_lc[i]) / (dl_nof_samples + 1); @@ -509,12 +538,12 @@ void scheduler_time_pf::ue_ctxt::compute_dl_avg_rate(const slice_ue& u) } // Compute DL average rate of the UE. + unsigned sched_bytes = dl_sum_alloc_bytes; if (dl_nof_samples < 1 / parent->exp_avg_alpha) { // Fast start before transitioning to exponential average. - total_dl_avg_rate_ = total_dl_avg_rate_ + (dl_sum_alloc_bytes - total_dl_avg_rate_) / (dl_nof_samples + 1); + total_dl_avg_rate_ = total_dl_avg_rate_ + (sched_bytes - total_dl_avg_rate_) / (dl_nof_samples + 1); } else { - total_dl_avg_rate_ = - (1 - parent->exp_avg_alpha) * total_dl_avg_rate_ + (parent->exp_avg_alpha) * dl_sum_alloc_bytes; + total_dl_avg_rate_ = (1 - parent->exp_avg_alpha) * total_dl_avg_rate_ + (parent->exp_avg_alpha) * sched_bytes; } // Flush allocated bytes for the current slot. diff --git a/lib/scheduler/policy/scheduler_time_pf.h b/lib/scheduler/policy/scheduler_time_pf.h index 484e2704e2..cc2a50efb5 100644 --- a/lib/scheduler/policy/scheduler_time_pf.h +++ b/lib/scheduler/policy/scheduler_time_pf.h @@ -53,7 +53,11 @@ class scheduler_time_pf : public scheduler_policy [[nodiscard]] double total_ul_avg_rate() const { return ul_nof_samples == 0 ? 0 : total_ul_avg_rate_; } /// Computes the priority of the UE to be scheduled in DL based on the QoS and proportional fair metric. - void compute_dl_prio(const slice_ue& u, ran_slice_id_t slice_id, slot_point pdcch_slot, slot_point pdsch_slot); + void compute_dl_prio(const slice_ue& u, + ran_slice_id_t slice_id, + slot_point pdcch_slot, + slot_point pdsch_slot, + unsigned nof_slots_elapsed); /// Computes the priority of the UE to be scheduled in UL based on the proportional fair metric. void compute_ul_prio(const slice_ue& u, ran_slice_id_t slice_id, slot_point pdcch_slot, slot_point pusch_slot); @@ -73,7 +77,7 @@ class scheduler_time_pf : public scheduler_policy bool sr_ind_received = false; private: - void compute_dl_avg_rate(const slice_ue& u); + void compute_dl_avg_rate(const slice_ue& u, unsigned nof_slots_elapsed); void compute_ul_avg_rate(const slice_ue& u); // Sum of DL bytes allocated for a given slot, before it is taken into account in the average rate computation. @@ -174,6 +178,9 @@ class scheduler_time_pf : public scheduler_policy } }; + slot_point last_pdsch_slot; + slot_point last_pusch_slot; + /// Priority queue of UEs to be scheduled in DL. The UE in front of the queue has highest priority and vice versa. ue_dl_queue_t dl_queue; /// Priority queue of UEs to be scheduled in UL. The UE in front of the queue has highest priority and vice versa. diff --git a/tests/unittests/scheduler/CMakeLists.txt b/tests/unittests/scheduler/CMakeLists.txt index d3f2d1a169..ac9a2b2fd9 100644 --- a/tests/unittests/scheduler/CMakeLists.txt +++ b/tests/unittests/scheduler/CMakeLists.txt @@ -44,6 +44,7 @@ add_executable(scheduler_test scheduler_ta_cmd_test.cpp multi_cell_scheduler_test.cpp multi_slice_scheduler_test.cpp + scheduler_qos_test.cpp ) target_link_libraries(scheduler_test srsran_sched diff --git a/tests/unittests/scheduler/scheduler_qos_test.cpp b/tests/unittests/scheduler/scheduler_qos_test.cpp new file mode 100644 index 0000000000..a0c639ba16 --- /dev/null +++ b/tests/unittests/scheduler/scheduler_qos_test.cpp @@ -0,0 +1,130 @@ +/* + * + * Copyright 2021-2025 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "test_utils/indication_generators.h" +#include "test_utils/result_test_helpers.h" +#include "test_utils/scheduler_test_simulator.h" +#include "tests/test_doubles/scheduler/cell_config_builder_profiles.h" +#include "tests/test_doubles/scheduler/pucch_res_test_builder_helper.h" +#include "tests/test_doubles/scheduler/scheduler_config_helper.h" +#include + +using namespace srsran; + +class scheduler_qos_test : public scheduler_test_simulator, public ::testing::Test +{ + struct ue_stats { + uint64_t dl_bytes_sum = 0; + }; + +public: + scheduler_qos_test() : scheduler_test_simulator(4, subcarrier_spacing::kHz30) + { + const unsigned TEST_NOF_UES = 8; + ue_stats_map.resize(TEST_NOF_UES); + + params = cell_config_builder_profiles::tdd(subcarrier_spacing::kHz30); + + // Add Cell. + auto cell_cfg_req = sched_config_helper::make_default_sched_cell_configuration_request(params); + cell_cfg_req.rrm_policy_members.resize(1); + cell_cfg_req.rrm_policy_members[0].rrc_member.s_nssai.sst = slice_service_type{1}; + cell_cfg_req.rrm_policy_members[0].policy_sched_cfg = time_pf_scheduler_expert_config{}; + this->add_cell(cell_cfg_req); + + srs_du::pucch_builder_params pucch_basic_params{.nof_ue_pucch_f0_or_f1_res_harq = 8, + .nof_ue_pucch_f2_or_f3_or_f4_res_harq = 8, + .nof_sr_resources = 8, + .nof_csi_resources = 8}; + auto& f1_params = pucch_basic_params.f0_or_f1_params.emplace(); + f1_params.nof_cyc_shifts = srs_du::nof_cyclic_shifts::twelve; + f1_params.occ_supported = true; + pucch_cfg_builder.setup(cell_cfg_list[0], pucch_basic_params); + + // Add UE0 with GBR + du_ue_index_t ue_idx = to_du_ue_index(0); + auto ue_cfg = sched_config_helper::create_default_sched_ue_creation_request(params, {LCID_MIN_DRB}); + ue_cfg.ue_index = ue_idx; + ue_cfg.crnti = to_rnti(0x4601 + (unsigned)ue_idx); + ue_cfg.cfg.lc_config_list.value()[2].rrm_policy.s_nssai.sst = slice_service_type{1}; + ue_cfg.cfg.lc_config_list.value()[2].qos.emplace(); + ue_cfg.cfg.lc_config_list.value()[2].qos.value().gbr_qos_info.emplace(); + ue_cfg.cfg.lc_config_list.value()[2].qos.value().gbr_qos_info.value().gbr_dl = 10e6; + ue_cfg.cfg.lc_config_list.value()[2].qos.value().gbr_qos_info.value().gbr_ul = 10e6; + report_fatal_error_if_not(pucch_cfg_builder.add_build_new_ue_pucch_cfg(ue_cfg.cfg.cells.value()[0].serv_cell_cfg), + "Failed to allocate PUCCH resources"); + this->add_ue(ue_cfg); + + // Add UEs without GBR. + ue_cfg.cfg.lc_config_list.value()[2].qos.reset(); + for (unsigned i = 1; i != TEST_NOF_UES; ++i) { + ue_idx = to_du_ue_index(i); + ue_cfg.ue_index = ue_idx; + ue_cfg.crnti = to_rnti(0x4601 + (unsigned)ue_idx); + report_fatal_error_if_not(pucch_cfg_builder.add_build_new_ue_pucch_cfg(ue_cfg.cfg.cells.value()[0].serv_cell_cfg), + "Failed to allocate PUCCH resources"); + this->add_ue(ue_cfg); + } + + // Enqueue enough bytes for continuous DL tx. + for (unsigned i = 0; i != TEST_NOF_UES; ++i) { + dl_buffer_state_indication_message dl_buf_st{to_du_ue_index(i), LCID_MIN_DRB, 10000000}; + this->push_dl_buffer_state(dl_buf_st); + } + } + + cell_config_builder_params params; + + pucch_res_builder_test_helper pucch_cfg_builder; + + std::vector ue_stats_map; +}; + +TEST_F(scheduler_qos_test, when_ue_has_gbr_drb_it_gets_higher_priority) +{ + const unsigned MAX_NOF_SLOT_RUNS = 1000; + + for (unsigned i = 0; i != MAX_NOF_SLOT_RUNS; ++i) { + this->run_slot(); + + span ue_grants = this->last_sched_res_list[0]->dl.ue_grants; + for (const dl_msg_alloc& grant : ue_grants) { + ue_stats_map[grant.context.ue_index].dl_bytes_sum += grant.pdsch_cfg.codewords[0].tb_size_bytes; + } + + for (const pucch_info& pucch : this->last_sched_res_list[to_du_cell_index(0)]->ul.pucchs) { + if (pucch.format == pucch_format::FORMAT_1 and pucch.format_1.sr_bits != sr_nof_bits::no_sr) { + // Skip SRs for this test. + continue; + } + + du_ue_index_t ue_idx = to_du_ue_index((unsigned)pucch.crnti - 0x4601); + uci_indication uci_ind = test_helper::create_uci_indication(this->last_result_slot(), ue_idx, pucch); + this->sched->handle_uci_indication(uci_ind); + } + } + + double nof_sec_elapsed = (MAX_NOF_SLOT_RUNS / (double)get_nof_slots_per_subframe(params.scs_common)) / 1000; + std::vector ue_dl_rate_mbps(ue_stats_map.size(), 0); + for (unsigned i = 0; i != ue_stats_map.size(); ++i) { + ue_dl_rate_mbps[i] = ue_stats_map[i].dl_bytes_sum * 8U * 1e-6 / nof_sec_elapsed; + } + + fmt::memory_buffer fmtbuf; + for (unsigned i = 0; i != ue_dl_rate_mbps.size(); ++i) { + fmt::format_to(std::back_inserter(fmtbuf), " {:.3}", ue_dl_rate_mbps[i]); + } + test_logger.info("DL bit rates [Mbps]: [{}]", fmt::to_string(fmtbuf)); + + const unsigned GBR_UE_INDEX = 0; + for (unsigned i = 1; i != ue_stats_map.size(); ++i) { + ASSERT_GT(ue_dl_rate_mbps[GBR_UE_INDEX], ue_dl_rate_mbps[i]) << "UE GBR rate < UE non-GBR rate"; + } +} diff --git a/tests/unittests/scheduler/test_utils/indication_generators.cpp b/tests/unittests/scheduler/test_utils/indication_generators.cpp index a42ad875de..388f78dcf9 100644 --- a/tests/unittests/scheduler/test_utils/indication_generators.cpp +++ b/tests/unittests/scheduler/test_utils/indication_generators.cpp @@ -62,11 +62,11 @@ srsran::test_helper::create_uci_indication(slot_point uci_sl, du_ue_index_t ue_i uci_indication::uci_pdu::uci_pucch_f2_or_f3_or_f4_pdu f2{}; f2.harqs.resize(pucch_pdu.format_2.harq_ack_nof_bits); std::fill(f2.harqs.begin(), f2.harqs.end(), mac_harq_ack_report_status::ack); - uci_ind.ucis[0].pdu = f2; if (pucch_pdu.format_2.csi_part1_bits > 0) { f2.csi.emplace(); f2.csi->first_tb_wideband_cqi = 15; } + uci_ind.ucis[0].pdu = f2; } break; default: report_fatal_error("Unsupported PUCCH format"); From 5778081145ac954d4c8ec5df61b539e54eff07fb Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 9 Jan 2025 10:44:08 +0100 Subject: [PATCH 086/107] sched: extend sched QoS unit test to UL --- include/srsran/scheduler/result/pusch_info.h | 4 +- .../logging/scheduler_result_logger.cpp | 4 +- lib/scheduler/support/sch_pdu_builder.cpp | 6 +- .../fapi_adaptor/mac/messages/helpers.cpp | 2 +- .../scheduler/scheduler_qos_test.cpp | 54 +++++++++++++++-- .../test_utils/indication_generators.cpp | 58 +++++++++++++++---- .../test_utils/indication_generators.h | 8 +++ 7 files changed, 113 insertions(+), 23 deletions(-) diff --git a/include/srsran/scheduler/result/pusch_info.h b/include/srsran/scheduler/result/pusch_info.h index 1f3b342d13..a5286cd190 100644 --- a/include/srsran/scheduler/result/pusch_info.h +++ b/include/srsran/scheduler/result/pusch_info.h @@ -12,7 +12,9 @@ #include "srsran/ran/csi_report/csi_report_configuration.h" #include "srsran/ran/pusch/pusch_mcs.h" +#include "srsran/ran/rnti.h" #include "srsran/ran/uci/uci_configuration.h" +#include "srsran/scheduler/harq_id.h" #include "srsran/scheduler/result/dmrs_info.h" #include "srsran/scheduler/result/vrb_alloc.h" @@ -61,7 +63,7 @@ struct pusch_information { /// Redundancy version index (see TS38.214 Table 6.1.4). Values: {0,...,3}. uint8_t rv_index; /// HARQ process number as per TS38.212 Section 6.3.1.1. Values: {0,...,15}. - uint8_t harq_id; + harq_id_t harq_id; /// \brief Signals whether the PUSCH PDU corresponds to an initial transmission or a retransmission of a MAC PDU for /// this HARQ process ID for this TB. Note: Unlike NDI, new_data does not toggle to indicate new transmission, /// but is set to 1. diff --git a/lib/scheduler/logging/scheduler_result_logger.cpp b/lib/scheduler/logging/scheduler_result_logger.cpp index 330cc17aea..528c69d702 100644 --- a/lib/scheduler/logging/scheduler_result_logger.cpp +++ b/lib/scheduler/logging/scheduler_result_logger.cpp @@ -234,7 +234,7 @@ void scheduler_result_logger::log_debug(const sched_result& result, std::chrono: fmt::underlying(ul_info.context.ue_index), ul_info.context.ue_index == INVALID_DU_UE_INDEX ? "t" : "", ul_info.pusch_cfg.rnti, - ul_info.pusch_cfg.harq_id, + fmt::underlying(ul_info.pusch_cfg.harq_id), ul_info.pusch_cfg.rbs, ul_info.pusch_cfg.symbols, ul_info.pusch_cfg.tb_size_bytes, @@ -395,7 +395,7 @@ void scheduler_result_logger::log_info(const sched_result& result, std::chrono:: fmtbuf.size() == 0 ? "" : ", ", fmt::underlying(ue_msg.context.ue_index), ue_msg.pusch_cfg.rnti, - ue_msg.pusch_cfg.harq_id, + fmt::underlying(ue_msg.pusch_cfg.harq_id), fmt::underlying(ue_msg.context.ss_id), ue_msg.pusch_cfg.rbs, ue_msg.pusch_cfg.new_data, diff --git a/lib/scheduler/support/sch_pdu_builder.cpp b/lib/scheduler/support/sch_pdu_builder.cpp index 42d885d61c..167b3bb013 100644 --- a/lib/scheduler/support/sch_pdu_builder.cpp +++ b/lib/scheduler/support/sch_pdu_builder.cpp @@ -636,7 +636,7 @@ void srsran::build_pusch_f0_0_tc_rnti(pusch_information& pusch pusch.pusch_dmrs_id = cell_cfg.pci; pusch.rv_index = dci_cfg.redundancy_version; // TS 38.321, 5.4.2.1 - "For UL transmission with UL grant in RA Response, HARQ process identifier 0 is used". - pusch.harq_id = 0; + pusch.harq_id = to_harq_id(0); pusch.new_data = is_new_data; pusch.tb_size_bytes = tbs_bytes; @@ -691,7 +691,7 @@ void srsran::build_pusch_f0_0_c_rnti(pusch_information& pusch, // HARQ. pusch.rv_index = dci_cfg.redundancy_version; - pusch.harq_id = dci_cfg.harq_process_number; + pusch.harq_id = to_harq_id(dci_cfg.harq_process_number); pusch.new_data = is_new_data; } @@ -765,6 +765,6 @@ void srsran::build_pusch_f0_1_c_rnti(pusch_information& pusch, // HARQ. pusch.rv_index = dci_cfg.redundancy_version; - pusch.harq_id = dci_cfg.harq_process_number; + pusch.harq_id = to_harq_id(dci_cfg.harq_process_number); pusch.new_data = is_new_data; } diff --git a/tests/unittests/fapi_adaptor/mac/messages/helpers.cpp b/tests/unittests/fapi_adaptor/mac/messages/helpers.cpp index 1e0468c426..32d49c414e 100644 --- a/tests/unittests/fapi_adaptor/mac/messages/helpers.cpp +++ b/tests/unittests/fapi_adaptor/mac/messages/helpers.cpp @@ -566,7 +566,7 @@ ul_sched_info_test_helper unittests::build_valid_pusch_pdu() pusch.pusch_dmrs_id = 18; pusch.dmrs_hopping_mode = pusch_information::dmrs_hopping_mode::no_hopping; pusch.rv_index = 1; - pusch.harq_id = 2; + pusch.harq_id = to_harq_id(2); pusch.new_data = true; pusch.tb_size_bytes = 11; pusch.num_cb = 0; diff --git a/tests/unittests/scheduler/scheduler_qos_test.cpp b/tests/unittests/scheduler/scheduler_qos_test.cpp index a0c639ba16..faa7d2aaf9 100644 --- a/tests/unittests/scheduler/scheduler_qos_test.cpp +++ b/tests/unittests/scheduler/scheduler_qos_test.cpp @@ -22,6 +22,7 @@ class scheduler_qos_test : public scheduler_test_simulator, public ::testing::Te { struct ue_stats { uint64_t dl_bytes_sum = 0; + uint64_t ul_bytes_sum = 0; }; public: @@ -57,7 +58,7 @@ class scheduler_qos_test : public scheduler_test_simulator, public ::testing::Te ue_cfg.cfg.lc_config_list.value()[2].qos.emplace(); ue_cfg.cfg.lc_config_list.value()[2].qos.value().gbr_qos_info.emplace(); ue_cfg.cfg.lc_config_list.value()[2].qos.value().gbr_qos_info.value().gbr_dl = 10e6; - ue_cfg.cfg.lc_config_list.value()[2].qos.value().gbr_qos_info.value().gbr_ul = 10e6; + ue_cfg.cfg.lc_config_list.value()[2].qos.value().gbr_qos_info.value().gbr_ul = 5e6; report_fatal_error_if_not(pucch_cfg_builder.add_build_new_ue_pucch_cfg(ue_cfg.cfg.cells.value()[0].serv_cell_cfg), "Failed to allocate PUCCH resources"); this->add_ue(ue_cfg); @@ -78,6 +79,16 @@ class scheduler_qos_test : public scheduler_test_simulator, public ::testing::Te dl_buffer_state_indication_message dl_buf_st{to_du_ue_index(i), LCID_MIN_DRB, 10000000}; this->push_dl_buffer_state(dl_buf_st); } + + // Enqueue BSR. + for (unsigned i = 0; i != TEST_NOF_UES; ++i) { + ul_bsr_indication_message bsr{to_du_cell_index(0), + to_du_ue_index(i), + to_rnti(0x4601 + i), + bsr_format::LONG_BSR, + ul_bsr_lcg_report_list{{uint_to_lcg_id(2), 100000}}}; + this->push_bsr(bsr); + } } cell_config_builder_params params; @@ -94,34 +105,65 @@ TEST_F(scheduler_qos_test, when_ue_has_gbr_drb_it_gets_higher_priority) for (unsigned i = 0; i != MAX_NOF_SLOT_RUNS; ++i) { this->run_slot(); + uci_indication uci_ind; + uci_ind.cell_index = to_du_cell_index(0); + uci_ind.slot_rx = this->last_result_slot(); + ul_crc_indication crc_ind; + crc_ind.cell_index = to_du_cell_index(0); + crc_ind.sl_rx = last_result_slot(); + + // Handle DL result. span ue_grants = this->last_sched_res_list[0]->dl.ue_grants; for (const dl_msg_alloc& grant : ue_grants) { ue_stats_map[grant.context.ue_index].dl_bytes_sum += grant.pdsch_cfg.codewords[0].tb_size_bytes; } + // Handle PUCCHs. for (const pucch_info& pucch : this->last_sched_res_list[to_du_cell_index(0)]->ul.pucchs) { if (pucch.format == pucch_format::FORMAT_1 and pucch.format_1.sr_bits != sr_nof_bits::no_sr) { // Skip SRs for this test. continue; } - du_ue_index_t ue_idx = to_du_ue_index((unsigned)pucch.crnti - 0x4601); - uci_indication uci_ind = test_helper::create_uci_indication(this->last_result_slot(), ue_idx, pucch); + du_ue_index_t ue_idx = to_du_ue_index((unsigned)pucch.crnti - 0x4601); + uci_ind.ucis.push_back(test_helper::create_uci_indication_pdu(ue_idx, pucch)); + } + + // Handle UL result. + span ul_grants = this->last_sched_res_list[0]->ul.puschs; + for (const ul_sched_info& grant : ul_grants) { + ue_stats_map[grant.context.ue_index].ul_bytes_sum += grant.pusch_cfg.tb_size_bytes; + crc_ind.crcs.emplace_back(test_helper::create_crc_pdu_indication(grant)); + if (grant.uci.has_value()) { + uci_ind.ucis.push_back( + test_helper::create_uci_indication_pdu(grant.pusch_cfg.rnti, grant.context.ue_index, grant.uci.value())); + } + } + + if (not uci_ind.ucis.empty()) { this->sched->handle_uci_indication(uci_ind); } + if (not crc_ind.crcs.empty()) { + this->sched->handle_crc_indication(crc_ind); + } } double nof_sec_elapsed = (MAX_NOF_SLOT_RUNS / (double)get_nof_slots_per_subframe(params.scs_common)) / 1000; std::vector ue_dl_rate_mbps(ue_stats_map.size(), 0); + std::vector ue_ul_rate_mbps(ue_stats_map.size(), 0); for (unsigned i = 0; i != ue_stats_map.size(); ++i) { ue_dl_rate_mbps[i] = ue_stats_map[i].dl_bytes_sum * 8U * 1e-6 / nof_sec_elapsed; + ue_ul_rate_mbps[i] = ue_stats_map[i].ul_bytes_sum * 8U * 1e-6 / nof_sec_elapsed; } - fmt::memory_buffer fmtbuf; + fmt::memory_buffer dl_fmtbuf; + fmt::memory_buffer ul_fmtbuf; for (unsigned i = 0; i != ue_dl_rate_mbps.size(); ++i) { - fmt::format_to(std::back_inserter(fmtbuf), " {:.3}", ue_dl_rate_mbps[i]); + fmt::format_to(std::back_inserter(dl_fmtbuf), " {:.3}", ue_dl_rate_mbps[i]); + fmt::format_to(std::back_inserter(ul_fmtbuf), " {:.3}", ue_ul_rate_mbps[i]); } - test_logger.info("DL bit rates [Mbps]: [{}]", fmt::to_string(fmtbuf)); + test_logger.info("DL bit rates [Mbps]: [{}]", fmt::to_string(dl_fmtbuf)); + test_logger.info("UL bit rates [Mbps]: [{}]", fmt::to_string(ul_fmtbuf)); const unsigned GBR_UE_INDEX = 0; for (unsigned i = 1; i != ue_stats_map.size(); ++i) { diff --git a/tests/unittests/scheduler/test_utils/indication_generators.cpp b/tests/unittests/scheduler/test_utils/indication_generators.cpp index 388f78dcf9..d4fbc5ba38 100644 --- a/tests/unittests/scheduler/test_utils/indication_generators.cpp +++ b/tests/unittests/scheduler/test_utils/indication_generators.cpp @@ -42,21 +42,17 @@ srsran::test_helper::create_rach_indication(slot_point return rach_ind; } -uci_indication -srsran::test_helper::create_uci_indication(slot_point uci_sl, du_ue_index_t ue_idx, const pucch_info& pucch_pdu) +uci_indication::uci_pdu test_helper::create_uci_indication_pdu(du_ue_index_t ue_idx, const pucch_info& pucch_pdu) { - uci_indication uci_ind{}; - uci_ind.cell_index = to_du_cell_index(0); - uci_ind.slot_rx = uci_sl; - uci_ind.ucis.resize(1); - uci_ind.ucis[0].crnti = pucch_pdu.crnti; - uci_ind.ucis[0].ue_index = ue_idx; + uci_indication::uci_pdu pdu; + pdu.crnti = pucch_pdu.crnti; + pdu.ue_index = ue_idx; switch (pucch_pdu.format) { case pucch_format::FORMAT_1: { uci_indication::uci_pdu::uci_pucch_f0_or_f1_pdu f1{}; f1.harqs.resize(pucch_pdu.format_1.harq_ack_nof_bits); std::fill(f1.harqs.begin(), f1.harqs.end(), mac_harq_ack_report_status::ack); - uci_ind.ucis[0].pdu = f1; + pdu.pdu = f1; } break; case pucch_format::FORMAT_2: { uci_indication::uci_pdu::uci_pucch_f2_or_f3_or_f4_pdu f2{}; @@ -66,10 +62,52 @@ srsran::test_helper::create_uci_indication(slot_point uci_sl, du_ue_index_t ue_i f2.csi.emplace(); f2.csi->first_tb_wideband_cqi = 15; } - uci_ind.ucis[0].pdu = f2; + pdu.pdu = f2; } break; default: report_fatal_error("Unsupported PUCCH format"); } + return pdu; +} + +uci_indication +srsran::test_helper::create_uci_indication(slot_point uci_sl, du_ue_index_t ue_idx, const pucch_info& pucch_pdu) +{ + uci_indication uci_ind{}; + uci_ind.cell_index = to_du_cell_index(0); + uci_ind.slot_rx = uci_sl; + uci_ind.ucis.emplace_back(create_uci_indication_pdu(ue_idx, pucch_pdu)); return uci_ind; } + +uci_indication::uci_pdu +srsran::test_helper::create_uci_indication_pdu(rnti_t rnti, du_ue_index_t ue_idx, const uci_info& uci) +{ + uci_indication::uci_pdu pdu; + pdu.crnti = rnti; + pdu.ue_index = ue_idx; + auto& puschpdu = pdu.pdu.emplace(); + if (uci.csi.has_value()) { + puschpdu.csi.emplace(); + if (uci.csi.value().csi_part1_nof_bits > 0) { + puschpdu.csi.value().first_tb_wideband_cqi = 15; + } + } + if (uci.harq.has_value()) { + puschpdu.harqs.resize(uci.harq.value().harq_ack_nof_bits, mac_harq_ack_report_status::ack); + } + return pdu; +} + +ul_crc_pdu_indication srsran::test_helper::create_crc_pdu_indication(const ul_sched_info& ul_grant) +{ + ul_crc_pdu_indication pdu{}; + + pdu.rnti = ul_grant.pusch_cfg.rnti; + pdu.ue_index = ul_grant.context.ue_index; + pdu.harq_id = ul_grant.pusch_cfg.harq_id; + pdu.tb_crc_success = true; + pdu.ul_sinr_dB = 100; + + return pdu; +} diff --git a/tests/unittests/scheduler/test_utils/indication_generators.h b/tests/unittests/scheduler/test_utils/indication_generators.h index 7cca6575ed..f49d03655e 100644 --- a/tests/unittests/scheduler/test_utils/indication_generators.h +++ b/tests/unittests/scheduler/test_utils/indication_generators.h @@ -10,6 +10,7 @@ #pragma once +#include "srsran/scheduler/result/pusch_info.h" #include "srsran/scheduler/scheduler_configurator.h" #include "srsran/scheduler/scheduler_feedback_handler.h" @@ -28,6 +29,13 @@ rach_indication_message create_rach_indication(slot_point /// Create dummy UCI indication based on a PUCCH PDU. uci_indication create_uci_indication(slot_point uci_sl, du_ue_index_t ue_idx, const pucch_info& pucch_pdu); +uci_indication::uci_pdu create_uci_indication_pdu(du_ue_index_t ue_idx, const pucch_info& pucch_pdu); + +/// Create dummy UCI PDU based on a PUSCH UCI PDU. +uci_indication::uci_pdu create_uci_indication_pdu(rnti_t rnti, du_ue_index_t ue_idx, const uci_info& uci); + +/// Create dummy CRC PDU indication based on a PUSCH PDU. +ul_crc_pdu_indication create_crc_pdu_indication(const ul_sched_info& ul_grant); } // namespace test_helper From 1f41e7c119336cddd8308ba088a4fb653970dbc7 Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 9 Jan 2025 11:28:42 +0100 Subject: [PATCH 087/107] sched: fix UL QoS in the scheduler --- lib/scheduler/policy/scheduler_time_pf.cpp | 42 ++++++++++++++++--- lib/scheduler/policy/scheduler_time_pf.h | 8 +++- .../scheduler/scheduler_qos_test.cpp | 3 +- 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/lib/scheduler/policy/scheduler_time_pf.cpp b/lib/scheduler/policy/scheduler_time_pf.cpp index 30b6d9baeb..82b73bfe4d 100644 --- a/lib/scheduler/policy/scheduler_time_pf.cpp +++ b/lib/scheduler/policy/scheduler_time_pf.cpp @@ -193,10 +193,16 @@ void scheduler_time_pf::ul_sched(ue_pusch_allocator& pusch_alloc, // Clear the existing contents of the queue. ul_queue.clear(); + slot_point pusch_slot = slice_candidate.get_slot_tx(); + unsigned nof_slots_elapsed = last_pusch_slot.valid() ? pusch_slot - last_pusch_slot : 1; + last_pusch_slot = pusch_slot; for (const auto& u : ues) { ue_ctxt& ctxt = ue_history_db[u.ue_index()]; - ctxt.compute_ul_prio( - u, slice_candidate.id(), res_grid.get_pdcch_slot(u.get_pcell().cell_index), slice_candidate.get_slot_tx()); + ctxt.compute_ul_prio(u, + slice_candidate.id(), + res_grid.get_pdcch_slot(u.get_pcell().cell_index), + slice_candidate.get_slot_tx(), + nof_slots_elapsed); ul_queue.push(&ctxt); } @@ -393,12 +399,13 @@ void scheduler_time_pf::ue_ctxt::compute_dl_prio(const slice_ue& u, void scheduler_time_pf::ue_ctxt::compute_ul_prio(const slice_ue& u, ran_slice_id_t slice_id, slot_point pdcch_slot, - slot_point pusch_slot) + slot_point pusch_slot, + unsigned nof_slots_elapsed) { ul_prio = forbid_prio; // Process bytes allocated in previous slot and compute average. - compute_ul_avg_rate(u); + compute_ul_avg_rate(u, nof_slots_elapsed); const ue_cell* ue_cc = u.find_cell(cell_index); if (ue_cc == nullptr) { @@ -553,12 +560,37 @@ void scheduler_time_pf::ue_ctxt::compute_dl_avg_rate(const slice_ue& u, unsigned dl_nof_samples++; } -void scheduler_time_pf::ue_ctxt::compute_ul_avg_rate(const slice_ue& u) +void scheduler_time_pf::ue_ctxt::compute_ul_avg_rate(const slice_ue& u, unsigned nof_slots_elapsed) { // Redimension LCID arrays to the UE configured bearers. ul_alloc_bytes_per_lcg.resize(u.get_lcgs().size(), 0); ul_avg_rate_per_lcg.resize(ul_alloc_bytes_per_lcg.size(), 0); + // In case more than one slot elapsed. + for (unsigned s = 0; s != nof_slots_elapsed - 1; ++s) { + for (unsigned i = 0; i != ul_alloc_bytes_per_lcg.size(); ++i) { + if (not u.contains(uint_to_lcid(i))) { + // Skip LCIDs that are not configured. + ul_alloc_bytes_per_lcg[i] = 0; + ul_avg_rate_per_lcg[i] = 0; + continue; + } + if (ul_nof_samples < 1 / parent->exp_avg_alpha) { + // Fast start before transitioning to exponential average. + ul_avg_rate_per_lcg[i] -= ul_avg_rate_per_lcg[i] / (ul_nof_samples + 1); + } else { + ul_avg_rate_per_lcg[i] -= parent->exp_avg_alpha * ul_avg_rate_per_lcg[i]; + } + } + if (ul_nof_samples < 1 / parent->exp_avg_alpha) { + // Fast start before transitioning to exponential average. + total_ul_avg_rate_ -= total_ul_avg_rate_ / (ul_nof_samples + 1); + } else { + total_ul_avg_rate_ -= parent->exp_avg_alpha * total_ul_avg_rate_; + } + ul_nof_samples++; + } + // Compute UL average rate on a per-logical channel group basis. for (unsigned i = 0; i != ul_alloc_bytes_per_lcg.size(); ++i) { if (not u.contains(uint_to_lcg_id(i))) { diff --git a/lib/scheduler/policy/scheduler_time_pf.h b/lib/scheduler/policy/scheduler_time_pf.h index cc2a50efb5..4058a16794 100644 --- a/lib/scheduler/policy/scheduler_time_pf.h +++ b/lib/scheduler/policy/scheduler_time_pf.h @@ -59,7 +59,11 @@ class scheduler_time_pf : public scheduler_policy slot_point pdsch_slot, unsigned nof_slots_elapsed); /// Computes the priority of the UE to be scheduled in UL based on the proportional fair metric. - void compute_ul_prio(const slice_ue& u, ran_slice_id_t slice_id, slot_point pdcch_slot, slot_point pusch_slot); + void compute_ul_prio(const slice_ue& u, + ran_slice_id_t slice_id, + slot_point pdcch_slot, + slot_point pusch_slot, + unsigned nof_slots_elapsed); void save_dl_alloc(uint32_t total_alloc_bytes, const dl_msg_tb_info& tb_info); void save_ul_alloc(const slice_ue& u, unsigned alloc_bytes); @@ -78,7 +82,7 @@ class scheduler_time_pf : public scheduler_policy private: void compute_dl_avg_rate(const slice_ue& u, unsigned nof_slots_elapsed); - void compute_ul_avg_rate(const slice_ue& u); + void compute_ul_avg_rate(const slice_ue& u, unsigned nof_slots_elapsed); // Sum of DL bytes allocated for a given slot, before it is taken into account in the average rate computation. static_vector dl_alloc_bytes_per_lc; diff --git a/tests/unittests/scheduler/scheduler_qos_test.cpp b/tests/unittests/scheduler/scheduler_qos_test.cpp index faa7d2aaf9..79738d1a80 100644 --- a/tests/unittests/scheduler/scheduler_qos_test.cpp +++ b/tests/unittests/scheduler/scheduler_qos_test.cpp @@ -167,6 +167,7 @@ TEST_F(scheduler_qos_test, when_ue_has_gbr_drb_it_gets_higher_priority) const unsigned GBR_UE_INDEX = 0; for (unsigned i = 1; i != ue_stats_map.size(); ++i) { - ASSERT_GT(ue_dl_rate_mbps[GBR_UE_INDEX], ue_dl_rate_mbps[i]) << "UE GBR rate < UE non-GBR rate"; + ASSERT_GT(ue_dl_rate_mbps[GBR_UE_INDEX], ue_dl_rate_mbps[i]) << "UE DL GBR rate < UE DL non-GBR rate"; + ASSERT_GT(ue_ul_rate_mbps[GBR_UE_INDEX], ue_ul_rate_mbps[i]) << "UE UL GBR rate < UE UL non-GBR rate"; } } From 8fa3a2acd5a86c812c24b7afcb2904853c065a6e Mon Sep 17 00:00:00 2001 From: frankist Date: Thu, 9 Jan 2025 12:11:31 +0100 Subject: [PATCH 088/107] sched: code format --- tests/unittests/scheduler/test_utils/indication_generators.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/unittests/scheduler/test_utils/indication_generators.h b/tests/unittests/scheduler/test_utils/indication_generators.h index f49d03655e..b044b57d7b 100644 --- a/tests/unittests/scheduler/test_utils/indication_generators.h +++ b/tests/unittests/scheduler/test_utils/indication_generators.h @@ -28,7 +28,7 @@ rach_indication_message create_rach_indication(slot_point const std::vector& preambles); /// Create dummy UCI indication based on a PUCCH PDU. -uci_indication create_uci_indication(slot_point uci_sl, du_ue_index_t ue_idx, const pucch_info& pucch_pdu); +uci_indication create_uci_indication(slot_point uci_sl, du_ue_index_t ue_idx, const pucch_info& pucch_pdu); uci_indication::uci_pdu create_uci_indication_pdu(du_ue_index_t ue_idx, const pucch_info& pucch_pdu); /// Create dummy UCI PDU based on a PUSCH UCI PDU. From 508617a71ae1d31e30983b7d802e62a758aaadc4 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Mon, 16 Dec 2024 19:08:20 +0100 Subject: [PATCH 089/107] phy: refactor downlink processor phy: review downlink processor documentation phy: review downlink processor documentation phy: review downlink processor related phy: review downlink processor --- .../gateways/baseband/baseband_gateway_base.h | 4 +- .../ethernet/dpdk/dpdk_ethernet_rx_buffer.h | 2 +- include/srsran/phy/upper/downlink_processor.h | 154 +++++++++++---- include/srsran/phy/upper/unique_rx_buffer.h | 2 +- .../srsran/phy/upper/upper_phy_factories.h | 6 +- include/srsran/radio/radio_base.h | 4 +- .../ran/precoding/precoding_weight_matrix.h | 2 +- .../phy/fapi_to_phy_translator.cpp | 26 +-- lib/fapi_adaptor/phy/fapi_to_phy_translator.h | 9 +- .../upper/downlink_processor_pool_impl.cpp | 6 +- lib/phy/upper/downlink_processor_pool_impl.h | 6 +- ...ownlink_processor_single_executor_impl.cpp | 10 +- .../downlink_processor_single_executor_impl.h | 13 +- lib/phy/upper/upper_phy_factories.cpp | 8 +- .../phy/fapi_to_phy_translator_test.cpp | 2 +- .../upper/downlink_processor_pool_test.cpp | 70 +++---- .../phy/upper/downlink_processor_test.cpp | 176 ++++-------------- .../upper/downlink_processor_test_doubles.h | 35 ++-- 18 files changed, 261 insertions(+), 274 deletions(-) diff --git a/include/srsran/gateways/baseband/baseband_gateway_base.h b/include/srsran/gateways/baseband/baseband_gateway_base.h index b62a588118..90911467dc 100644 --- a/include/srsran/gateways/baseband/baseband_gateway_base.h +++ b/include/srsran/gateways/baseband/baseband_gateway_base.h @@ -29,10 +29,10 @@ class baseband_gateway_base /// Forbid move constructor. baseband_gateway_base(const baseband_gateway_base&& other) = delete; - /// Forbid copy assigment operator. + /// Forbid copy assignment operator. baseband_gateway_base& operator=(const baseband_gateway_base&) = delete; - /// Forbid move assigment operator. + /// Forbid move assignment operator. baseband_gateway_base& operator=(baseband_gateway_base&&) = delete; }; diff --git a/include/srsran/ofh/ethernet/dpdk/dpdk_ethernet_rx_buffer.h b/include/srsran/ofh/ethernet/dpdk/dpdk_ethernet_rx_buffer.h index b580f0521f..0e44edac0a 100644 --- a/include/srsran/ofh/ethernet/dpdk/dpdk_ethernet_rx_buffer.h +++ b/include/srsran/ofh/ethernet/dpdk/dpdk_ethernet_rx_buffer.h @@ -36,7 +36,7 @@ class dpdk_rx_buffer_impl : public rx_buffer other.mbuf = nullptr; }; - /// Move assigment operator. + /// Move assignment operator. dpdk_rx_buffer_impl& operator=(dpdk_rx_buffer_impl&& other) noexcept { // Free stored mbuf. diff --git a/include/srsran/phy/upper/downlink_processor.h b/include/srsran/phy/upper/downlink_processor.h index 8266032180..5b2baca27c 100644 --- a/include/srsran/phy/upper/downlink_processor.h +++ b/include/srsran/phy/upper/downlink_processor.h @@ -10,6 +10,21 @@ #pragma once +/// \file +/// \brief Downlink processor classes. +/// +/// The downlink processor handles all the given downlink PDUs and sends the configured resource grid through a gateway +/// when every PDU for the given slot has finished processing. +/// +/// The slot context processing starts with the downlink processor controller interface. The downlink processor slot +/// context is configured once using \ref downlink_processor_controller::configure_resource_grid() which returns a +/// unique downlink processor. +/// +/// The unique downlink processor will accept downlink transmissions as the object is available in a scope. When the +/// unique downlink processor is released or deleted, the downlink processor controller will not accept configuring a +/// new slot context until all the transmissions are completed and the resource grid is sent over the gateway. +/// + #include "srsran/phy/upper/channel_processors/pdcch/pdcch_processor.h" #include "srsran/phy/upper/channel_processors/pdsch/pdsch_processor.h" #include "srsran/phy/upper/channel_processors/ssb_processor.h" @@ -20,17 +35,7 @@ namespace srsran { struct resource_grid_context; class shared_resource_grid; -/// \brief Downlink processor class that groups and process all the downlink channels within a slot. -/// -/// The downlink processor process all the given downlink PDUs and sends the configured resource grid through a gateway -/// when every PDU has finished processing and the finish_processing_pdus() has been called. Prior to start processing -/// PDUs, configure_resource_grid() must be called in order to configure the resource grid and the context for the -/// downlink processor. -/// -/// \note -/// Undefined behavior can be caused by: -/// - Calling any process method prior to configure_resource_grid(). -/// - Calling finish_processing_pdus() without calling configure_resource_grid() in a slot context. +/// Downlink processor interface that groups and processes all the downlink channels within a slot. class downlink_processor { public: @@ -50,33 +55,118 @@ class downlink_processor /// \brief Process the given SSB PDU. /// - /// \param[in] pdu SSB PDU to pr + /// \param[in] pdu SS/PBCH block PDU to process. virtual void process_ssb(const ssb_processor::pdu_t& pdu) = 0; /// \brief Process the given NZP-CSI-RS configuration. /// /// \param[in] config NZP-CSI-RS configuration to process. virtual void process_nzp_csi_rs(const nzp_csi_rs_generator::config_t& config) = 0; +}; - /// \brief Configures the resource grid of the downlink_processor. - /// - /// \param[in] context Resource grid context that contains the information of the processing slot. - /// \param[in] grid Resource grid that will contain the data of the processed downlink channels. - /// \return \c true if the resource grid is successfully configured, \c false otherwise. - /// - /// \note - /// - Calling this method is mandatory before processing any PDU. - /// - The resource grid number of ports and bandwidth must be sufficient to accommodate all the PDUs. - // :TODO: move this method to other interface to avoid controlling the order of the methods execution. - virtual bool configure_resource_grid(const resource_grid_context& context, shared_resource_grid grid) = 0; +/// \brief Unique downlink processor. +/// +/// Keeps the downlink processor interface available for processing downlink transmissions as long as it is available in +/// a scope. +/// +/// The downlink processor closes the window for accepting new transmissions when either the unique downlink processor +/// is destroyed or released. +class unique_downlink_processor +{ +public: + /// Downlink processor underlying interface. + class downlink_processor_callback : public downlink_processor + { + public: + /// Default destructor. + virtual ~downlink_processor_callback() = default; + + /// \brief Stops accepting PDUs. + /// + /// When this method is called, the interface will not expect to process more PDUs, so once it finishes to process + /// all the enqueued PDUs, the resource grid will be sent to the lower bound gateway using the \c context from + /// configure_resource_grid() to provide the processing context of the resource grid in the lower physical layer. + virtual void finish_processing_pdus() = 0; + }; + + /// Default constructor - creates an invalid downlink processor. + unique_downlink_processor() = default; + + /// Builds a unique downlink processor from an underlying instance. + explicit unique_downlink_processor(downlink_processor_callback& processor_) : processor(&processor_) {} + + /// Disabled copy constructor. + unique_downlink_processor(const unique_downlink_processor&) = delete; + + /// Disabled copy assignment operator. + unique_downlink_processor& operator=(const unique_downlink_processor& other) = delete; + + /// Move constructor. + unique_downlink_processor(unique_downlink_processor&& other) noexcept + { + release(); + processor = other.processor; + other.processor = nullptr; + } + + /// Move assignment operator. + unique_downlink_processor& operator=(unique_downlink_processor&& other) noexcept + { + release(); + processor = other.processor; + other.processor = nullptr; + return *this; + } + + /// Returns True if the unique processor is valid, false otherwise. + bool is_valid() const { return processor != nullptr; } + + /// Default destructor - notifies the end of processing PDUs. + ~unique_downlink_processor() { release(); } + + /// Releases the unique processor. + void release() + { + if (is_valid()) { + processor->finish_processing_pdus(); + } + processor = nullptr; + } + + /// Gets the underlying downlink processor. + downlink_processor& get() + { + report_fatal_error_if_not(is_valid(), "Invalid processor."); + return *processor; + } + + /// Gets the underlying downlink processor. + downlink_processor* operator->() + { + report_fatal_error_if_not(is_valid(), "Invalid processor."); + return processor; + } + +private: + /// Reference to the underlying downlink processor. Set to \c nullptr for an invalid processor. + downlink_processor_callback* processor = nullptr; +}; + +/// Downlink processor controller. +class downlink_processor_controller +{ +public: + /// Default destructor. + virtual ~downlink_processor_controller() = default; - /// \brief Stops accepting PDUs. + /// \brief Configures the downlink processor with a slot context and resource grid. /// - /// When this method is called, the interface will not expect to process more PDUs, so once it finishes to process all - /// the enqueued PDUs, the resource grid will be sent to the lower bound gateway using the \c context from - /// configure_resource_grid() to provide the processing context of the resource grid in the lower physical layer. - // :TODO: move this method to other interface to avoid controlling the order of the methods execution. - virtual void finish_processing_pdus() = 0; + /// \param[in] context Slot processing context information. + /// \param[in] grid Resource grid associated to the slot. + /// \return A valid unique downlink processor if the downlink processor controller. Otherwise, an invalid unique + /// downlink processor. + virtual unique_downlink_processor configure_resource_grid(const resource_grid_context& context, + shared_resource_grid grid) = 0; }; /// Downlink processor validation interface. @@ -86,7 +176,7 @@ class downlink_pdu_validator /// Default destructor. virtual ~downlink_pdu_validator() = default; - /// \brief Validates SSB processor configuration parameters. + /// \brief Validates the SS/PBCH block processor configuration parameters. /// \return True if the parameters contained in \c pdu are supported, false otherwise. virtual bool is_valid(const ssb_processor::pdu_t& pdu) const = 0; @@ -109,12 +199,12 @@ class downlink_processor_pool public: virtual ~downlink_processor_pool() = default; - /// \brief Returns a downlink processor with the given slot and sector. + /// \brief Returns a downlink processor controller with the given slot and sector. /// /// \param slot Slot point. /// \param sector_id Sector ID. /// \return A downlink processor. - virtual downlink_processor& get_processor(slot_point slot, unsigned sector_id) = 0; + virtual downlink_processor_controller& get_processor_controller(slot_point slot, unsigned sector_id) = 0; }; } // namespace srsran diff --git a/include/srsran/phy/upper/unique_rx_buffer.h b/include/srsran/phy/upper/unique_rx_buffer.h index 05a9a88ab4..a1be274572 100644 --- a/include/srsran/phy/upper/unique_rx_buffer.h +++ b/include/srsran/phy/upper/unique_rx_buffer.h @@ -69,7 +69,7 @@ class unique_rx_buffer other.ptr = nullptr; }; - /// Move assigment operator. + /// Move assignment operator. unique_rx_buffer& operator=(unique_rx_buffer&& other) noexcept { // Unlock current soft buffer if it is actually not unlocked. diff --git a/include/srsran/phy/upper/upper_phy_factories.h b/include/srsran/phy/upper/upper_phy_factories.h index 86b3a30e94..b27aa8d1b4 100644 --- a/include/srsran/phy/upper/upper_phy_factories.h +++ b/include/srsran/phy/upper/upper_phy_factories.h @@ -100,10 +100,10 @@ class downlink_processor_factory virtual ~downlink_processor_factory() = default; /// \brief Creates a downlink processor. - virtual std::unique_ptr create(const downlink_processor_config& config) = 0; + virtual std::unique_ptr create(const downlink_processor_config& config) = 0; /// \brief Creates a downlink processor with logging capabilities. - virtual std::unique_ptr + virtual std::unique_ptr create(const downlink_processor_config& config, srslog::basic_logger& logger, bool enable_broadcast) = 0; /// \brief Creates a downlink PDU validator. @@ -188,7 +188,7 @@ struct downlink_processor_pool_config { /// Subcarrier spacing. subcarrier_spacing scs; /// Pointers to the actual downlink processors. - std::vector> procs; + std::vector> procs; }; /// Collection of all downlink processors, organized by radio sector and numerology. diff --git a/include/srsran/radio/radio_base.h b/include/srsran/radio/radio_base.h index 0d42dca30b..88ef39b63a 100644 --- a/include/srsran/radio/radio_base.h +++ b/include/srsran/radio/radio_base.h @@ -29,10 +29,10 @@ class radio_base /// Forbid move constructor. radio_base(const radio_base&& other) = delete; - /// Forbid copy assigment operator. + /// Forbid copy assignment operator. radio_base& operator=(const radio_base&) = delete; - /// Forbid move assigment operator. + /// Forbid move assignment operator. radio_base& operator=(radio_base&&) = delete; }; } // namespace srsran diff --git a/include/srsran/ran/precoding/precoding_weight_matrix.h b/include/srsran/ran/precoding/precoding_weight_matrix.h index cbd7ef6950..6a9fdeb4b4 100644 --- a/include/srsran/ran/precoding/precoding_weight_matrix.h +++ b/include/srsran/ran/precoding/precoding_weight_matrix.h @@ -108,7 +108,7 @@ class precoding_weight_matrix other.data.get_view(dims::all)>({})); } - /// \brief Overload assigment operator. + /// \brief Overload assignment operator. /// \param[in] other Precoding weight matrix to copy. precoding_weight_matrix& operator=(const precoding_weight_matrix& other) { diff --git a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp index 5bbe8afdf1..16e49c6c8d 100644 --- a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp +++ b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp @@ -33,7 +33,7 @@ using namespace fapi_adaptor; namespace { -class downlink_processor_dummy : public downlink_processor +class downlink_processor_dummy : public unique_downlink_processor::downlink_processor_callback { public: void process_pdcch(const pdcch_processor::pdu_t& pdu) override @@ -53,10 +53,6 @@ class downlink_processor_dummy : public downlink_processor { srslog::fetch_basic_logger("FAPI").warning("Could not enqueue NZP-CSI-RS PDU in the downlink processor"); } - bool configure_resource_grid(const resource_grid_context& context, shared_resource_grid grid) override - { - return true; - } void finish_processing_pdus() override {} }; @@ -104,17 +100,12 @@ fapi_to_phy_translator::fapi_to_phy_translator(const fapi_to_phy_translator_conf srsran_assert(!prach_ports.empty(), "The PRACH ports must not be empty."); } -fapi_to_phy_translator::slot_based_upper_phy_controller::slot_based_upper_phy_controller() : - dl_processor(dummy_dl_processor) -{ -} - fapi_to_phy_translator::slot_based_upper_phy_controller::slot_based_upper_phy_controller( downlink_processor_pool& dl_processor_pool, resource_grid_pool& rg_pool, slot_point slot_, unsigned sector_id) : - slot(slot_), dl_processor(dl_processor_pool.get_processor(slot_, 0)) + slot(slot_) { resource_grid_context context = {slot_, sector_id}; // Grab the resource grid. @@ -122,16 +113,19 @@ fapi_to_phy_translator::slot_based_upper_phy_controller::slot_based_upper_phy_co // If the resource grid is not valid, all DL transmissions for this slot shall be discarded. if (!grid) { - dl_processor = dummy_dl_processor; + dl_processor = unique_downlink_processor(dummy_dl_processor); return; } + // Obtain a downlink processor controller associated with the given slot and sector. + downlink_processor_controller& dl_proc_controller = dl_processor_pool.get_processor_controller(slot, sector_id); + // Configure the downlink processor. - bool success = dl_processor.get().configure_resource_grid(context, std::move(grid)); + dl_processor = dl_proc_controller.configure_resource_grid(context, std::move(grid)); // Swap the DL processor with a dummy if it failed to configure the resource grid. - if (!success) { - dl_processor = dummy_dl_processor; + if (!dl_processor.is_valid()) { + dl_processor = unique_downlink_processor(dummy_dl_processor); } } @@ -147,7 +141,7 @@ fapi_to_phy_translator::slot_based_upper_phy_controller::operator=( fapi_to_phy_translator::slot_based_upper_phy_controller::~slot_based_upper_phy_controller() { - dl_processor.get().finish_processing_pdus(); + dl_processor = unique_downlink_processor(dummy_dl_processor); } namespace { diff --git a/lib/fapi_adaptor/phy/fapi_to_phy_translator.h b/lib/fapi_adaptor/phy/fapi_to_phy_translator.h index 5b1ff88072..bebe61543f 100644 --- a/lib/fapi_adaptor/phy/fapi_to_phy_translator.h +++ b/lib/fapi_adaptor/phy/fapi_to_phy_translator.h @@ -16,6 +16,7 @@ #include "srsran/fapi_adaptor/precoding_matrix_repository.h" #include "srsran/fapi_adaptor/uci_part2_correspondence_repository.h" #include "srsran/phy/upper/channel_processors/pdsch/pdsch_processor.h" +#include "srsran/phy/upper/downlink_processor.h" #include "srsran/srslog/logger.h" #include "srsran/support/executors/task_executor.h" #include @@ -99,11 +100,11 @@ class fapi_to_phy_translator : public fapi::slot_message_gateway /// \note The lifetime of any instantiation of this class is meant to be a single slot. class slot_based_upper_phy_controller { - slot_point slot; - std::reference_wrapper dl_processor; + slot_point slot; + unique_downlink_processor dl_processor; public: - slot_based_upper_phy_controller(); + slot_based_upper_phy_controller() = default; slot_based_upper_phy_controller(downlink_processor_pool& dl_processor_pool, resource_grid_pool& rg_pool, @@ -118,8 +119,6 @@ class fapi_to_phy_translator : public fapi::slot_message_gateway /// Overloaded member of pointer operator. downlink_processor* operator->() { return &dl_processor.get(); } - /// Overloaded member of pointer operator. - const downlink_processor* operator->() const { return &dl_processor.get(); } }; /// Manages slot based controllers. diff --git a/lib/phy/upper/downlink_processor_pool_impl.cpp b/lib/phy/upper/downlink_processor_pool_impl.cpp index 1f943e048a..32c1afb1dc 100644 --- a/lib/phy/upper/downlink_processor_pool_impl.cpp +++ b/lib/phy/upper/downlink_processor_pool_impl.cpp @@ -22,11 +22,11 @@ downlink_processor_pool_impl::downlink_processor_pool_impl(downlink_processor_po } } -downlink_processor& downlink_processor_pool_impl::get_processor(slot_point slot, unsigned sector_id) +downlink_processor_controller& downlink_processor_pool_impl::get_processor_controller(slot_point slot, + unsigned sector_id) { srsran_assert(sector_id < processors.size(), "Invalid sector ({}) when requesting a downlink processor", sector_id); srsran_assert(slot.valid(), "Invalid slot ({}) when requesting a downlink processor", slot); - downlink_processor& proc = processors[sector_id].get_processor(slot); - return proc; + return processors[sector_id].get_processor(slot); } diff --git a/lib/phy/upper/downlink_processor_pool_impl.h b/lib/phy/upper/downlink_processor_pool_impl.h index 94efa0b8f5..4999e5b1ae 100644 --- a/lib/phy/upper/downlink_processor_pool_impl.h +++ b/lib/phy/upper/downlink_processor_pool_impl.h @@ -24,7 +24,7 @@ struct downlink_processor_pool_impl_config { /// Subcarrier spacing. subcarrier_spacing scs; /// Pointers to the actual downlink processors. - std::vector> procs; + std::vector> procs; }; /// Collection of all downlink processors, organized by radio sector and numerology. @@ -43,11 +43,11 @@ class downlink_processor_pool_impl : public downlink_processor_pool explicit downlink_processor_pool_impl(downlink_processor_pool_impl_config dl_processors); // See interface for documentation. - downlink_processor& get_processor(slot_point slot, unsigned sector_id) override; + downlink_processor_controller& get_processor_controller(slot_point slot, unsigned sector_id) override; private: /// Container for downlink processors. Each entry belongs to a different sector. - std::vector> processors; + std::vector> processors; }; } // namespace srsran diff --git a/lib/phy/upper/downlink_processor_single_executor_impl.cpp b/lib/phy/upper/downlink_processor_single_executor_impl.cpp index ae3fcdf82f..7e22e8c3a6 100644 --- a/lib/phy/upper/downlink_processor_single_executor_impl.cpp +++ b/lib/phy/upper/downlink_processor_single_executor_impl.cpp @@ -15,6 +15,7 @@ #include "srsran/phy/upper/channel_processors/pdsch/formatters.h" #include "srsran/phy/upper/signal_processors/signal_processor_formatters.h" #include "srsran/phy/upper/upper_phy_rg_gateway.h" +#include "srsran/srslog/srslog.h" #include "srsran/support/executors/task_executor.h" #include "srsran/support/srsran_assert.h" @@ -225,14 +226,15 @@ void downlink_processor_single_executor_impl::process_nzp_csi_rs(const nzp_csi_r } } -bool downlink_processor_single_executor_impl::configure_resource_grid(const resource_grid_context& context, - shared_resource_grid grid) +unique_downlink_processor +downlink_processor_single_executor_impl::configure_resource_grid(const resource_grid_context& context, + shared_resource_grid grid) { std::lock_guard lock(mutex); // Don't configure the grid if the DL processor is not available. if (!state.is_idle()) { - return false; + return unique_downlink_processor(); } report_fatal_error_if_not(!current_grid, "A previously configured resource grid is still in use."); @@ -251,7 +253,7 @@ bool downlink_processor_single_executor_impl::configure_resource_grid(const reso l1_tracer << instant_trace_event("configure_rg", instant_trace_event::cpu_scope::global); - return true; + return unique_downlink_processor(*this); } void srsran::downlink_processor_single_executor_impl::finish_processing_pdus() diff --git a/lib/phy/upper/downlink_processor_single_executor_impl.h b/lib/phy/upper/downlink_processor_single_executor_impl.h index c958c4ebd9..6f3f202735 100644 --- a/lib/phy/upper/downlink_processor_single_executor_impl.h +++ b/lib/phy/upper/downlink_processor_single_executor_impl.h @@ -53,7 +53,9 @@ class downlink_processor_callback /// the gateway as soon as every enqueued PDU before finish_processing_pdus() is processed . This is controlled counting /// the PDUs that are processed and finished processing. /// \note Thread safe class. -class downlink_processor_single_executor_impl : public downlink_processor, private detail::downlink_processor_callback +class downlink_processor_single_executor_impl : public downlink_processor_controller, + private unique_downlink_processor::downlink_processor_callback, + private detail::downlink_processor_callback { public: /// \brief Builds a downlink processor single executor impl object with the given parameters. @@ -73,6 +75,11 @@ class downlink_processor_single_executor_impl : public downlink_processor, priva task_executor& executor_, srslog::basic_logger& logger_); + // See interface for documentation. + unique_downlink_processor configure_resource_grid(const resource_grid_context& context, + shared_resource_grid grid) override; + +private: // See interface for documentation. void process_pdcch(const pdcch_processor::pdu_t& pdu) override; @@ -86,13 +93,9 @@ class downlink_processor_single_executor_impl : public downlink_processor, priva // See interface for documentation. void process_nzp_csi_rs(const nzp_csi_rs_generator::config_t& config) override; - // See interface for documentation. - bool configure_resource_grid(const resource_grid_context& context, shared_resource_grid grid) override; - // See interface for documentation. void finish_processing_pdus() override; -private: class pdsch_processor_notifier_wrapper : public pdsch_processor_notifier { public: diff --git a/lib/phy/upper/upper_phy_factories.cpp b/lib/phy/upper/upper_phy_factories.cpp index 72e5926043..8d54daa5a7 100644 --- a/lib/phy/upper/upper_phy_factories.cpp +++ b/lib/phy/upper/upper_phy_factories.cpp @@ -177,7 +177,7 @@ class downlink_processor_single_executor_factory : public downlink_processor_fac } // See interface for documentation. - std::unique_ptr create(const downlink_processor_config& config) override + std::unique_ptr create(const downlink_processor_config& config) override { std::unique_ptr pdcch = pdcch_proc_factory->create(); report_fatal_error_if_not(pdcch, "Invalid PDCCH processor."); @@ -201,7 +201,7 @@ class downlink_processor_single_executor_factory : public downlink_processor_fac } // See interface for documentation. - std::unique_ptr + std::unique_ptr create(const downlink_processor_config& config, srslog::basic_logger& logger, bool enable_broadcast) override { std::unique_ptr pdcch = pdcch_proc_factory->create(logger, enable_broadcast); @@ -226,7 +226,7 @@ class downlink_processor_single_executor_factory : public downlink_processor_fac } report_fatal_error_if_not(nzp_csi, "Invalid NZP-CSI-RS generator."); - std::unique_ptr downlink_proc = + std::unique_ptr downlink_proc = std::make_unique(*config.gateway, std::move(pdcch), std::move(pdsch), @@ -281,7 +281,7 @@ create_downlink_processor_pool(std::shared_ptr facto // Assign an executor to each DL processor from the list in round-robin fashion. processor_config.executor = config.dl_executors[i_proc % config.dl_executors.size()]; - std::unique_ptr dl_proc; + std::unique_ptr dl_proc; if (config.log_level == srslog::basic_levels::none) { dl_proc = factory->create(processor_config); } else { diff --git a/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp b/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp index e79e4a59e3..fe8d178a25 100644 --- a/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp +++ b/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp @@ -139,7 +139,7 @@ class downlink_processor_pool_dummy : public downlink_processor_pool dl_processor.emplace_back(1); } - downlink_processor& get_processor(slot_point slot, unsigned sector_id) override + downlink_processor_controller& get_processor_controller(slot_point slot, unsigned sector_id) override { return dl_processor[slot.slot_index()]; } diff --git a/tests/unittests/phy/upper/downlink_processor_pool_test.cpp b/tests/unittests/phy/upper/downlink_processor_pool_test.cpp index c7ba0e0b66..c4fe929e62 100644 --- a/tests/unittests/phy/upper/downlink_processor_pool_test.cpp +++ b/tests/unittests/phy/upper/downlink_processor_pool_test.cpp @@ -28,8 +28,8 @@ static void test_dl_processor_ok() std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); - slot_point slot(to_numerology_value(scs), 0, 0); - downlink_processor& dl_processor = dl_proc_pool->get_processor(slot, id); + slot_point slot(to_numerology_value(scs), 0, 0); + downlink_processor_controller& dl_processor = dl_proc_pool->get_processor_controller(slot, id); TESTASSERT_EQ(static_cast(dl_processor).get_id(), id); } @@ -48,9 +48,9 @@ void test_pass_same_slot_gets_same_processor() std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); - slot_point slot(to_numerology_value(scs), 0, 0); - downlink_processor& dl_processor_0 = dl_proc_pool->get_processor(slot, sector); - downlink_processor& dl_processor_1 = dl_proc_pool->get_processor(slot, sector); + slot_point slot(to_numerology_value(scs), 0, 0); + downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(slot, sector); + downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(slot, sector); TESTASSERT_EQ(static_cast(dl_processor_0).get_id(), id); TESTASSERT_EQ(static_cast(dl_processor_1).get_id(), id); @@ -71,10 +71,10 @@ static void test_consecutive_dl_processor_ok() std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); - slot_point first(to_numerology_value(scs), 0, 0); - downlink_processor& dl_processor_0 = dl_proc_pool->get_processor(first, sector); - slot_point second(to_numerology_value(scs), 0, 1); - downlink_processor& dl_processor_1 = dl_proc_pool->get_processor(second, sector); + slot_point first(to_numerology_value(scs), 0, 0); + downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(first, sector); + slot_point second(to_numerology_value(scs), 0, 1); + downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(second, sector); TESTASSERT_EQ(static_cast(dl_processor_0).get_id(), 0); TESTASSERT_EQ(static_cast(dl_processor_1).get_id(), 1); @@ -94,14 +94,14 @@ static void test_2sectors_2dl_processor_ok() std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); - slot_point first(to_numerology_value(scs), 0, 0); - downlink_processor& dl_processor_00 = dl_proc_pool->get_processor(first, 0); - slot_point second(to_numerology_value(scs), 0, 1); - downlink_processor& dl_processor_01 = dl_proc_pool->get_processor(second, 0); - slot_point third(to_numerology_value(scs), 0, 0); - downlink_processor& dl_processor_10 = dl_proc_pool->get_processor(third, 1); - slot_point fourth(to_numerology_value(scs), 0, 1); - downlink_processor& dl_processor_11 = dl_proc_pool->get_processor(fourth, 1); + slot_point first(to_numerology_value(scs), 0, 0); + downlink_processor_controller& dl_processor_00 = dl_proc_pool->get_processor_controller(first, 0); + slot_point second(to_numerology_value(scs), 0, 1); + downlink_processor_controller& dl_processor_01 = dl_proc_pool->get_processor_controller(second, 0); + slot_point third(to_numerology_value(scs), 0, 0); + downlink_processor_controller& dl_processor_10 = dl_proc_pool->get_processor_controller(third, 1); + slot_point fourth(to_numerology_value(scs), 0, 1); + downlink_processor_controller& dl_processor_11 = dl_proc_pool->get_processor_controller(fourth, 1); TESTASSERT_EQ(static_cast(dl_processor_00).get_id(), 0); TESTASSERT_EQ(static_cast(dl_processor_01).get_id(), 1); @@ -122,12 +122,12 @@ static void test_circular_buffer_ok() std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); - slot_point first(to_numerology_value(scs), 0, 0); - downlink_processor& dl_processor_0 = dl_proc_pool->get_processor(first, sector); - slot_point second(to_numerology_value(scs), 0, 1); - downlink_processor& dl_processor_1 = dl_proc_pool->get_processor(second, sector); - slot_point third(to_numerology_value(scs), 0, 2); - downlink_processor& dl_processor_2 = dl_proc_pool->get_processor(third, sector); + slot_point first(to_numerology_value(scs), 0, 0); + downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(first, sector); + slot_point second(to_numerology_value(scs), 0, 1); + downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(second, sector); + slot_point third(to_numerology_value(scs), 0, 2); + downlink_processor_controller& dl_processor_2 = dl_proc_pool->get_processor_controller(third, sector); TESTASSERT_EQ(static_cast(dl_processor_0).get_id(), 0); TESTASSERT_EQ(static_cast(dl_processor_1).get_id(), 1); @@ -149,18 +149,18 @@ static void test_2sectors_2numerologies_2dl_processor_ok() std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); - slot_point first(1, 0, 0); - downlink_processor& dl_processor_010 = dl_proc_pool->get_processor(first, 0); - slot_point second(1, 0, 1); - downlink_processor& dl_processor_011 = dl_proc_pool->get_processor(second, 0); - slot_point third(3, 0, 0); - downlink_processor& dl_processor_030 = dl_proc_pool->get_processor(third, 0); - slot_point fourth(3, 0, 1); - downlink_processor& dl_processor_031 = dl_proc_pool->get_processor(fourth, 0); - downlink_processor& dl_processor_110 = dl_proc_pool->get_processor(first, 1); - downlink_processor& dl_processor_111 = dl_proc_pool->get_processor(second, 1); - downlink_processor& dl_processor_130 = dl_proc_pool->get_processor(third, 1); - downlink_processor& dl_processor_131 = dl_proc_pool->get_processor(fourth, 1); + slot_point first(1, 0, 0); + downlink_processor_controller& dl_processor_010 = dl_proc_pool->get_processor_controller(first, 0); + slot_point second(1, 0, 1); + downlink_processor_controller& dl_processor_011 = dl_proc_pool->get_processor_controller(second, 0); + slot_point third(3, 0, 0); + downlink_processor_controller& dl_processor_030 = dl_proc_pool->get_processor_controller(third, 0); + slot_point fourth(3, 0, 1); + downlink_processor_controller& dl_processor_031 = dl_proc_pool->get_processor_controller(fourth, 0); + downlink_processor_controller& dl_processor_110 = dl_proc_pool->get_processor_controller(first, 1); + downlink_processor_controller& dl_processor_111 = dl_proc_pool->get_processor_controller(second, 1); + downlink_processor_controller& dl_processor_130 = dl_proc_pool->get_processor_controller(third, 1); + downlink_processor_controller& dl_processor_131 = dl_proc_pool->get_processor_controller(fourth, 1); TESTASSERT_EQ(static_cast(dl_processor_010).get_id(), 10); TESTASSERT_EQ(static_cast(dl_processor_011).get_id(), 11); diff --git a/tests/unittests/phy/upper/downlink_processor_test.cpp b/tests/unittests/phy/upper/downlink_processor_test.cpp index acca5a5a49..fb2978e577 100644 --- a/tests/unittests/phy/upper/downlink_processor_test.cpp +++ b/tests/unittests/phy/upper/downlink_processor_test.cpp @@ -48,17 +48,19 @@ TEST(downlinkProcessorTest, worksInOrder) ssb_processor_spy& ssb_ref = *ssb_processor; csi_rs_processor_spy& csi_rs_ref = *csi_rs_processor; - auto dl_processor = std::make_unique(gw, - std::move(pdcch_processor), - std::move(pdsch_processor), - std::move(ssb_processor), - std::move(csi_rs_processor), - executor, - logger); + std::unique_ptr dl_proc_control = + std::make_unique(gw, + std::move(pdcch_processor), + std::move(pdsch_processor), + std::move(ssb_processor), + std::move(csi_rs_processor), + executor, + logger); slot_point slot(1, 2, 1); unsigned sector = 0; - dl_processor->configure_resource_grid({slot, sector}, get_dummy_grid()); + unique_downlink_processor dl_processor = dl_proc_control->configure_resource_grid({slot, sector}, get_dummy_grid()); + ASSERT_TRUE(dl_processor.is_valid()); ASSERT_FALSE(pdcch_ref.is_process_called()); ASSERT_FALSE(pdsch_ref.is_process_called()); @@ -83,7 +85,7 @@ TEST(downlinkProcessorTest, worksInOrder) ASSERT_FALSE(gw.sent); - dl_processor->finish_processing_pdus(); + dl_processor.release(); ASSERT_TRUE(gw.sent); } @@ -103,18 +105,20 @@ TEST(downlinkProcessorTest, finishIsCalledBeforeProcessingPdus) ssb_processor_spy& ssb_ref = *ssb_processor; csi_rs_processor_spy& csi_rs_ref = *csi_rs_processor; - auto dl_processor = std::make_unique(gw, - std::move(pdcch_processor), - std::move(pdsch_processor), - std::move(ssb_processor), - std::move(csi_rs_processor), - executor, - logger); + std::unique_ptr dl_proc_control = + std::make_unique(gw, + std::move(pdcch_processor), + std::move(pdsch_processor), + std::move(ssb_processor), + std::move(csi_rs_processor), + executor, + logger); slot_point slot(1, 2, 1); unsigned sector = 0; - dl_processor->configure_resource_grid({slot, sector}, get_dummy_grid()); + unique_downlink_processor dl_processor = dl_proc_control->configure_resource_grid({slot, sector}, get_dummy_grid()); + ASSERT_TRUE(dl_processor.is_valid()); dl_processor->process_ssb({}); pdcch_processor::pdu_t pdu; @@ -130,7 +134,7 @@ TEST(downlinkProcessorTest, finishIsCalledBeforeProcessingPdus) ASSERT_FALSE(csi_rs_ref.is_map_called()); ASSERT_FALSE(gw.sent); - dl_processor->finish_processing_pdus(); + dl_processor.release(); ASSERT_FALSE(gw.sent); // Run all the queued tasks. @@ -144,124 +148,12 @@ TEST(downlinkProcessorTest, finishIsCalledBeforeProcessingPdus) ASSERT_TRUE(gw.sent); } -TEST(downlinkProcessorTest, processPduAfterFinishProcessingPdusDoesNothing) -{ - upper_phy_rg_gateway_fto gw; - manual_task_worker executor(10); - - auto pdcch_processor = std::make_unique(); - auto pdsch_processor = std::make_unique(); - auto ssb_processor = std::make_unique(); - auto csi_rs_processor = std::make_unique(); - - pdcch_processor_spy& pdcch_ref = *pdcch_processor; - pdsch_processor_spy& pdsch_ref = *pdsch_processor; - ssb_processor_spy& ssb_ref = *ssb_processor; - csi_rs_processor_spy& csi_rs_ref = *csi_rs_processor; - - auto dl_processor = std::make_unique(gw, - std::move(pdcch_processor), - std::move(pdsch_processor), - std::move(ssb_processor), - std::move(csi_rs_processor), - executor, - logger); - - slot_point slot(1, 2, 1); - unsigned sector = 0; - - resource_grid_dummy grid; - dl_processor->configure_resource_grid({slot, sector}, get_dummy_grid()); - - dl_processor->process_ssb({}); - pdcch_processor::pdu_t pdu; - pdu.dci.precoding = precoding_configuration::make_wideband(make_single_port()); - dl_processor->process_pdcch(pdu); - std::vector data = {1, 2, 3, 4}; - dl_processor->process_pdsch({shared_transport_block(data)}, {}); - dl_processor->finish_processing_pdus(); - - ASSERT_TRUE(pdcch_ref.is_process_called()); - ASSERT_TRUE(pdsch_ref.is_process_called()); - ASSERT_TRUE(ssb_ref.is_process_called()); - ASSERT_TRUE(gw.sent); - - // Process a PDU after finish_processing_pdus() method has been called. - dl_processor->process_nzp_csi_rs({}); - ASSERT_FALSE(csi_rs_ref.is_map_called()); -} - -TEST(downlinkProcessorTest, processPduBeforeConfigureDoesNothing) -{ - upper_phy_rg_gateway_fto gw; - manual_task_worker executor(10); - - auto pdcch_processor = std::make_unique(); - auto pdsch_processor = std::make_unique(); - auto ssb_processor = std::make_unique(); - auto csi_rs_processor = std::make_unique(); - - pdcch_processor_spy& pdcch_ref = *pdcch_processor; - pdsch_processor_spy& pdsch_ref = *pdsch_processor; - ssb_processor_spy& ssb_ref = *ssb_processor; - csi_rs_processor_spy& csi_rs_ref = *csi_rs_processor; - - auto dl_processor = std::make_unique(gw, - std::move(pdcch_processor), - std::move(pdsch_processor), - std::move(ssb_processor), - std::move(csi_rs_processor), - executor, - logger); - - dl_processor->process_ssb({}); - pdcch_processor::pdu_t pdu; - pdu.dci.precoding = precoding_configuration::make_wideband(make_single_port()); - std::vector data = {1, 2, 3, 4}; - - dl_processor->process_pdcch(pdu); - dl_processor->process_pdsch({shared_transport_block(data)}, {}); - dl_processor->process_nzp_csi_rs({}); - - ASSERT_FALSE(pdcch_ref.is_process_called()); - ASSERT_FALSE(pdsch_ref.is_process_called()); - ASSERT_FALSE(ssb_ref.is_process_called()); - ASSERT_FALSE(csi_rs_ref.is_map_called()); - ASSERT_FALSE(gw.sent); -} - -TEST(downlinkProcessorTest, finishBeforeConfigureDeath) -{ - ::testing::GTEST_FLAG(death_test_style) = "threadsafe"; - - upper_phy_rg_gateway_fto gw; - manual_task_worker executor(10); - - auto dl_processor = - std::make_unique(gw, - std::make_unique(), - std::make_unique(), - std::make_unique(), - std::make_unique(), - executor, - logger); - - ASSERT_TRUE(!gw.sent); - -#ifdef ASSERTS_ENABLED - ASSERT_DEATH({ dl_processor->finish_processing_pdus(); }, - R"(DL processor finish was requested in an invalid state\, i\.e\.\, idle.)"); -#endif // ASSERTS_ENABLED - - ASSERT_TRUE(!gw.sent); -} - TEST(downlinkProcessorTest, twoConsecutiveSlots) { upper_phy_rg_gateway_fto gw; manual_task_worker executor(10); - auto dl_processor = + std::unique_ptr dl_proc_control = std::make_unique(gw, std::make_unique(), std::make_unique(), @@ -269,11 +161,13 @@ TEST(downlinkProcessorTest, twoConsecutiveSlots) std::make_unique(), executor, logger); + slot_point slot(1, 2, 1); unsigned sector = 0; - resource_grid_dummy grid; - dl_processor->configure_resource_grid({slot, sector}, get_dummy_grid()); + resource_grid_dummy grid; + unique_downlink_processor dl_processor = dl_proc_control->configure_resource_grid({slot, sector}, get_dummy_grid()); + ASSERT_TRUE(dl_processor.is_valid()); dl_processor->process_ssb({}); pdcch_processor::pdu_t pdu; @@ -284,13 +178,13 @@ TEST(downlinkProcessorTest, twoConsecutiveSlots) dl_processor->process_nzp_csi_rs({}); ASSERT_TRUE(!gw.sent); - dl_processor->finish_processing_pdus(); - + dl_processor.release(); ASSERT_TRUE(gw.sent); slot_point slot2(1, 2, 2); gw.clear_sent(); - dl_processor->configure_resource_grid({slot2, sector}, get_dummy_grid()); + dl_processor = dl_proc_control->configure_resource_grid({slot2, sector}, get_dummy_grid()); + ASSERT_TRUE(dl_processor.is_valid()); dl_processor->process_ssb({}); dl_processor->process_pdcch(pdu); @@ -298,7 +192,7 @@ TEST(downlinkProcessorTest, twoConsecutiveSlots) dl_processor->process_nzp_csi_rs({}); ASSERT_FALSE(gw.sent); - dl_processor->finish_processing_pdus(); + dl_processor.release(); ASSERT_TRUE(gw.sent); } @@ -308,7 +202,7 @@ TEST(downlinkProcessorTest, finishWithoutProcessingPdusSendsTheGrid) upper_phy_rg_gateway_fto gw; manual_task_worker_always_enqueue_tasks executor(10); - auto dl_processor = + std::unique_ptr dl_proc_control = std::make_unique(gw, std::make_unique(), std::make_unique(), @@ -319,10 +213,12 @@ TEST(downlinkProcessorTest, finishWithoutProcessingPdusSendsTheGrid) slot_point slot(1, 2, 1); unsigned sector = 0; - dl_processor->configure_resource_grid({slot, sector}, get_dummy_grid()); + resource_grid_dummy grid; + unique_downlink_processor dl_processor = dl_proc_control->configure_resource_grid({slot, sector}, get_dummy_grid()); + ASSERT_TRUE(dl_processor.is_valid()); // By finishing PDUs, the resource grid should be sent. - dl_processor->finish_processing_pdus(); + dl_processor.release(); ASSERT_FALSE(executor.has_pending_tasks()); ASSERT_TRUE(gw.sent); diff --git a/tests/unittests/phy/upper/downlink_processor_test_doubles.h b/tests/unittests/phy/upper/downlink_processor_test_doubles.h index 244388c9a6..d1e682283a 100644 --- a/tests/unittests/phy/upper/downlink_processor_test_doubles.h +++ b/tests/unittests/phy/upper/downlink_processor_test_doubles.h @@ -16,24 +16,15 @@ namespace srsran { /// Spy implementation of a downlink processor -class downlink_processor_spy : public downlink_processor +class downlink_processor_spy : public downlink_processor_controller, + private unique_downlink_processor::downlink_processor_callback { +private: unsigned id; bool configure_resource_grid_method_called = false; bool process_ssb_method_called = false; bool finish_processing_pdus_method_called = false; -public: - explicit downlink_processor_spy(unsigned id_) : id(id_) {} - - /// Returns the identifier of downlink processor. - unsigned get_id() const { return id; } - - /// Returns true if the method has been called, otherwise false. - bool has_configure_resource_grid_method_been_called() const { return configure_resource_grid_method_called; } - bool has_process_ssb_method_been_called() const { return process_ssb_method_called; } - bool has_finish_processing_pdus_method_been_called() const { return finish_processing_pdus_method_called; } - void process_pdcch(const pdcch_processor::pdu_t& pdu) override {} void process_pdsch(static_vector data, @@ -45,13 +36,25 @@ class downlink_processor_spy : public downlink_processor void process_nzp_csi_rs(const nzp_csi_rs_generator::config_t& config) override {} - bool configure_resource_grid(const resource_grid_context& context, shared_resource_grid grid) override + void finish_processing_pdus() override { finish_processing_pdus_method_called = true; } + +public: + explicit downlink_processor_spy(unsigned id_) : id(id_) {} + + /// Returns the identifier of downlink processor. + unsigned get_id() const { return id; } + + /// Returns true if the method has been called, otherwise false. + bool has_configure_resource_grid_method_been_called() const { return configure_resource_grid_method_called; } + bool has_process_ssb_method_been_called() const { return process_ssb_method_called; } + bool has_finish_processing_pdus_method_been_called() const { return finish_processing_pdus_method_called; } + + unique_downlink_processor configure_resource_grid(const resource_grid_context& context, + shared_resource_grid grid) override { configure_resource_grid_method_called = true; - return true; + return unique_downlink_processor(*this); } - - void finish_processing_pdus() override { finish_processing_pdus_method_called = true; } }; } // namespace srsran From ff1a32265f01e27530228d9ba427eb26db26d17b Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Wed, 8 Jan 2025 16:04:54 +0100 Subject: [PATCH 090/107] phy: fix downlink processor for multicell phy: remove unused header --- include/srsran/phy/upper/downlink_processor.h | 8 +- .../srsran/phy/upper/upper_phy_factories.h | 4 - .../phy/fapi_to_phy_translator.cpp | 4 +- .../upper/downlink_processor_pool_impl.cpp | 12 +- lib/phy/upper/downlink_processor_pool_impl.h | 8 +- lib/phy/upper/upper_phy_factories.cpp | 6 +- .../phy/fapi_to_phy_translator_test.cpp | 2 +- tests/unittests/phy/upper/CMakeLists.txt | 2 +- .../upper/downlink_processor_pool_test.cpp | 164 ++++++------------ 9 files changed, 69 insertions(+), 141 deletions(-) diff --git a/include/srsran/phy/upper/downlink_processor.h b/include/srsran/phy/upper/downlink_processor.h index 5b2baca27c..6d6f83aa9c 100644 --- a/include/srsran/phy/upper/downlink_processor.h +++ b/include/srsran/phy/upper/downlink_processor.h @@ -199,12 +199,8 @@ class downlink_processor_pool public: virtual ~downlink_processor_pool() = default; - /// \brief Returns a downlink processor controller with the given slot and sector. - /// - /// \param slot Slot point. - /// \param sector_id Sector ID. - /// \return A downlink processor. - virtual downlink_processor_controller& get_processor_controller(slot_point slot, unsigned sector_id) = 0; + /// Gets the downlink processor controller associated with the given slot. + virtual downlink_processor_controller& get_processor_controller(slot_point slot) = 0; }; } // namespace srsran diff --git a/include/srsran/phy/upper/upper_phy_factories.h b/include/srsran/phy/upper/upper_phy_factories.h index b27aa8d1b4..e9f8c5d110 100644 --- a/include/srsran/phy/upper/upper_phy_factories.h +++ b/include/srsran/phy/upper/upper_phy_factories.h @@ -183,8 +183,6 @@ create_downlink_processor_factory_sw(const downlink_processor_factory_sw_config& struct downlink_processor_pool_config { /// Downlink processors for a given sector and numerology. struct sector_dl_processor { - /// Base station sector identifier. - unsigned sector; /// Subcarrier spacing. subcarrier_spacing scs; /// Pointers to the actual downlink processors. @@ -193,8 +191,6 @@ struct downlink_processor_pool_config { /// Collection of all downlink processors, organized by radio sector and numerology. std::vector dl_processors; - /// Number of base station sector. - unsigned num_sectors; }; /// \brief Creates and returns a downlink processor pool. diff --git a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp index 16e49c6c8d..c31cabc5ec 100644 --- a/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp +++ b/lib/fapi_adaptor/phy/fapi_to_phy_translator.cpp @@ -117,8 +117,8 @@ fapi_to_phy_translator::slot_based_upper_phy_controller::slot_based_upper_phy_co return; } - // Obtain a downlink processor controller associated with the given slot and sector. - downlink_processor_controller& dl_proc_controller = dl_processor_pool.get_processor_controller(slot, sector_id); + // Obtain the downlink processor controller associated with the given slot. + downlink_processor_controller& dl_proc_controller = dl_processor_pool.get_processor_controller(slot); // Configure the downlink processor. dl_processor = dl_proc_controller.configure_resource_grid(context, std::move(grid)); diff --git a/lib/phy/upper/downlink_processor_pool_impl.cpp b/lib/phy/upper/downlink_processor_pool_impl.cpp index 32c1afb1dc..5affca81c1 100644 --- a/lib/phy/upper/downlink_processor_pool_impl.cpp +++ b/lib/phy/upper/downlink_processor_pool_impl.cpp @@ -13,20 +13,16 @@ using namespace srsran; -downlink_processor_pool_impl::downlink_processor_pool_impl(downlink_processor_pool_impl_config dl_processors) : - processors(dl_processors.num_sectors) +downlink_processor_pool_impl::downlink_processor_pool_impl(downlink_processor_pool_impl_config dl_processors) { for (auto& proc : dl_processors.procs) { srsran_assert(!proc.procs.empty(), "Cannot store an empty processor pool"); - processors[proc.sector].insert(proc.scs, std::move(proc.procs)); + processors.insert(proc.scs, std::move(proc.procs)); } } -downlink_processor_controller& downlink_processor_pool_impl::get_processor_controller(slot_point slot, - unsigned sector_id) +downlink_processor_controller& downlink_processor_pool_impl::get_processor_controller(slot_point slot) { - srsran_assert(sector_id < processors.size(), "Invalid sector ({}) when requesting a downlink processor", sector_id); srsran_assert(slot.valid(), "Invalid slot ({}) when requesting a downlink processor", slot); - - return processors[sector_id].get_processor(slot); + return processors.get_processor(slot); } diff --git a/lib/phy/upper/downlink_processor_pool_impl.h b/lib/phy/upper/downlink_processor_pool_impl.h index 4999e5b1ae..68689a130f 100644 --- a/lib/phy/upper/downlink_processor_pool_impl.h +++ b/lib/phy/upper/downlink_processor_pool_impl.h @@ -19,8 +19,6 @@ namespace srsran { struct downlink_processor_pool_impl_config { /// Downlink processors for a given sector and numerology. struct sector_dl_processor { - /// Radio sector identifier. - unsigned sector; /// Subcarrier spacing. subcarrier_spacing scs; /// Pointers to the actual downlink processors. @@ -29,8 +27,6 @@ struct downlink_processor_pool_impl_config { /// Collection of all downlink processors, organized by radio sector and numerology. std::vector procs; - /// Number of radio sectors. - unsigned num_sectors; }; /// Implementation of a downlink processor pool. @@ -43,11 +39,11 @@ class downlink_processor_pool_impl : public downlink_processor_pool explicit downlink_processor_pool_impl(downlink_processor_pool_impl_config dl_processors); // See interface for documentation. - downlink_processor_controller& get_processor_controller(slot_point slot, unsigned sector_id) override; + downlink_processor_controller& get_processor_controller(slot_point slot) override; private: /// Container for downlink processors. Each entry belongs to a different sector. - std::vector> processors; + processor_pool_repository processors; }; } // namespace srsran diff --git a/lib/phy/upper/upper_phy_factories.cpp b/lib/phy/upper/upper_phy_factories.cpp index 8d54daa5a7..669a9259e7 100644 --- a/lib/phy/upper/upper_phy_factories.cpp +++ b/lib/phy/upper/upper_phy_factories.cpp @@ -261,7 +261,6 @@ create_downlink_processor_pool(std::shared_ptr facto dl_phy_logger.set_hex_dump_max_size(config.logger_max_hex_size); downlink_processor_pool_config config_pool; - config_pool.num_sectors = 1; for (unsigned numerology = 0, numerology_end = to_numerology_value(subcarrier_spacing::invalid); numerology != numerology_end; @@ -271,7 +270,7 @@ create_downlink_processor_pool(std::shared_ptr facto continue; } - downlink_processor_pool_config::sector_dl_processor info = {0, to_subcarrier_spacing(numerology), {}}; + downlink_processor_pool_config::sector_dl_processor info = {to_subcarrier_spacing(numerology), {}}; for (unsigned i_proc = 0, nof_procs = config.nof_dl_processors; i_proc != nof_procs; ++i_proc) { downlink_processor_config processor_config; @@ -883,10 +882,9 @@ std::unique_ptr srsran::create_dl_processor_pool(downli { // Convert from pool config to pool_impl config. downlink_processor_pool_impl_config dl_processors; - dl_processors.num_sectors = config.num_sectors; for (auto& proc : config.dl_processors) { - dl_processors.procs.push_back({proc.sector, proc.scs, std::move(proc.procs)}); + dl_processors.procs.push_back({proc.scs, std::move(proc.procs)}); } return std::make_unique(std::move(dl_processors)); diff --git a/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp b/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp index fe8d178a25..1281a9aa8c 100644 --- a/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp +++ b/tests/unittests/fapi_adaptor/phy/fapi_to_phy_translator_test.cpp @@ -139,7 +139,7 @@ class downlink_processor_pool_dummy : public downlink_processor_pool dl_processor.emplace_back(1); } - downlink_processor_controller& get_processor_controller(slot_point slot, unsigned sector_id) override + downlink_processor_controller& get_processor_controller(slot_point slot) override { return dl_processor[slot.slot_index()]; } diff --git a/tests/unittests/phy/upper/CMakeLists.txt b/tests/unittests/phy/upper/CMakeLists.txt index 7e8c5fd3e4..c97ad6845a 100644 --- a/tests/unittests/phy/upper/CMakeLists.txt +++ b/tests/unittests/phy/upper/CMakeLists.txt @@ -23,7 +23,7 @@ target_link_libraries(channel_state_information_test srslog fmt gtest gtest_main add_test(channel_state_information_test channel_state_information_test) add_executable(downlink_processor_pool_test downlink_processor_pool_test.cpp) -target_link_libraries(downlink_processor_pool_test srsran_upper_phy srslog) +target_link_libraries(downlink_processor_pool_test srsran_upper_phy srslog gtest gtest_main) add_test(downlink_processor_pool_test downlink_processor_pool_test) add_executable(downlink_processor_test downlink_processor_test.cpp) diff --git a/tests/unittests/phy/upper/downlink_processor_pool_test.cpp b/tests/unittests/phy/upper/downlink_processor_pool_test.cpp index c4fe929e62..e278bec5c7 100644 --- a/tests/unittests/phy/upper/downlink_processor_pool_test.cpp +++ b/tests/unittests/phy/upper/downlink_processor_pool_test.cpp @@ -10,112 +10,79 @@ #include "downlink_processor_test_doubles.h" #include "srsran/phy/upper/upper_phy_factories.h" -#include "srsran/support/srsran_test.h" +#include using namespace srsran; -static void test_dl_processor_ok() +TEST(DownlingProcessorPool, ProcessorOk) { downlink_processor_pool_config dl_procs; - dl_procs.num_sectors = 1; - unsigned sector = 0; - subcarrier_spacing scs = subcarrier_spacing::kHz30; - unsigned id = 0; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned id = 0; - downlink_processor_pool_config::sector_dl_processor info{sector, scs, {}}; + downlink_processor_pool_config::sector_dl_processor info{scs, {}}; info.procs.emplace_back(std::make_unique(id)); dl_procs.dl_processors.push_back(std::move(info)); std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); slot_point slot(to_numerology_value(scs), 0, 0); - downlink_processor_controller& dl_processor = dl_proc_pool->get_processor_controller(slot, id); + downlink_processor_controller& dl_processor = dl_proc_pool->get_processor_controller(slot); - TESTASSERT_EQ(static_cast(dl_processor).get_id(), id); + ASSERT_EQ(static_cast(dl_processor).get_id(), id); } -void test_pass_same_slot_gets_same_processor() +TEST(DownlingProcessorPool, SameSlotSameProcessor) { downlink_processor_pool_config dl_procs; - dl_procs.num_sectors = 1; - unsigned sector = 0; - subcarrier_spacing scs = subcarrier_spacing::kHz30; - unsigned id = 0; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned id = 0; - downlink_processor_pool_config::sector_dl_processor info{sector, scs, {}}; + downlink_processor_pool_config::sector_dl_processor info{scs, {}}; info.procs.emplace_back(std::make_unique(id)); + info.procs.emplace_back(std::make_unique(id + 1)); + info.procs.emplace_back(std::make_unique(id + 2)); dl_procs.dl_processors.push_back(std::move(info)); std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); slot_point slot(to_numerology_value(scs), 0, 0); - downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(slot, sector); - downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(slot, sector); + downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(slot); + downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(slot); - TESTASSERT_EQ(static_cast(dl_processor_0).get_id(), id); - TESTASSERT_EQ(static_cast(dl_processor_1).get_id(), id); - TESTASSERT(&dl_processor_0 == &dl_processor_1); + ASSERT_EQ(static_cast(dl_processor_0).get_id(), id); + ASSERT_EQ(static_cast(dl_processor_1).get_id(), id); + ASSERT_TRUE(&dl_processor_0 == &dl_processor_1); } -static void test_consecutive_dl_processor_ok() +TEST(DownlingProcessorPool, ConsecutiveProcessorOk) { - downlink_processor_pool_config dl_procs; - dl_procs.num_sectors = 1; - subcarrier_spacing scs = subcarrier_spacing::kHz30; - unsigned sector = 0; - downlink_processor_pool_config::sector_dl_processor info{sector, scs, {}}; - - info.procs.emplace_back(std::make_unique(0)); - info.procs.emplace_back(std::make_unique(1)); + downlink_processor_pool_config dl_procs; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + unsigned id0 = 0; + unsigned id1 = 1; + downlink_processor_pool_config::sector_dl_processor info{scs, {}}; + + info.procs.emplace_back(std::make_unique(id0)); + info.procs.emplace_back(std::make_unique(id1)); dl_procs.dl_processors.push_back(std::move(info)); std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); slot_point first(to_numerology_value(scs), 0, 0); - downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(first, sector); + downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(first); slot_point second(to_numerology_value(scs), 0, 1); - downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(second, sector); + downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(second); - TESTASSERT_EQ(static_cast(dl_processor_0).get_id(), 0); - TESTASSERT_EQ(static_cast(dl_processor_1).get_id(), 1); + ASSERT_EQ(static_cast(dl_processor_0).get_id(), id0); + ASSERT_EQ(static_cast(dl_processor_1).get_id(), id1); } -static void test_2sectors_2dl_processor_ok() +TEST(DownlingProcessorPool, CircularBufferOk) { - downlink_processor_pool_config dl_procs; - dl_procs.num_sectors = 2; - subcarrier_spacing scs = subcarrier_spacing::kHz30; - for (unsigned i = 0, e = dl_procs.num_sectors; i != e; ++i) { - downlink_processor_pool_config::sector_dl_processor info{i, scs, {}}; - info.procs.emplace_back(std::make_unique(i * 10 + 0)); - info.procs.emplace_back(std::make_unique(i * 10 + 1)); - dl_procs.dl_processors.push_back(std::move(info)); - } - - std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); - - slot_point first(to_numerology_value(scs), 0, 0); - downlink_processor_controller& dl_processor_00 = dl_proc_pool->get_processor_controller(first, 0); - slot_point second(to_numerology_value(scs), 0, 1); - downlink_processor_controller& dl_processor_01 = dl_proc_pool->get_processor_controller(second, 0); - slot_point third(to_numerology_value(scs), 0, 0); - downlink_processor_controller& dl_processor_10 = dl_proc_pool->get_processor_controller(third, 1); - slot_point fourth(to_numerology_value(scs), 0, 1); - downlink_processor_controller& dl_processor_11 = dl_proc_pool->get_processor_controller(fourth, 1); - - TESTASSERT_EQ(static_cast(dl_processor_00).get_id(), 0); - TESTASSERT_EQ(static_cast(dl_processor_01).get_id(), 1); - TESTASSERT_EQ(static_cast(dl_processor_10).get_id(), 10); - TESTASSERT_EQ(static_cast(dl_processor_11).get_id(), 11); -} - -static void test_circular_buffer_ok() -{ - downlink_processor_pool_config dl_procs; - dl_procs.num_sectors = 1; - subcarrier_spacing scs = subcarrier_spacing::kHz30; - unsigned sector = 0; - downlink_processor_pool_config::sector_dl_processor info{sector, scs, {}}; + downlink_processor_pool_config dl_procs; + subcarrier_spacing scs = subcarrier_spacing::kHz30; + downlink_processor_pool_config::sector_dl_processor info{scs, {}}; info.procs.emplace_back(std::make_unique(0)); info.procs.emplace_back(std::make_unique(1)); dl_procs.dl_processors.push_back(std::move(info)); @@ -123,61 +90,40 @@ static void test_circular_buffer_ok() std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); slot_point first(to_numerology_value(scs), 0, 0); - downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(first, sector); + downlink_processor_controller& dl_processor_0 = dl_proc_pool->get_processor_controller(first); slot_point second(to_numerology_value(scs), 0, 1); - downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(second, sector); + downlink_processor_controller& dl_processor_1 = dl_proc_pool->get_processor_controller(second); slot_point third(to_numerology_value(scs), 0, 2); - downlink_processor_controller& dl_processor_2 = dl_proc_pool->get_processor_controller(third, sector); + downlink_processor_controller& dl_processor_2 = dl_proc_pool->get_processor_controller(third); - TESTASSERT_EQ(static_cast(dl_processor_0).get_id(), 0); - TESTASSERT_EQ(static_cast(dl_processor_1).get_id(), 1); - TESTASSERT_EQ(static_cast(dl_processor_2).get_id(), 0); + ASSERT_EQ(static_cast(dl_processor_0).get_id(), 0); + ASSERT_EQ(static_cast(dl_processor_1).get_id(), 1); + ASSERT_EQ(static_cast(dl_processor_2).get_id(), 0); } -static void test_2sectors_2numerologies_2dl_processor_ok() +TEST(DownlingProcessorPool, TwoNumerologiesTwoProcessors) { downlink_processor_pool_config dl_procs; - dl_procs.num_sectors = 2; - for (unsigned i = 0, e = dl_procs.num_sectors; i != e; ++i) { - for (auto scs : {subcarrier_spacing::kHz30, subcarrier_spacing::kHz120}) { - downlink_processor_pool_config::sector_dl_processor info{i, scs, {}}; - info.procs.emplace_back(std::make_unique(i * 100 + to_numerology_value(scs) * 10 + 0)); - info.procs.emplace_back(std::make_unique(i * 100 + to_numerology_value(scs) * 10 + 1)); - dl_procs.dl_processors.push_back(std::move(info)); - } + for (auto scs : {subcarrier_spacing::kHz30, subcarrier_spacing::kHz120}) { + downlink_processor_pool_config::sector_dl_processor info{scs, {}}; + info.procs.emplace_back(std::make_unique(to_numerology_value(scs) * 10 + 0)); + info.procs.emplace_back(std::make_unique(to_numerology_value(scs) * 10 + 1)); + dl_procs.dl_processors.push_back(std::move(info)); } std::unique_ptr dl_proc_pool = create_dl_processor_pool({std::move(dl_procs)}); slot_point first(1, 0, 0); - downlink_processor_controller& dl_processor_010 = dl_proc_pool->get_processor_controller(first, 0); + downlink_processor_controller& dl_processor_010 = dl_proc_pool->get_processor_controller(first); slot_point second(1, 0, 1); - downlink_processor_controller& dl_processor_011 = dl_proc_pool->get_processor_controller(second, 0); + downlink_processor_controller& dl_processor_011 = dl_proc_pool->get_processor_controller(second); slot_point third(3, 0, 0); - downlink_processor_controller& dl_processor_030 = dl_proc_pool->get_processor_controller(third, 0); + downlink_processor_controller& dl_processor_030 = dl_proc_pool->get_processor_controller(third); slot_point fourth(3, 0, 1); - downlink_processor_controller& dl_processor_031 = dl_proc_pool->get_processor_controller(fourth, 0); - downlink_processor_controller& dl_processor_110 = dl_proc_pool->get_processor_controller(first, 1); - downlink_processor_controller& dl_processor_111 = dl_proc_pool->get_processor_controller(second, 1); - downlink_processor_controller& dl_processor_130 = dl_proc_pool->get_processor_controller(third, 1); - downlink_processor_controller& dl_processor_131 = dl_proc_pool->get_processor_controller(fourth, 1); - - TESTASSERT_EQ(static_cast(dl_processor_010).get_id(), 10); - TESTASSERT_EQ(static_cast(dl_processor_011).get_id(), 11); - TESTASSERT_EQ(static_cast(dl_processor_030).get_id(), 30); - TESTASSERT_EQ(static_cast(dl_processor_031).get_id(), 31); - TESTASSERT_EQ(static_cast(dl_processor_110).get_id(), 110); - TESTASSERT_EQ(static_cast(dl_processor_111).get_id(), 111); - TESTASSERT_EQ(static_cast(dl_processor_130).get_id(), 130); - TESTASSERT_EQ(static_cast(dl_processor_131).get_id(), 131); -} + downlink_processor_controller& dl_processor_031 = dl_proc_pool->get_processor_controller(fourth); -int main() -{ - test_dl_processor_ok(); - test_pass_same_slot_gets_same_processor(); - test_consecutive_dl_processor_ok(); - test_2sectors_2dl_processor_ok(); - test_2sectors_2numerologies_2dl_processor_ok(); - test_circular_buffer_ok(); + ASSERT_EQ(static_cast(dl_processor_010).get_id(), 10); + ASSERT_EQ(static_cast(dl_processor_011).get_id(), 11); + ASSERT_EQ(static_cast(dl_processor_030).get_id(), 30); + ASSERT_EQ(static_cast(dl_processor_031).get_id(), 31); } From 359f02bc4c32cd00f766e19384f7c8954bd09de5 Mon Sep 17 00:00:00 2001 From: Fabian Eckermann Date: Thu, 9 Jan 2025 16:51:20 +0100 Subject: [PATCH 091/107] cu_cp,ngap: add gbr qos flow info conversion --- lib/ngap/ngap_asn1_converters.h | 21 +++++++++++++++++++++ lib/ngap/ngap_asn1_helpers.h | 3 ++- 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/lib/ngap/ngap_asn1_converters.h b/lib/ngap/ngap_asn1_converters.h index eec2a06622..3a7ae16b6d 100644 --- a/lib/ngap/ngap_asn1_converters.h +++ b/lib/ngap/ngap_asn1_converters.h @@ -849,5 +849,26 @@ inline cu_cp_five_g_s_tmsi ngap_asn1_to_ue_paging_id(const asn1::ngap::ue_paging asn1_ue_id.five_g_s_tmsi().five_g_tmsi.to_number()}; } +/// \brief Convert NGAP ASN.1 to \c gbr_qos_flow_information. +/// \param[in] asn1_gbr_qos_info The ASN.1 type gbr qos info. +/// \return The common type gbr qos flow information. +inline gbr_qos_flow_information +ngap_asn1_to_gbr_qos_flow_information(const asn1::ngap::gbr_qos_info_s& asn1_gbr_qos_info) +{ + gbr_qos_flow_information gbr_qos_info; + gbr_qos_info.max_br_dl = asn1_gbr_qos_info.max_flow_bit_rate_dl; + gbr_qos_info.max_br_ul = asn1_gbr_qos_info.max_flow_bit_rate_ul; + gbr_qos_info.gbr_dl = asn1_gbr_qos_info.guaranteed_flow_bit_rate_dl; + gbr_qos_info.gbr_ul = asn1_gbr_qos_info.guaranteed_flow_bit_rate_ul; + if (asn1_gbr_qos_info.max_packet_loss_rate_dl_present) { + gbr_qos_info.max_packet_loss_rate_dl = asn1_gbr_qos_info.max_packet_loss_rate_dl; + } + if (asn1_gbr_qos_info.max_packet_loss_rate_ul_present) { + gbr_qos_info.max_packet_loss_rate_ul = asn1_gbr_qos_info.max_packet_loss_rate_ul; + } + + return gbr_qos_info; +} + } // namespace srs_cu_cp } // namespace srsran diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index ce0d3e8211..1d53f5f5c2 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -335,7 +335,8 @@ inline bool fill_cu_cp_pdu_session_resource_setup_item_base(cu_cp_pdu_session_re } if (asn1_flow_item.qos_flow_level_qos_params.gbr_qos_info_present) { - // TODO: Add to common type. + qos_flow_setup_req_item.qos_flow_level_qos_params.gbr_qos_info = + ngap_asn1_to_gbr_qos_flow_information(asn1_flow_item.qos_flow_level_qos_params.gbr_qos_info); } if (asn1_flow_item.qos_flow_level_qos_params.reflective_qos_attribute_present) { From 313a1661572b066573b1205d8c6877049692341f Mon Sep 17 00:00:00 2001 From: dvdgrgrtt Date: Tue, 10 Dec 2024 18:21:12 +0100 Subject: [PATCH 092/107] phy, ch estimator: minor refactor Prepare for four uplink layers. --- .../port_channel_estimator_average_impl.cpp | 33 ++++++++----- .../port_channel_estimator_average_impl.h | 47 +++++++++++++------ 2 files changed, 54 insertions(+), 26 deletions(-) diff --git a/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp b/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp index 63af8ee458..699a997142 100644 --- a/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp +++ b/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp @@ -212,8 +212,8 @@ void port_channel_estimator_average_impl::compute_hop(srsran::channel_estimate& // Preprocess the pilots and compute the hop contribution to the CFO. Recall that this method updates // pilot_products and pilots_lse. - std::optional cfo_hop = preprocess_pilots_and_cfo( - pilots, pattern.symbols, cfg.scs, first_symbol, last_symbol, hop_offset, layer0, layer0 + 1); + std::optional cfo_hop = preprocess_pilots_and_estimate_cfo( + pilots, pattern.symbols, first_symbol, last_symbol, hop_offset, layer0, layer0 + 1); if (cfo_hop.has_value()) { cfo_normalized = evaluate_or( cfo_normalized, cfo_hop.value(), [](float a, float b) { return (a + b) / 2.0F; }, cfo_hop.value()); @@ -284,15 +284,14 @@ void port_channel_estimator_average_impl::compute_hop(srsran::channel_estimate& hop_offset); } -std::optional -port_channel_estimator_average_impl::preprocess_pilots_and_cfo(const dmrs_symbol_list& pilots, - const bounded_bitset& dmrs_mask, - const subcarrier_spacing& scs, - unsigned first_hop_symbol, - unsigned last_hop_symbol, - unsigned hop_offset, - unsigned /* unused */, - unsigned /* unused */) +std::optional port_channel_estimator_average_impl::preprocess_pilots_and_estimate_cfo( + const dmrs_symbol_list& pilots, + const bounded_bitset& dmrs_mask, + unsigned first_hop_symbol, + unsigned last_hop_symbol, + unsigned hop_offset, + unsigned /* unused */, + unsigned /* unused */) { constexpr unsigned layer0 = 0; @@ -355,6 +354,18 @@ port_channel_estimator_average_impl::preprocess_pilots_and_cfo(const dmrs_symbol return cfo; } +// In this version of the code, the preprocess_pilots_and_estimate_cfo method takes care of compensating the CFO. +void port_channel_estimator_average_impl:: + compensate_cfo_and_accumulate( // NOLINT(readability-convert-member-functions-to-static) + const dmrs_symbol_list& /* unused */, + const bounded_bitset& /* unused */, + unsigned /* unused */, + unsigned /* unused */, + std::optional /* unused */) +{ + srsran_assertion_failure("Function not implemented."); +} + static unsigned extract_layer_hop_rx_pilots(dmrs_symbol_list& rx_symbols, const resource_grid_reader& grid, unsigned port, diff --git a/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.h b/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.h index 2f2504ce1f..e57bdcbe83 100644 --- a/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.h +++ b/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.h @@ -26,6 +26,9 @@ namespace srsran { class port_channel_estimator_average_impl : public port_channel_estimator { public: + /// Maximum supported number of layers. + static constexpr unsigned MAX_LAYERS = 4; + /// \brief Maximum SINR in decibels. /// /// The SINR is bounded above to avoid a zero noise variance. @@ -78,12 +81,9 @@ class port_channel_estimator_average_impl : public port_channel_estimator /// /// For the current hop, the function does the following: /// - matches the received pilots with the expected ones (element-wise multiplication with complex conjugate); - /// - estimates the CFO (if the number of OFDM symbols with pilots is at least 2); - /// - compensates the CFO for all pilots; - /// - accumulates all the matched, CFO-compensated received pilots. + /// - estimates the CFO (if the number of OFDM symbols with pilots is at least 2). /// \param[in] pilots Transmitted pilots. /// \param[in] dmrs_mask Boolean mask identifying the OFDM symbols carrying DM-RS within the slot. - /// \param[in] scs Subcarrier spacing. /// \param[in] first_hop_symbol Index of the first OFDM symbol of the current hop, within the slot. /// \param[in] last_hop_symbol Index of the last OFDM symbol of the current hop (not included), within the slot. /// \param[in] hop_offset Number of OFDM symbols carrying DM-RS in the previous hop. @@ -92,14 +92,31 @@ class port_channel_estimator_average_impl : public port_channel_estimator /// \return A contribution to the CFO estimate. CFO is empty if the hop has only one OFDM symbol carrying DM-RS. /// /// \warning This method updates the content of the buffers \c pilots_lse and \c pilot_products. - std::optional preprocess_pilots_and_cfo(const dmrs_symbol_list& pilots, - const bounded_bitset& dmrs_mask, - const subcarrier_spacing& scs, - unsigned first_hop_symbol, - unsigned last_hop_symbol, - unsigned hop_offset, - unsigned start_layer, - unsigned stop_layer); + std::optional preprocess_pilots_and_estimate_cfo(const dmrs_symbol_list& pilots, + const bounded_bitset& dmrs_mask, + unsigned first_hop_symbol, + unsigned last_hop_symbol, + unsigned hop_offset, + unsigned start_layer, + unsigned stop_layer); + + /// \brief Compensates the CFO. + /// + /// For the current hop: + /// - compensates the CFO for all pilots; + /// - accumulates all the matched, CFO-compensated received pilots from all OFDM symbols carrying DM-RS. + /// \param[in] pilots Transmitted pilots. + /// \param[in] dmrs_mask Boolean mask identifying the OFDM symbols carrying DM-RS within the slot. + /// \param[in] first_hop_symbol Index of the first OFDM symbol of the current hop, within the slot. + /// \param[in] last_hop_symbol Index of the last OFDM symbol of the current hop (not included), within the slot. + /// \param[in] cfo Estimated CFO. + /// + /// \warning This method updates the content of the buffers \c pilots_lse and \c pilot_products. + void compensate_cfo_and_accumulate(const dmrs_symbol_list& pilots, + const bounded_bitset& dmrs_mask, + unsigned first_hop_symbol, + unsigned last_hop_symbol, + std::optional cfo); /// \brief Computes the starting time of the symbols inside a slot for the given subcarrier spacing. /// @@ -125,11 +142,11 @@ class port_channel_estimator_average_impl : public port_channel_estimator dmrs_symbol_list rx_pilots; /// Auxiliary buffer for processing the pilots. - static_re_buffer<2, MAX_RB * NRE> pilot_products; + static_re_buffer pilot_products; /// Second auxiliary buffer for processing the pilots. - static_re_buffer<2, MAX_RB * NRE + 2 * MAX_V_PILOTS> enlarged_pilots_lse; - modular_re_buffer pilots_lse; + static_re_buffer enlarged_pilots_lse; + modular_re_buffer pilots_lse; /// Buffer of frequency response coefficients. std::array freq_response; From d93a5ed584a873c82f7ef6b16961c1f38648ad99 Mon Sep 17 00:00:00 2001 From: dvdgrgrtt Date: Tue, 10 Dec 2024 18:23:28 +0100 Subject: [PATCH 093/107] tests, ch estimator: new set of test vectors --- .../dmrs_pusch_estimator_test_data.h | 2 +- .../dmrs_pusch_estimator_test_data.tar.gz | 4 +- .../port_channel_estimator_test.cpp | 11 +- .../port_channel_estimator_test_data.h | 370 +++++++++++------- .../port_channel_estimator_test_data.tar.gz | 4 +- 5 files changed, 236 insertions(+), 155 deletions(-) diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.h b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.h index f344ce3376..855c4416fb 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.h +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.h @@ -10,7 +10,7 @@ #pragma once -// This file was generated using the following MATLAB class on 07-10-2024 (seed 0): +// This file was generated using the following MATLAB class on 23-12-2024 (seed 0): // + "srsPUSCHdmrsUnittest.m" #include "../../support/resource_grid_test_doubles.h" diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.tar.gz b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.tar.gz index ca6b83a7a2..6a2007f8d8 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.tar.gz +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test_data.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9a3e0bfbdddae86fd68282247d6e1279d7b129486a3e6d0e48a3d14d2e8bec5d -size 8237197 +oid sha256:bed56e7900b98888a9a67fce9e6b2a3a83a81e76302ef91ca375100b56fbfd2e +size 8653924 diff --git a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp index 6d2d75b463..d869ab019b 100644 --- a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp @@ -114,12 +114,12 @@ TEST_P(ChannelEstFixture, test) const port_channel_estimator::configuration& cfg = test_params.cfg; - // For now, we consider at most 2 layers. + // For now, we consider at most 4 layers. unsigned nof_layers = cfg.dmrs_pattern.size(); - ASSERT_LE(nof_layers, 2) << "For now, at most two transmission layers are supported."; + ASSERT_LE(nof_layers, 4) << "For now, at most four transmission layers are supported."; - if ((nof_layers == 2) && (get_pusch_processor_phy_capabilities().max_nof_layers < 2)) { - GTEST_SKIP() << "The 2-layer channel estimator is not supported in this version - skipping the test."; + if ((nof_layers > 1) && (get_pusch_processor_phy_capabilities().max_nof_layers < 2)) { + GTEST_SKIP() << "The channel estimator for 2 or more layers is not supported in this version - skipping the test."; } // The test only considers a single port. @@ -137,8 +137,9 @@ TEST_P(ChannelEstFixture, test) ASSERT_EQ(pilots.size(), nof_dmrs_pilots * nof_layers) << fmt::format( "Number of DM-RS pilots mismatch: configured {}, provided {}.", nof_dmrs_pilots * nof_layers, pilots.size()); + unsigned nof_cdms = divide_ceil(nof_layers, 2); std::vector grid_entries = test_params.grid.read(); - ASSERT_EQ(grid_entries.size(), nof_dmrs_pilots) << fmt::format( + ASSERT_EQ(grid_entries.size(), nof_dmrs_pilots * nof_cdms) << fmt::format( "Number of received pilots mismatch: configured {}, read {}.", nof_dmrs_pilots, grid_entries.size()); unsigned nof_allocated_res = nof_allocatd_rblocks * NRE * cfg.nof_symbols; diff --git a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.h b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.h index e010db6725..d2dd10b965 100644 --- a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.h +++ b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.h @@ -10,7 +10,7 @@ #pragma once -// This file was generated using the following MATLAB class on 24-09-2024 (seed 0): +// This file was generated using the following MATLAB class on 10-12-2024 (seed 0): // + "srsChEstimatorUnittest.m" #include "../../support/resource_grid_test_doubles.h" @@ -49,150 +49,230 @@ static const std::vector port_channel_estimator_test_data = { {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0578, 2.0249, 20, 20.3531, 0.0095082, 0.84635, 105, 105.5152, {"test_data/port_channel_estimator_test_input_rg5.dat"}, {"test_data/port_channel_estimator_test_pilots5.dat"}, {"test_data/port_channel_estimator_test_output_ch_est5.dat"}}, {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0202, 2.0275, 20, 20.9718, 0.0080948, 0.10579, -195, -188.1324, {"test_data/port_channel_estimator_test_input_rg6.dat"}, {"test_data/port_channel_estimator_test_pilots6.dat"}, {"test_data/port_channel_estimator_test_output_ch_est6.dat"}}, {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0204, 1.9896, 20, 19.556, 0.011216, 0.96842, 405, 407.0491, {"test_data/port_channel_estimator_test_input_rg7.dat"}, {"test_data/port_channel_estimator_test_pilots7.dat"}, {"test_data/port_channel_estimator_test_output_ch_est7.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0139, 2.0335, 20, 16.3946, 0.011655, 0.19531, 0, 6.8962, {"test_data/port_channel_estimator_test_input_rg8.dat"}, {"test_data/port_channel_estimator_test_pilots8.dat"}, {"test_data/port_channel_estimator_test_output_ch_est8.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0041, 2.0113, 20, 16.4025, 0.011522, 0.3418, 105, 103.4282, {"test_data/port_channel_estimator_test_input_rg9.dat"}, {"test_data/port_channel_estimator_test_pilots9.dat"}, {"test_data/port_channel_estimator_test_output_ch_est9.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0174, 2.0307, 20, 15.8544, 0.013245, 0.42318, -195, -205.2808, {"test_data/port_channel_estimator_test_input_rg10.dat"}, {"test_data/port_channel_estimator_test_pilots10.dat"}, {"test_data/port_channel_estimator_test_output_ch_est10.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.98505, 1.9569, 20, 15.1077, 0.015229, 0.63477, 405, 411.0762, {"test_data/port_channel_estimator_test_input_rg11.dat"}, {"test_data/port_channel_estimator_test_pilots11.dat"}, {"test_data/port_channel_estimator_test_output_ch_est11.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0119, 2.0118, 20, 12.2312, 0.030341, 1.0661, 0, -1.6171, {"test_data/port_channel_estimator_test_input_rg12.dat"}, {"test_data/port_channel_estimator_test_pilots12.dat"}, {"test_data/port_channel_estimator_test_output_ch_est12.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0132, 2.0319, 20, 18.1287, 0.0078133, 0.21159, 105, 111.1101, {"test_data/port_channel_estimator_test_input_rg13.dat"}, {"test_data/port_channel_estimator_test_pilots13.dat"}, {"test_data/port_channel_estimator_test_output_ch_est13.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0212, 2.0234, 20, 13.1433, 0.02482, 0.94401, -195, -201.9109, {"test_data/port_channel_estimator_test_input_rg14.dat"}, {"test_data/port_channel_estimator_test_pilots14.dat"}, {"test_data/port_channel_estimator_test_output_ch_est14.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0008, 1.9944, 20, 16.2954, 0.011771, 0.52897, 405, 398.5172, {"test_data/port_channel_estimator_test_input_rg15.dat"}, {"test_data/port_channel_estimator_test_pilots15.dat"}, {"test_data/port_channel_estimator_test_output_ch_est15.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.017, 2.0011, 20, 20.2926, 0.0094503, 0.31738, 0, -17.0273, {"test_data/port_channel_estimator_test_input_rg16.dat"}, {"test_data/port_channel_estimator_test_pilots16.dat"}, {"test_data/port_channel_estimator_test_output_ch_est16.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9888, 1.9859, 20, 19.2438, 0.011864, 0.21973, 210, 229.3464, {"test_data/port_channel_estimator_test_input_rg17.dat"}, {"test_data/port_channel_estimator_test_pilots17.dat"}, {"test_data/port_channel_estimator_test_output_ch_est17.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0201, 2.0153, 20, 20.4118, 0.0092088, 0.21973, -390, -374.4387, {"test_data/port_channel_estimator_test_input_rg18.dat"}, {"test_data/port_channel_estimator_test_pilots18.dat"}, {"test_data/port_channel_estimator_test_output_ch_est18.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0447, 2.0202, 20, 20.442, 0.0092562, 0.37435, 810, 810.3721, {"test_data/port_channel_estimator_test_input_rg19.dat"}, {"test_data/port_channel_estimator_test_pilots19.dat"}, {"test_data/port_channel_estimator_test_output_ch_est19.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0306, 2.0103, 20, 20.8674, 0.0083347, 0.31331, 0, 9.3824, {"test_data/port_channel_estimator_test_input_rg20.dat"}, {"test_data/port_channel_estimator_test_pilots20.dat"}, {"test_data/port_channel_estimator_test_output_ch_est20.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0346, 2.0239, 20, 20.7733, 0.0085341, 0.26855, 210, 220.9086, {"test_data/port_channel_estimator_test_input_rg21.dat"}, {"test_data/port_channel_estimator_test_pilots21.dat"}, {"test_data/port_channel_estimator_test_output_ch_est21.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.063, 2.029, 20, 20.2597, 0.0097393, 0.53711, -390, -374.049, {"test_data/port_channel_estimator_test_input_rg22.dat"}, {"test_data/port_channel_estimator_test_pilots22.dat"}, {"test_data/port_channel_estimator_test_output_ch_est22.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.989, 1.9895, 20, 20.6267, 0.0086292, 0.16276, 810, 830.516, {"test_data/port_channel_estimator_test_input_rg23.dat"}, {"test_data/port_channel_estimator_test_pilots23.dat"}, {"test_data/port_channel_estimator_test_output_ch_est23.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0179, 2.0153, 20, 12.9705, 0.025742, 0.48014, 0, -5.0197, {"test_data/port_channel_estimator_test_input_rg24.dat"}, {"test_data/port_channel_estimator_test_pilots24.dat"}, {"test_data/port_channel_estimator_test_output_ch_est24.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99806, 1.9929, 20, 16.5502, 0.01107, 0.21159, 210, 200.5906, {"test_data/port_channel_estimator_test_input_rg25.dat"}, {"test_data/port_channel_estimator_test_pilots25.dat"}, {"test_data/port_channel_estimator_test_output_ch_est25.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0061, 1.9915, 20, 12.4918, 0.02841, 0.52897, -390, -378.783, {"test_data/port_channel_estimator_test_input_rg26.dat"}, {"test_data/port_channel_estimator_test_pilots26.dat"}, {"test_data/port_channel_estimator_test_output_ch_est26.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0021, 1.9869, 20, 12.9362, 0.025544, 0.47201, 810, 836.1549, {"test_data/port_channel_estimator_test_input_rg27.dat"}, {"test_data/port_channel_estimator_test_pilots27.dat"}, {"test_data/port_channel_estimator_test_output_ch_est27.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0146, 2.0154, 20, 15.383, 0.014723, 0.31738, 0, 4.8712, {"test_data/port_channel_estimator_test_input_rg28.dat"}, {"test_data/port_channel_estimator_test_pilots28.dat"}, {"test_data/port_channel_estimator_test_output_ch_est28.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0103, 2.0212, 20, 17.5794, 0.008841, 0.15869, 210, 252.4566, {"test_data/port_channel_estimator_test_input_rg29.dat"}, {"test_data/port_channel_estimator_test_pilots29.dat"}, {"test_data/port_channel_estimator_test_output_ch_est29.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.007, 1.9944, 20, 12.5235, 0.028228, 0.53304, -390, -398.3374, {"test_data/port_channel_estimator_test_input_rg30.dat"}, {"test_data/port_channel_estimator_test_pilots30.dat"}, {"test_data/port_channel_estimator_test_output_ch_est30.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0134, 2.0062, 20, 13.7126, 0.021603, 0.43132, 810, 836.3236, {"test_data/port_channel_estimator_test_input_rg31.dat"}, {"test_data/port_channel_estimator_test_pilots31.dat"}, {"test_data/port_channel_estimator_test_output_ch_est31.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0038, 2.0119, 20, 20.7694, 0.0084125, 0.11393, 0, 1.6931, {"test_data/port_channel_estimator_test_input_rg32.dat"}, {"test_data/port_channel_estimator_test_pilots32.dat"}, {"test_data/port_channel_estimator_test_output_ch_est32.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0212, 2.0175, 20, 19.8405, 0.010509, 0.42318, 105, 106.4187, {"test_data/port_channel_estimator_test_input_rg33.dat"}, {"test_data/port_channel_estimator_test_pilots33.dat"}, {"test_data/port_channel_estimator_test_output_ch_est33.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.001, 1.996, 20, 20.2297, 0.0095118, 0.42318, -195, -193.2839, {"test_data/port_channel_estimator_test_input_rg34.dat"}, {"test_data/port_channel_estimator_test_pilots34.dat"}, {"test_data/port_channel_estimator_test_output_ch_est34.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0484, 2.016, 20, 20.2213, 0.0097561, 0.96029, 405, 402.6131, {"test_data/port_channel_estimator_test_input_rg35.dat"}, {"test_data/port_channel_estimator_test_pilots35.dat"}, {"test_data/port_channel_estimator_test_output_ch_est35.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0183, 1.9932, 20, 20.5556, 0.0089009, 0.7487, 0, -1.4062, {"test_data/port_channel_estimator_test_input_rg36.dat"}, {"test_data/port_channel_estimator_test_pilots36.dat"}, {"test_data/port_channel_estimator_test_output_ch_est36.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0038, 2.0121, 20, 20.3395, 0.0092877, 0.11393, 105, 105.5521, {"test_data/port_channel_estimator_test_input_rg37.dat"}, {"test_data/port_channel_estimator_test_pilots37.dat"}, {"test_data/port_channel_estimator_test_output_ch_est37.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0362, 2.0114, 20, 20.4369, 0.0092285, 0.7487, -195, -196.5362, {"test_data/port_channel_estimator_test_input_rg38.dat"}, {"test_data/port_channel_estimator_test_pilots38.dat"}, {"test_data/port_channel_estimator_test_output_ch_est38.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0309, 1.9987, 20, 20.5215, 0.0090266, 1.0742, 405, 401.7412, {"test_data/port_channel_estimator_test_input_rg39.dat"}, {"test_data/port_channel_estimator_test_pilots39.dat"}, {"test_data/port_channel_estimator_test_output_ch_est39.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.007, 2.0073, 20, 15.6407, 0.013771, 0.53711, 0, 4.4858, {"test_data/port_channel_estimator_test_input_rg40.dat"}, {"test_data/port_channel_estimator_test_pilots40.dat"}, {"test_data/port_channel_estimator_test_output_ch_est40.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0105, 2.0046, 20, 12.9773, 0.025515, 0.96029, 105, 111.2882, {"test_data/port_channel_estimator_test_input_rg41.dat"}, {"test_data/port_channel_estimator_test_pilots41.dat"}, {"test_data/port_channel_estimator_test_output_ch_est41.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0103, 2.008, 20, 12.405, 0.029105, 1.0742, -195, -198.7585, {"test_data/port_channel_estimator_test_input_rg42.dat"}, {"test_data/port_channel_estimator_test_pilots42.dat"}, {"test_data/port_channel_estimator_test_output_ch_est42.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0087, 2.0155, 20, 16.236, 0.012027, 0.42318, 405, 403.6036, {"test_data/port_channel_estimator_test_input_rg43.dat"}, {"test_data/port_channel_estimator_test_pilots43.dat"}, {"test_data/port_channel_estimator_test_output_ch_est43.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0164, 2.0176, 20, 14.4516, 0.018277, 0.7487, 0, 1.7794, {"test_data/port_channel_estimator_test_input_rg44.dat"}, {"test_data/port_channel_estimator_test_pilots44.dat"}, {"test_data/port_channel_estimator_test_output_ch_est44.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0096, 2.003, 20, 13.0485, 0.025079, 0.96029, 105, 104.7113, {"test_data/port_channel_estimator_test_input_rg45.dat"}, {"test_data/port_channel_estimator_test_pilots45.dat"}, {"test_data/port_channel_estimator_test_output_ch_est45.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0006, 2.0094, 20, 17.9804, 0.0079837, 0, -195, -197.9146, {"test_data/port_channel_estimator_test_input_rg46.dat"}, {"test_data/port_channel_estimator_test_pilots46.dat"}, {"test_data/port_channel_estimator_test_output_ch_est46.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0052, 1.995, 20, 14.5606, 0.017628, 0.7487, 405, 404.8838, {"test_data/port_channel_estimator_test_input_rg47.dat"}, {"test_data/port_channel_estimator_test_pilots47.dat"}, {"test_data/port_channel_estimator_test_output_ch_est47.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0146, 2.0039, 20, 20.1084, 0.0098478, 0.26855, 0, 8.1284, {"test_data/port_channel_estimator_test_input_rg48.dat"}, {"test_data/port_channel_estimator_test_pilots48.dat"}, {"test_data/port_channel_estimator_test_output_ch_est48.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9964, 2.0069, 20, 20.1945, 0.0095675, 0, 210, 206.2988, {"test_data/port_channel_estimator_test_input_rg49.dat"}, {"test_data/port_channel_estimator_test_pilots49.dat"}, {"test_data/port_channel_estimator_test_output_ch_est49.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0216, 2.0108, 20, 19.9654, 0.010213, 0.26855, -390, -400.1731, {"test_data/port_channel_estimator_test_input_rg50.dat"}, {"test_data/port_channel_estimator_test_pilots50.dat"}, {"test_data/port_channel_estimator_test_output_ch_est50.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0382, 2.0086, 20, 20.4107, 0.0092936, 0.42318, 810, 809.4031, {"test_data/port_channel_estimator_test_input_rg51.dat"}, {"test_data/port_channel_estimator_test_pilots51.dat"}, {"test_data/port_channel_estimator_test_output_ch_est51.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0263, 2.0017, 20, 20.5308, 0.0089869, 0.37435, 0, 4.6394, {"test_data/port_channel_estimator_test_input_rg52.dat"}, {"test_data/port_channel_estimator_test_pilots52.dat"}, {"test_data/port_channel_estimator_test_output_ch_est52.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9953, 2.003, 20, 20.56, 0.0087901, 0.052897, 210, 210.8737, {"test_data/port_channel_estimator_test_input_rg53.dat"}, {"test_data/port_channel_estimator_test_pilots53.dat"}, {"test_data/port_channel_estimator_test_output_ch_est53.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9935, 1.9947, 20, 20.3803, 0.0091538, 0.16276, -390, -389.5946, {"test_data/port_channel_estimator_test_input_rg54.dat"}, {"test_data/port_channel_estimator_test_pilots54.dat"}, {"test_data/port_channel_estimator_test_output_ch_est54.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0005, 2.0022, 20, 20.2665, 0.0094294, 0.16276, 810, 807.7632, {"test_data/port_channel_estimator_test_input_rg55.dat"}, {"test_data/port_channel_estimator_test_pilots55.dat"}, {"test_data/port_channel_estimator_test_output_ch_est55.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99962, 2.0057, 20, 17.1686, 0.0096155, 0.10579, 0, 6.8796, {"test_data/port_channel_estimator_test_input_rg56.dat"}, {"test_data/port_channel_estimator_test_pilots56.dat"}, {"test_data/port_channel_estimator_test_output_ch_est56.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0071, 2.0078, 20, 15.5407, 0.014093, 0.26855, 210, 219.5193, {"test_data/port_channel_estimator_test_input_rg57.dat"}, {"test_data/port_channel_estimator_test_pilots57.dat"}, {"test_data/port_channel_estimator_test_output_ch_est57.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0104, 2.0091, 20, 12.2758, 0.029986, 0.53711, -390, -398.0952, {"test_data/port_channel_estimator_test_input_rg58.dat"}, {"test_data/port_channel_estimator_test_pilots58.dat"}, {"test_data/port_channel_estimator_test_output_ch_est58.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99882, 1.9962, 20, 16.1163, 0.012242, 0.21159, 810, 805.359, {"test_data/port_channel_estimator_test_input_rg59.dat"}, {"test_data/port_channel_estimator_test_pilots59.dat"}, {"test_data/port_channel_estimator_test_output_ch_est59.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0053, 1.9935, 20, 13.8552, 0.020739, 0.43132, 0, -3.0987, {"test_data/port_channel_estimator_test_input_rg60.dat"}, {"test_data/port_channel_estimator_test_pilots60.dat"}, {"test_data/port_channel_estimator_test_output_ch_est60.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99805, 2.0047, 20, 17.8116, 0.0082793, 0, 210, 213.2994, {"test_data/port_channel_estimator_test_input_rg61.dat"}, {"test_data/port_channel_estimator_test_pilots61.dat"}, {"test_data/port_channel_estimator_test_output_ch_est61.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0096, 2.0021, 20, 13.1912, 0.024268, 0.48014, -390, -390.3443, {"test_data/port_channel_estimator_test_input_rg62.dat"}, {"test_data/port_channel_estimator_test_pilots62.dat"}, {"test_data/port_channel_estimator_test_output_ch_est62.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0111, 2.0052, 20, 13.7728, 0.021258, 0.42725, 810, 805.9962, {"test_data/port_channel_estimator_test_input_rg63.dat"}, {"test_data/port_channel_estimator_test_pilots63.dat"}, {"test_data/port_channel_estimator_test_output_ch_est63.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0093, 1.9983, 20, 20.3039, 0.00939, 0.53711, 0, -4.4741, {"test_data/port_channel_estimator_test_input_rg64.dat"}, {"test_data/port_channel_estimator_test_pilots64.dat"}, {"test_data/port_channel_estimator_test_output_ch_est64.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0044, 2.0104, 20, 20.1176, 0.0097773, 0.21159, 105, 107.6748, {"test_data/port_channel_estimator_test_input_rg65.dat"}, {"test_data/port_channel_estimator_test_pilots65.dat"}, {"test_data/port_channel_estimator_test_output_ch_est65.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0323, 2.0031, 20, 20.179, 0.0097743, 0.86263, -195, -193.9426, {"test_data/port_channel_estimator_test_input_rg66.dat"}, {"test_data/port_channel_estimator_test_pilots66.dat"}, {"test_data/port_channel_estimator_test_output_ch_est66.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9913, 1.997, 20, 20.2359, 0.0094526, 0.21159, 405, 410.2316, {"test_data/port_channel_estimator_test_input_rg67.dat"}, {"test_data/port_channel_estimator_test_pilots67.dat"}, {"test_data/port_channel_estimator_test_output_ch_est67.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0122, 2.0225, 20, 16.3163, 0.011848, 0.42318, 0, 2.1684, {"test_data/port_channel_estimator_test_input_rg68.dat"}, {"test_data/port_channel_estimator_test_pilots68.dat"}, {"test_data/port_channel_estimator_test_output_ch_est68.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0123, 2.008, 20, 13.6602, 0.021842, 0.86263, 105, 106.6012, {"test_data/port_channel_estimator_test_input_rg69.dat"}, {"test_data/port_channel_estimator_test_pilots69.dat"}, {"test_data/port_channel_estimator_test_output_ch_est69.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0037, 2.0141, 20, 16.9504, 0.010152, 0.21159, -195, -192.9149, {"test_data/port_channel_estimator_test_input_rg70.dat"}, {"test_data/port_channel_estimator_test_pilots70.dat"}, {"test_data/port_channel_estimator_test_output_ch_est70.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99749, 2.0031, 20, 17.4581, 0.0089763, 0.11393, 405, 405.0477, {"test_data/port_channel_estimator_test_input_rg71.dat"}, {"test_data/port_channel_estimator_test_pilots71.dat"}, {"test_data/port_channel_estimator_test_output_ch_est71.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0015, 1.9976, 20, 20.0406, 0.0099379, 0.21159, 0, 2.2183, {"test_data/port_channel_estimator_test_input_rg72.dat"}, {"test_data/port_channel_estimator_test_pilots72.dat"}, {"test_data/port_channel_estimator_test_output_ch_est72.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0235, 1.999, 20, 20.2695, 0.0095312, 0.37435, 210, 209.3828, {"test_data/port_channel_estimator_test_input_rg73.dat"}, {"test_data/port_channel_estimator_test_pilots73.dat"}, {"test_data/port_channel_estimator_test_output_ch_est73.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0345, 2.0101, 20, 20.2588, 0.0096068, 0.37435, -390, -389.2798, {"test_data/port_channel_estimator_test_input_rg74.dat"}, {"test_data/port_channel_estimator_test_pilots74.dat"}, {"test_data/port_channel_estimator_test_output_ch_est74.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0276, 1.996, 20, 20.0729, 0.0099931, 0.53711, 810, 817.1202, {"test_data/port_channel_estimator_test_input_rg75.dat"}, {"test_data/port_channel_estimator_test_pilots75.dat"}, {"test_data/port_channel_estimator_test_output_ch_est75.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0001, 2.0031, 20, 16.6578, 0.010821, 0.16276, 0, -0.48112, {"test_data/port_channel_estimator_test_input_rg76.dat"}, {"test_data/port_channel_estimator_test_pilots76.dat"}, {"test_data/port_channel_estimator_test_output_ch_est76.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0064, 2.0108, 20, 16.3604, 0.011661, 0.21159, 210, 203.4704, {"test_data/port_channel_estimator_test_input_rg77.dat"}, {"test_data/port_channel_estimator_test_pilots77.dat"}, {"test_data/port_channel_estimator_test_output_ch_est77.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0048, 2.0029, 20, 15.7426, 0.013422, 0.26855, -390, -398.2432, {"test_data/port_channel_estimator_test_input_rg78.dat"}, {"test_data/port_channel_estimator_test_pilots78.dat"}, {"test_data/port_channel_estimator_test_output_ch_est78.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0121, 2.0114, 20, 12.4467, 0.028876, 0.53711, 810, 813.8512, {"test_data/port_channel_estimator_test_input_rg79.dat"}, {"test_data/port_channel_estimator_test_pilots79.dat"}, {"test_data/port_channel_estimator_test_output_ch_est79.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 1.036, 1.0639, 20, 15.5145, 0.029101, 0, 0, -57.6539, {"test_data/port_channel_estimator_test_input_rg80.dat"}, {"test_data/port_channel_estimator_test_pilots80.dat"}, {"test_data/port_channel_estimator_test_output_ch_est80.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 1.0114, 1.0331, 20, 16.4957, 0.022664, 0, 105, 74.9316, {"test_data/port_channel_estimator_test_input_rg81.dat"}, {"test_data/port_channel_estimator_test_pilots81.dat"}, {"test_data/port_channel_estimator_test_output_ch_est81.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 1.0066, 1.0212, 20, 18.191, 0.015267, 0, -195, -181.1274, {"test_data/port_channel_estimator_test_input_rg82.dat"}, {"test_data/port_channel_estimator_test_pilots82.dat"}, {"test_data/port_channel_estimator_test_output_ch_est82.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.9809, 1.0315, 20, 12.6913, 0.052782, 0, 405, 369.9909, {"test_data/port_channel_estimator_test_input_rg83.dat"}, {"test_data/port_channel_estimator_test_pilots83.dat"}, {"test_data/port_channel_estimator_test_output_ch_est83.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.99448, 1.0071, 20, 18.7805, 0.013169, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg84.dat"}, {"test_data/port_channel_estimator_test_pilots84.dat"}, {"test_data/port_channel_estimator_test_output_ch_est84.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.94531, 0.97697, 20, 14.5664, 0.033032, 0, 105, {}, {"test_data/port_channel_estimator_test_input_rg85.dat"}, {"test_data/port_channel_estimator_test_pilots85.dat"}, {"test_data/port_channel_estimator_test_output_ch_est85.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.88628, 0.96498, 20, 10.3312, 0.082121, 0, -195, {}, {"test_data/port_channel_estimator_test_input_rg86.dat"}, {"test_data/port_channel_estimator_test_pilots86.dat"}, {"test_data/port_channel_estimator_test_output_ch_est86.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.97633, 0.98423, 20, 20.7382, 0.0082372, 0, 405, {}, {"test_data/port_channel_estimator_test_input_rg87.dat"}, {"test_data/port_channel_estimator_test_pilots87.dat"}, {"test_data/port_channel_estimator_test_output_ch_est87.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.94401, 0.96388, 20, 16.5832, 0.020733, 0, 0, 26.9056, {"test_data/port_channel_estimator_test_input_rg88.dat"}, {"test_data/port_channel_estimator_test_pilots88.dat"}, {"test_data/port_channel_estimator_test_output_ch_est88.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.90138, 0.99189, 20, 9.7971, 0.094449, 0, 210, 97.2533, {"test_data/port_channel_estimator_test_input_rg89.dat"}, {"test_data/port_channel_estimator_test_pilots89.dat"}, {"test_data/port_channel_estimator_test_output_ch_est89.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 1.0237, 1.0378, 20, 18.4393, 0.014664, 0, -390, -390.2024, {"test_data/port_channel_estimator_test_input_rg90.dat"}, {"test_data/port_channel_estimator_test_pilots90.dat"}, {"test_data/port_channel_estimator_test_output_ch_est90.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.92397, 0.9849, 20, 11.623, 0.063585, 0, 810, 966.3219, {"test_data/port_channel_estimator_test_input_rg91.dat"}, {"test_data/port_channel_estimator_test_pilots91.dat"}, {"test_data/port_channel_estimator_test_output_ch_est91.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.91226, 1.0188, 20, 9.1431, 0.11112, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg92.dat"}, {"test_data/port_channel_estimator_test_pilots92.dat"}, {"test_data/port_channel_estimator_test_output_ch_est92.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 1.0022, 1.0158, 20, 18.4882, 0.014195, 0, 210, {}, {"test_data/port_channel_estimator_test_input_rg93.dat"}, {"test_data/port_channel_estimator_test_pilots93.dat"}, {"test_data/port_channel_estimator_test_output_ch_est93.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.9556, 0.97938, 20, 15.8555, 0.024816, 0, -390, {}, {"test_data/port_channel_estimator_test_input_rg94.dat"}, {"test_data/port_channel_estimator_test_pilots94.dat"}, {"test_data/port_channel_estimator_test_output_ch_est94.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.95983, 0.96591, 20, 21.7939, 0.0063504, 0, 810, {}, {"test_data/port_channel_estimator_test_input_rg95.dat"}, {"test_data/port_channel_estimator_test_pilots95.dat"}, {"test_data/port_channel_estimator_test_output_ch_est95.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.96752, 1.0214, 20, 12.4923, 0.054504, 0, 0, 8.4564, {"test_data/port_channel_estimator_test_input_rg96.dat"}, {"test_data/port_channel_estimator_test_pilots96.dat"}, {"test_data/port_channel_estimator_test_output_ch_est96.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.96516, 1.0045, 20, 13.8475, 0.039797, 0, 105, 119.8632, {"test_data/port_channel_estimator_test_input_rg97.dat"}, {"test_data/port_channel_estimator_test_pilots97.dat"}, {"test_data/port_channel_estimator_test_output_ch_est97.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.83847, 1.0238, 20, 6.5039, 0.18754, 0, -195, -191.2821, {"test_data/port_channel_estimator_test_input_rg98.dat"}, {"test_data/port_channel_estimator_test_pilots98.dat"}, {"test_data/port_channel_estimator_test_output_ch_est98.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.54985, 1.0188, 20, 0.63923, 0.47459, 0, 405, 365.5569, {"test_data/port_channel_estimator_test_input_rg99.dat"}, {"test_data/port_channel_estimator_test_pilots99.dat"}, {"test_data/port_channel_estimator_test_output_ch_est99.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.95118, 1.0029, 20, 12.5955, 0.052325, 0, 0, 16.462, {"test_data/port_channel_estimator_test_input_rg100.dat"}, {"test_data/port_channel_estimator_test_pilots100.dat"}, {"test_data/port_channel_estimator_test_output_ch_est100.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.95016, 1.0011, 20, 12.6587, 0.051514, 0, 105, 129.1171, {"test_data/port_channel_estimator_test_input_rg101.dat"}, {"test_data/port_channel_estimator_test_pilots101.dat"}, {"test_data/port_channel_estimator_test_output_ch_est101.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.94229, 1.0238, 20, 10.5798, 0.082453, 0, -195, -215.6071, {"test_data/port_channel_estimator_test_input_rg102.dat"}, {"test_data/port_channel_estimator_test_pilots102.dat"}, {"test_data/port_channel_estimator_test_output_ch_est102.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.87535, 1.0233, 20, 7.6699, 0.14969, 0, 405, 443.4122, {"test_data/port_channel_estimator_test_input_rg103.dat"}, {"test_data/port_channel_estimator_test_pilots103.dat"}, {"test_data/port_channel_estimator_test_output_ch_est103.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 1.0037, 1.0435, 20, 13.9644, 0.040286, 0, 0, 21.1529, {"test_data/port_channel_estimator_test_input_rg104.dat"}, {"test_data/port_channel_estimator_test_pilots104.dat"}, {"test_data/port_channel_estimator_test_output_ch_est104.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.90449, 1.0039, 20, 9.5387, 0.10058, 0, 210, 173.6429, {"test_data/port_channel_estimator_test_input_rg105.dat"}, {"test_data/port_channel_estimator_test_pilots105.dat"}, {"test_data/port_channel_estimator_test_output_ch_est105.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.87126, 1.0044, 20, 8.1071, 0.13472, 0, -390, -340.595, {"test_data/port_channel_estimator_test_input_rg106.dat"}, {"test_data/port_channel_estimator_test_pilots106.dat"}, {"test_data/port_channel_estimator_test_output_ch_est106.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.51688, 1.0014, 20, 0.22865, 0.49037, 0, 810, 792.0087, {"test_data/port_channel_estimator_test_input_rg107.dat"}, {"test_data/port_channel_estimator_test_pilots107.dat"}, {"test_data/port_channel_estimator_test_output_ch_est107.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.94076, 1.0309, 20, 10.1328, 0.091244, 0, 0, -6.2647, {"test_data/port_channel_estimator_test_input_rg108.dat"}, {"test_data/port_channel_estimator_test_pilots108.dat"}, {"test_data/port_channel_estimator_test_output_ch_est108.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.97656, 1.0062, 20, 15.1238, 0.030014, 0, 210, 235.6151, {"test_data/port_channel_estimator_test_input_rg109.dat"}, {"test_data/port_channel_estimator_test_pilots109.dat"}, {"test_data/port_channel_estimator_test_output_ch_est109.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.87337, 0.99249, 20, 8.6001, 0.12056, 0, -390, -481.6069, {"test_data/port_channel_estimator_test_input_rg110.dat"}, {"test_data/port_channel_estimator_test_pilots110.dat"}, {"test_data/port_channel_estimator_test_output_ch_est110.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.86307, 1.0034, 20, 7.8372, 0.14201, 0, 810, 819.5106, {"test_data/port_channel_estimator_test_input_rg111.dat"}, {"test_data/port_channel_estimator_test_pilots111.dat"}, {"test_data/port_channel_estimator_test_output_ch_est111.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.95828, 0.9892, 20, 21.7989, 0.0063329, 0.63477, 0, -35.0401, {"test_data/port_channel_estimator_test_input_rg112.dat"}, {"test_data/port_channel_estimator_test_pilots112.dat"}, {"test_data/port_channel_estimator_test_output_ch_est112.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0273, 1.0279, 20, 22.9707, 0.0051838, 0.30924, 105, 17.4341, {"test_data/port_channel_estimator_test_input_rg113.dat"}, {"test_data/port_channel_estimator_test_pilots113.dat"}, {"test_data/port_channel_estimator_test_output_ch_est113.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.93904, 0.95096, 20, 25.7787, 0.0024821, 0.39062, -195, -181.1401, {"test_data/port_channel_estimator_test_input_rg114.dat"}, {"test_data/port_channel_estimator_test_pilots114.dat"}, {"test_data/port_channel_estimator_test_output_ch_est114.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.971, 0.98565, 20, 20.4673, 0.0087196, 0.29297, 405, 333.9925, {"test_data/port_channel_estimator_test_input_rg115.dat"}, {"test_data/port_channel_estimator_test_pilots115.dat"}, {"test_data/port_channel_estimator_test_output_ch_est115.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0101, 1.0377, 20, 27.0496, 0.0019926, 0.71615, 0, {}, {"test_data/port_channel_estimator_test_input_rg116.dat"}, {"test_data/port_channel_estimator_test_pilots116.dat"}, {"test_data/port_channel_estimator_test_output_ch_est116.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.93039, 0.93991, 20, 20.2846, 0.0087137, 0.20345, 105, {}, {"test_data/port_channel_estimator_test_input_rg117.dat"}, {"test_data/port_channel_estimator_test_pilots117.dat"}, {"test_data/port_channel_estimator_test_output_ch_est117.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.96018, 0.96403, 20, 22.4164, 0.0055044, 0.016276, -195, {}, {"test_data/port_channel_estimator_test_input_rg118.dat"}, {"test_data/port_channel_estimator_test_pilots118.dat"}, {"test_data/port_channel_estimator_test_output_ch_est118.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.036, 1.0564, 20, 22.3266, 0.006063, 0.61035, 405, {}, {"test_data/port_channel_estimator_test_input_rg119.dat"}, {"test_data/port_channel_estimator_test_pilots119.dat"}, {"test_data/port_channel_estimator_test_output_ch_est119.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0562, 1.0664, 20, 23.02, 0.0052694, 0.016276, 0, -32.5732, {"test_data/port_channel_estimator_test_input_rg120.dat"}, {"test_data/port_channel_estimator_test_pilots120.dat"}, {"test_data/port_channel_estimator_test_output_ch_est120.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.94022, 0.9724, 20, 18.9016, 0.012108, 0.27669, 210, 263.748, {"test_data/port_channel_estimator_test_input_rg121.dat"}, {"test_data/port_channel_estimator_test_pilots121.dat"}, {"test_data/port_channel_estimator_test_output_ch_est121.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.97346, 1.0203, 20, 19.6209, 0.010622, 0.42318, -390, -342.9196, {"test_data/port_channel_estimator_test_input_rg122.dat"}, {"test_data/port_channel_estimator_test_pilots122.dat"}, {"test_data/port_channel_estimator_test_output_ch_est122.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.97578, 1.02, 20, 21.4804, 0.0069392, 0.43945, 810, 773.7149, {"test_data/port_channel_estimator_test_input_rg123.dat"}, {"test_data/port_channel_estimator_test_pilots123.dat"}, {"test_data/port_channel_estimator_test_output_ch_est123.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0179, 1.0449, 20, 24.3232, 0.0037617, 0.43132, 0, {}, {"test_data/port_channel_estimator_test_input_rg124.dat"}, {"test_data/port_channel_estimator_test_pilots124.dat"}, {"test_data/port_channel_estimator_test_output_ch_est124.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.95109, 0.97176, 20, 21.6164, 0.0065551, 0.26855, 210, {}, {"test_data/port_channel_estimator_test_input_rg125.dat"}, {"test_data/port_channel_estimator_test_pilots125.dat"}, {"test_data/port_channel_estimator_test_output_ch_est125.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.94419, 0.95256, 20, 26.7638, 0.0019892, -0.073242, -390, {}, {"test_data/port_channel_estimator_test_input_rg126.dat"}, {"test_data/port_channel_estimator_test_pilots126.dat"}, {"test_data/port_channel_estimator_test_output_ch_est126.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0248, 1.0216, 20, 20.9203, 0.0082912, 0.028483, 810, {}, {"test_data/port_channel_estimator_test_input_rg127.dat"}, {"test_data/port_channel_estimator_test_pilots127.dat"}, {"test_data/port_channel_estimator_test_output_ch_est127.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.032, 1.0239, 20, 22.4782, 0.0058327, 0.32552, 0, {}, {"test_data/port_channel_estimator_test_input_rg128.dat"}, {"test_data/port_channel_estimator_test_pilots128.dat"}, {"test_data/port_channel_estimator_test_output_ch_est128.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.135, 1.002, 20, 19.1783, 0.013714, 1.0742, 105, {}, {"test_data/port_channel_estimator_test_input_rg129.dat"}, {"test_data/port_channel_estimator_test_pilots129.dat"}, {"test_data/port_channel_estimator_test_output_ch_est129.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99858, 0.97924, 20, 21.9513, 0.0063716, 0.42318, -195, {}, {"test_data/port_channel_estimator_test_input_rg130.dat"}, {"test_data/port_channel_estimator_test_pilots130.dat"}, {"test_data/port_channel_estimator_test_output_ch_est130.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1583, 1.0437, 20, 19.3618, 0.013417, 0.96029, 405, {}, {"test_data/port_channel_estimator_test_input_rg131.dat"}, {"test_data/port_channel_estimator_test_pilots131.dat"}, {"test_data/port_channel_estimator_test_output_ch_est131.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1664, 1.0468, 20, 20.6572, 0.010026, 0.48014, 0, {}, {"test_data/port_channel_estimator_test_input_rg132.dat"}, {"test_data/port_channel_estimator_test_pilots132.dat"}, {"test_data/port_channel_estimator_test_output_ch_est132.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0375, 0.99824, 20, 21.7233, 0.006977, 0.26042, 210, {}, {"test_data/port_channel_estimator_test_input_rg133.dat"}, {"test_data/port_channel_estimator_test_pilots133.dat"}, {"test_data/port_channel_estimator_test_output_ch_est133.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.15, 1.0369, 20, 19.9031, 0.011759, 0.48014, -390, {}, {"test_data/port_channel_estimator_test_input_rg134.dat"}, {"test_data/port_channel_estimator_test_pilots134.dat"}, {"test_data/port_channel_estimator_test_output_ch_est134.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.059, 1.0491, 20, 20.6011, 0.0092209, 0.16276, 810, {}, {"test_data/port_channel_estimator_test_input_rg135.dat"}, {"test_data/port_channel_estimator_test_pilots135.dat"}, {"test_data/port_channel_estimator_test_output_ch_est135.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0065, 1.0079, 20, 21.1655, 0.0076964, 0.21159, 0, 8.2796, {"test_data/port_channel_estimator_test_input_rg136.dat"}, {"test_data/port_channel_estimator_test_pilots136.dat"}, {"test_data/port_channel_estimator_test_output_ch_est136.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0005, 1.0026, 20, 20.0925, 0.0097945, 0.21159, 105, 78.1284, {"test_data/port_channel_estimator_test_input_rg137.dat"}, {"test_data/port_channel_estimator_test_pilots137.dat"}, {"test_data/port_channel_estimator_test_output_ch_est137.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1037, 1.0092, 20, 20.3212, 0.01025, 0.86263, -195, -185.3494, {"test_data/port_channel_estimator_test_input_rg138.dat"}, {"test_data/port_channel_estimator_test_pilots138.dat"}, {"test_data/port_channel_estimator_test_output_ch_est138.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1429, 1.0262, 20, 19.7889, 0.011999, 0.96029, 405, 373.5717, {"test_data/port_channel_estimator_test_input_rg139.dat"}, {"test_data/port_channel_estimator_test_pilots139.dat"}, {"test_data/port_channel_estimator_test_output_ch_est139.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0834, 1.0073, 20, 20.7659, 0.0090828, 0.7487, 0, {}, {"test_data/port_channel_estimator_test_input_rg140.dat"}, {"test_data/port_channel_estimator_test_pilots140.dat"}, {"test_data/port_channel_estimator_test_output_ch_est140.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.98909, 0.99364, 20, 21.5552, 0.0069137, 0.11393, 105, {}, {"test_data/port_channel_estimator_test_input_rg141.dat"}, {"test_data/port_channel_estimator_test_pilots141.dat"}, {"test_data/port_channel_estimator_test_output_ch_est141.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0125, 1.0187, 20, 21.979, 0.0064195, 0, -195, {}, {"test_data/port_channel_estimator_test_input_rg142.dat"}, {"test_data/port_channel_estimator_test_pilots142.dat"}, {"test_data/port_channel_estimator_test_output_ch_est142.dat"}}, - {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.03, 1.0279, 20, 22.5348, 0.0057461, 0.21159, 405, {}, {"test_data/port_channel_estimator_test_input_rg143.dat"}, {"test_data/port_channel_estimator_test_pilots143.dat"}, {"test_data/port_channel_estimator_test_output_ch_est143.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1555, 1.0204, 20, 19.3109, 0.013542, 0.53711, 0, -38.1748, {"test_data/port_channel_estimator_test_input_rg144.dat"}, {"test_data/port_channel_estimator_test_pilots144.dat"}, {"test_data/port_channel_estimator_test_output_ch_est144.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1446, 1.0101, 20, 19.5974, 0.012558, 0.53711, 210, 207.7111, {"test_data/port_channel_estimator_test_input_rg145.dat"}, {"test_data/port_channel_estimator_test_pilots145.dat"}, {"test_data/port_channel_estimator_test_output_ch_est145.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0118, 1.0174, 20, 20.57, 0.0088731, 0.056966, -390, -416.6302, {"test_data/port_channel_estimator_test_input_rg146.dat"}, {"test_data/port_channel_estimator_test_pilots146.dat"}, {"test_data/port_channel_estimator_test_output_ch_est146.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0731, 1.0002, 20, 20.3472, 0.0099068, 0.37435, 810, 782.1602, {"test_data/port_channel_estimator_test_input_rg147.dat"}, {"test_data/port_channel_estimator_test_pilots147.dat"}, {"test_data/port_channel_estimator_test_output_ch_est147.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0228, 1.0297, 20, 21.7985, 0.0067602, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg148.dat"}, {"test_data/port_channel_estimator_test_pilots148.dat"}, {"test_data/port_channel_estimator_test_output_ch_est148.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1428, 1.0255, 20, 20.3995, 0.010423, 0.48014, 210, {}, {"test_data/port_channel_estimator_test_input_rg149.dat"}, {"test_data/port_channel_estimator_test_pilots149.dat"}, {"test_data/port_channel_estimator_test_output_ch_est149.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0296, 1.0277, 20, 22.2898, 0.0060768, 0.10579, -390, {}, {"test_data/port_channel_estimator_test_input_rg150.dat"}, {"test_data/port_channel_estimator_test_pilots150.dat"}, {"test_data/port_channel_estimator_test_output_ch_est150.dat"}}, - {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0302, 1.0076, 20, 22.2011, 0.0062061, 0.21159, 810, {}, {"test_data/port_channel_estimator_test_input_rg151.dat"}, {"test_data/port_channel_estimator_test_pilots151.dat"}, {"test_data/port_channel_estimator_test_output_ch_est151.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0139, 2.0335, 20, 19.4049, 0.011655, 0.19531, 0, 6.8962, {"test_data/port_channel_estimator_test_input_rg8.dat"}, {"test_data/port_channel_estimator_test_pilots8.dat"}, {"test_data/port_channel_estimator_test_output_ch_est8.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0041, 2.0113, 20, 19.4128, 0.011522, 0.3418, 105, 103.4282, {"test_data/port_channel_estimator_test_input_rg9.dat"}, {"test_data/port_channel_estimator_test_pilots9.dat"}, {"test_data/port_channel_estimator_test_output_ch_est9.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0174, 2.0307, 20, 18.8647, 0.013245, 0.42318, -195, -205.2808, {"test_data/port_channel_estimator_test_input_rg10.dat"}, {"test_data/port_channel_estimator_test_pilots10.dat"}, {"test_data/port_channel_estimator_test_output_ch_est10.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.98505, 1.9569, 20, 18.118, 0.015229, 0.63477, 405, 411.0762, {"test_data/port_channel_estimator_test_input_rg11.dat"}, {"test_data/port_channel_estimator_test_pilots11.dat"}, {"test_data/port_channel_estimator_test_output_ch_est11.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0119, 2.0118, 20, 15.2415, 0.030341, 1.0661, 0, -1.6171, {"test_data/port_channel_estimator_test_input_rg12.dat"}, {"test_data/port_channel_estimator_test_pilots12.dat"}, {"test_data/port_channel_estimator_test_output_ch_est12.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0132, 2.0319, 20, 21.139, 0.0078133, 0.21159, 105, 111.1101, {"test_data/port_channel_estimator_test_input_rg13.dat"}, {"test_data/port_channel_estimator_test_pilots13.dat"}, {"test_data/port_channel_estimator_test_output_ch_est13.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0212, 2.0234, 20, 16.1536, 0.02482, 0.94401, -195, -201.9109, {"test_data/port_channel_estimator_test_input_rg14.dat"}, {"test_data/port_channel_estimator_test_pilots14.dat"}, {"test_data/port_channel_estimator_test_output_ch_est14.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0008, 1.9944, 20, 19.3057, 0.011771, 0.52897, 405, 398.5172, {"test_data/port_channel_estimator_test_input_rg15.dat"}, {"test_data/port_channel_estimator_test_pilots15.dat"}, {"test_data/port_channel_estimator_test_output_ch_est15.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66181, 1.9847, 20, 19.1001, 0.012242, 0.65104, 0, -23.5236, {"test_data/port_channel_estimator_test_input_rg16.dat"}, {"test_data/port_channel_estimator_test_pilots16.dat"}, {"test_data/port_channel_estimator_test_output_ch_est16.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.68364, 2.0473, 20, 19.6883, 0.011044, 0.63477, 105, 93.5236, {"test_data/port_channel_estimator_test_input_rg17.dat"}, {"test_data/port_channel_estimator_test_pilots17.dat"}, {"test_data/port_channel_estimator_test_output_ch_est17.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67587, 2.0345, 20, 18.7092, 0.013679, 0.53711, -195, -182.071, {"test_data/port_channel_estimator_test_input_rg18.dat"}, {"test_data/port_channel_estimator_test_pilots18.dat"}, {"test_data/port_channel_estimator_test_output_ch_est18.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67377, 2.0224, 20, 18.462, 0.014436, 0.7487, 405, 423.279, {"test_data/port_channel_estimator_test_input_rg19.dat"}, {"test_data/port_channel_estimator_test_pilots19.dat"}, {"test_data/port_channel_estimator_test_output_ch_est19.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67342, 2.0296, 20, 21.2713, 0.0075558, 0.31738, 0, 2.7886, {"test_data/port_channel_estimator_test_input_rg20.dat"}, {"test_data/port_channel_estimator_test_pilots20.dat"}, {"test_data/port_channel_estimator_test_output_ch_est20.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.65632, 1.9797, 20, 20.8915, 0.0080368, 0.30924, 105, 100.4613, {"test_data/port_channel_estimator_test_input_rg21.dat"}, {"test_data/port_channel_estimator_test_pilots21.dat"}, {"test_data/port_channel_estimator_test_output_ch_est21.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66571, 2.0105, 20, 21.2891, 0.0074387, 0, -195, -189.4665, {"test_data/port_channel_estimator_test_input_rg22.dat"}, {"test_data/port_channel_estimator_test_pilots22.dat"}, {"test_data/port_channel_estimator_test_output_ch_est22.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66145, 1.9997, 20, 20.4723, 0.0089205, 0.21159, 405, 401.7791, {"test_data/port_channel_estimator_test_input_rg23.dat"}, {"test_data/port_channel_estimator_test_pilots23.dat"}, {"test_data/port_channel_estimator_test_output_ch_est23.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.5045, 2.0216, 20, 19.762, 0.010684, 0.4069, 0, 5.7408, {"test_data/port_channel_estimator_test_input_rg24.dat"}, {"test_data/port_channel_estimator_test_pilots24.dat"}, {"test_data/port_channel_estimator_test_output_ch_est24.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49979, 2.0168, 20, 19.9599, 0.010112, 0.21159, 105, 115.3477, {"test_data/port_channel_estimator_test_input_rg25.dat"}, {"test_data/port_channel_estimator_test_pilots25.dat"}, {"test_data/port_channel_estimator_test_output_ch_est25.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49499, 1.9995, 20, 20.0412, 0.0098296, 0.11393, -195, -204.7404, {"test_data/port_channel_estimator_test_input_rg26.dat"}, {"test_data/port_channel_estimator_test_pilots26.dat"}, {"test_data/port_channel_estimator_test_output_ch_est26.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50529, 2.0133, 20, 18.545, 0.014161, 0.7487, 405, 423.5926, {"test_data/port_channel_estimator_test_input_rg27.dat"}, {"test_data/port_channel_estimator_test_pilots27.dat"}, {"test_data/port_channel_estimator_test_output_ch_est27.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50169, 2.0197, 20, 21.4078, 0.0072729, 0.10579, 0, -6.2335, {"test_data/port_channel_estimator_test_input_rg28.dat"}, {"test_data/port_channel_estimator_test_pilots28.dat"}, {"test_data/port_channel_estimator_test_output_ch_est28.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50291, 2.0203, 20, 20.9196, 0.0081582, 0.32552, 105, 106.1368, {"test_data/port_channel_estimator_test_input_rg29.dat"}, {"test_data/port_channel_estimator_test_pilots29.dat"}, {"test_data/port_channel_estimator_test_output_ch_est29.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.51255, 2.0475, 20, 20.4244, 0.0093188, 0.53711, -195, -193.6587, {"test_data/port_channel_estimator_test_input_rg30.dat"}, {"test_data/port_channel_estimator_test_pilots30.dat"}, {"test_data/port_channel_estimator_test_output_ch_est30.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50036, 2.0147, 20, 21.3066, 0.0074247, 0.016276, 405, 402.6045, {"test_data/port_channel_estimator_test_input_rg31.dat"}, {"test_data/port_channel_estimator_test_pilots31.dat"}, {"test_data/port_channel_estimator_test_output_ch_est31.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9774, 1.9887, 20, 18.8232, 0.012995, 0.056966, 0, 17.0571, {"test_data/port_channel_estimator_test_input_rg32.dat"}, {"test_data/port_channel_estimator_test_pilots32.dat"}, {"test_data/port_channel_estimator_test_output_ch_est32.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9757, 1.9858, 20, 20.9796, 0.0079023, 0.048828, 210, 216.1198, {"test_data/port_channel_estimator_test_input_rg33.dat"}, {"test_data/port_channel_estimator_test_pilots33.dat"}, {"test_data/port_channel_estimator_test_output_ch_est33.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0816, 2.0636, 20, 18.198, 0.015798, 0.37435, -390, -431.642, {"test_data/port_channel_estimator_test_input_rg34.dat"}, {"test_data/port_channel_estimator_test_pilots34.dat"}, {"test_data/port_channel_estimator_test_output_ch_est34.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0452, 2.0256, 20, 20.4942, 0.0091478, 0.31738, 810, 809.5471, {"test_data/port_channel_estimator_test_input_rg35.dat"}, {"test_data/port_channel_estimator_test_pilots35.dat"}, {"test_data/port_channel_estimator_test_output_ch_est35.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0441, 2.0452, 20, 20.4124, 0.0093167, 0.11393, 0, 22.8131, {"test_data/port_channel_estimator_test_input_rg36.dat"}, {"test_data/port_channel_estimator_test_pilots36.dat"}, {"test_data/port_channel_estimator_test_output_ch_est36.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9819, 1.9938, 20, 19.8989, 0.010167, 0.004069, 210, 215.238, {"test_data/port_channel_estimator_test_input_rg37.dat"}, {"test_data/port_channel_estimator_test_pilots37.dat"}, {"test_data/port_channel_estimator_test_output_ch_est37.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9964, 2.0005, 20, 20.2, 0.0095554, 0.15869, -390, -409.6846, {"test_data/port_channel_estimator_test_input_rg38.dat"}, {"test_data/port_channel_estimator_test_pilots38.dat"}, {"test_data/port_channel_estimator_test_output_ch_est38.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0158, 1.9936, 20, 20.8286, 0.0083482, 0.37435, 810, 810.1457, {"test_data/port_channel_estimator_test_input_rg39.dat"}, {"test_data/port_channel_estimator_test_pilots39.dat"}, {"test_data/port_channel_estimator_test_output_ch_est39.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0043, 2.0079, 20, 18.6824, 0.013635, 0.21159, 0, -20.5102, {"test_data/port_channel_estimator_test_input_rg40.dat"}, {"test_data/port_channel_estimator_test_pilots40.dat"}, {"test_data/port_channel_estimator_test_output_ch_est40.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99241, 1.9877, 20, 19.9802, 0.009993, 0.16276, 210, 195.018, {"test_data/port_channel_estimator_test_input_rg41.dat"}, {"test_data/port_channel_estimator_test_pilots41.dat"}, {"test_data/port_channel_estimator_test_output_ch_est41.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99753, 2.003, 20, 19.717, 0.010672, 0.056966, -390, -386.9159, {"test_data/port_channel_estimator_test_input_rg42.dat"}, {"test_data/port_channel_estimator_test_pilots42.dat"}, {"test_data/port_channel_estimator_test_output_ch_est42.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.98609, 1.98, 20, 20.7318, 0.0083515, 0.056966, 810, 798.9527, {"test_data/port_channel_estimator_test_input_rg43.dat"}, {"test_data/port_channel_estimator_test_pilots43.dat"}, {"test_data/port_channel_estimator_test_output_ch_est43.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0074, 1.9949, 20, 15.926, 0.025801, 0.53304, 0, -6.5679, {"test_data/port_channel_estimator_test_input_rg44.dat"}, {"test_data/port_channel_estimator_test_pilots44.dat"}, {"test_data/port_channel_estimator_test_output_ch_est44.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0257, 2.0318, 20, 16.2632, 0.024307, 0.48014, 210, 191.4598, {"test_data/port_channel_estimator_test_input_rg45.dat"}, {"test_data/port_channel_estimator_test_pilots45.dat"}, {"test_data/port_channel_estimator_test_output_ch_est45.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0249, 2.0463, 20, 19.6192, 0.011215, 0.21566, -390, -367.4152, {"test_data/port_channel_estimator_test_input_rg46.dat"}, {"test_data/port_channel_estimator_test_pilots46.dat"}, {"test_data/port_channel_estimator_test_output_ch_est46.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.006, 2.0217, 20, 21.3789, 0.0073406, 0.052897, 810, 799.6667, {"test_data/port_channel_estimator_test_input_rg47.dat"}, {"test_data/port_channel_estimator_test_pilots47.dat"}, {"test_data/port_channel_estimator_test_output_ch_est47.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67613, 2.0233, 20, 18.0282, 0.016008, 0.52897, 0, 2.7583, {"test_data/port_channel_estimator_test_input_rg48.dat"}, {"test_data/port_channel_estimator_test_pilots48.dat"}, {"test_data/port_channel_estimator_test_output_ch_est48.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67225, 2.0243, 20, 20.005, 0.010096, 0.21973, 210, 208.8929, {"test_data/port_channel_estimator_test_input_rg49.dat"}, {"test_data/port_channel_estimator_test_pilots49.dat"}, {"test_data/port_channel_estimator_test_output_ch_est49.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67479, 2.0391, 20, 20.7821, 0.008474, 0.048828, -390, -380.6417, {"test_data/port_channel_estimator_test_input_rg50.dat"}, {"test_data/port_channel_estimator_test_pilots50.dat"}, {"test_data/port_channel_estimator_test_output_ch_est50.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67239, 2.0342, 20, 20.9227, 0.0081747, 0, 810, 793.0787, {"test_data/port_channel_estimator_test_input_rg51.dat"}, {"test_data/port_channel_estimator_test_pilots51.dat"}, {"test_data/port_channel_estimator_test_output_ch_est51.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66322, 2.004, 20, 19.9833, 0.01001, 0.15462, 0, 5.8857, {"test_data/port_channel_estimator_test_input_rg52.dat"}, {"test_data/port_channel_estimator_test_pilots52.dat"}, {"test_data/port_channel_estimator_test_output_ch_est52.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67478, 2.0271, 20, 17.8442, 0.016667, 0.53304, 210, 211.0319, {"test_data/port_channel_estimator_test_input_rg53.dat"}, {"test_data/port_channel_estimator_test_pilots53.dat"}, {"test_data/port_channel_estimator_test_output_ch_est53.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.68561, 2.047, 20, 19.6596, 0.011149, 0.43132, -390, -366.5846, {"test_data/port_channel_estimator_test_input_rg54.dat"}, {"test_data/port_channel_estimator_test_pilots54.dat"}, {"test_data/port_channel_estimator_test_output_ch_est54.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.65895, 1.9943, 20, 20.9566, 0.0079489, 0.004069, 810, 827.4101, {"test_data/port_channel_estimator_test_input_rg55.dat"}, {"test_data/port_channel_estimator_test_pilots55.dat"}, {"test_data/port_channel_estimator_test_output_ch_est55.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50218, 2.0228, 20, 20.4601, 0.0090555, 0.10579, 0, 9.8219, {"test_data/port_channel_estimator_test_input_rg56.dat"}, {"test_data/port_channel_estimator_test_pilots56.dat"}, {"test_data/port_channel_estimator_test_output_ch_est56.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50226, 2.0144, 20, 19.7161, 0.010749, 0.21159, 210, 204.1263, {"test_data/port_channel_estimator_test_input_rg57.dat"}, {"test_data/port_channel_estimator_test_pilots57.dat"}, {"test_data/port_channel_estimator_test_output_ch_est57.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49434, 1.9792, 20, 19.5171, 0.011076, 0.26042, -390, -354.1191, {"test_data/port_channel_estimator_test_input_rg58.dat"}, {"test_data/port_channel_estimator_test_pilots58.dat"}, {"test_data/port_channel_estimator_test_output_ch_est58.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49978, 1.9937, 20, 17.3189, 0.018576, 0.52897, 810, 817.596, {"test_data/port_channel_estimator_test_input_rg59.dat"}, {"test_data/port_channel_estimator_test_pilots59.dat"}, {"test_data/port_channel_estimator_test_output_ch_est59.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50855, 2.0315, 20, 19.1572, 0.012379, 0.32552, 0, -13.1634, {"test_data/port_channel_estimator_test_input_rg60.dat"}, {"test_data/port_channel_estimator_test_pilots60.dat"}, {"test_data/port_channel_estimator_test_output_ch_est60.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.4982, 1.9953, 20, 20.8135, 0.0082816, 0.21566, 210, 199.0561, {"test_data/port_channel_estimator_test_input_rg61.dat"}, {"test_data/port_channel_estimator_test_pilots61.dat"}, {"test_data/port_channel_estimator_test_output_ch_est61.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.5125, 2.0386, 20, 18.2236, 0.015467, 0.47201, -390, -398.7625, {"test_data/port_channel_estimator_test_input_rg62.dat"}, {"test_data/port_channel_estimator_test_pilots62.dat"}, {"test_data/port_channel_estimator_test_output_ch_est62.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50746, 2.0184, 20, 18.0129, 0.016076, 0.47607, 810, 800.6136, {"test_data/port_channel_estimator_test_input_rg63.dat"}, {"test_data/port_channel_estimator_test_pilots63.dat"}, {"test_data/port_channel_estimator_test_output_ch_est63.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0039, 2.0119, 20, 20.3483, 0.0092694, 0.11393, 0, -6.0829, {"test_data/port_channel_estimator_test_input_rg64.dat"}, {"test_data/port_channel_estimator_test_pilots64.dat"}, {"test_data/port_channel_estimator_test_output_ch_est64.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.994, 1.9957, 20, 20.0993, 0.0097679, 0.32552, 105, 105.8204, {"test_data/port_channel_estimator_test_input_rg65.dat"}, {"test_data/port_channel_estimator_test_pilots65.dat"}, {"test_data/port_channel_estimator_test_output_ch_est65.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0225, 2.0044, 20, 20.1245, 0.0098502, 0.63477, -195, -197.7873, {"test_data/port_channel_estimator_test_input_rg66.dat"}, {"test_data/port_channel_estimator_test_pilots66.dat"}, {"test_data/port_channel_estimator_test_output_ch_est66.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0075, 2.0032, 20, 20.1433, 0.0097348, 0.42318, 405, 408.5145, {"test_data/port_channel_estimator_test_input_rg67.dat"}, {"test_data/port_channel_estimator_test_pilots67.dat"}, {"test_data/port_channel_estimator_test_output_ch_est67.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0344, 2.0016, 20, 20.6583, 0.0087622, 1.0742, 0, 4.4685, {"test_data/port_channel_estimator_test_input_rg68.dat"}, {"test_data/port_channel_estimator_test_pilots68.dat"}, {"test_data/port_channel_estimator_test_output_ch_est68.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0534, 2.0221, 20, 20.8328, 0.0084956, 0.86263, 105, 107.4516, {"test_data/port_channel_estimator_test_input_rg69.dat"}, {"test_data/port_channel_estimator_test_pilots69.dat"}, {"test_data/port_channel_estimator_test_output_ch_est69.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9978, 1.9924, 20, 20.7637, 0.008398, 0.42318, -195, -198.7036, {"test_data/port_channel_estimator_test_input_rg70.dat"}, {"test_data/port_channel_estimator_test_pilots70.dat"}, {"test_data/port_channel_estimator_test_output_ch_est70.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0085, 2.0186, 20, 20.1399, 0.0097473, 0, 405, 407.0872, {"test_data/port_channel_estimator_test_input_rg71.dat"}, {"test_data/port_channel_estimator_test_pilots71.dat"}, {"test_data/port_channel_estimator_test_output_ch_est71.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.98919, 1.9847, 20, 19.9902, 0.0099377, 0.21159, 0, -5.0888, {"test_data/port_channel_estimator_test_input_rg72.dat"}, {"test_data/port_channel_estimator_test_pilots72.dat"}, {"test_data/port_channel_estimator_test_output_ch_est72.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0119, 2.0075, 20, 16.6038, 0.022172, 0.86263, 105, 105.9112, {"test_data/port_channel_estimator_test_input_rg73.dat"}, {"test_data/port_channel_estimator_test_pilots73.dat"}, {"test_data/port_channel_estimator_test_output_ch_est73.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0081, 2.009, 20, 18.7439, 0.013494, 0.53711, -195, -195.292, {"test_data/port_channel_estimator_test_input_rg74.dat"}, {"test_data/port_channel_estimator_test_pilots74.dat"}, {"test_data/port_channel_estimator_test_output_ch_est74.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0028, 2.012, 20, 20.0164, 0.010014, 0.21159, 405, 411.3746, {"test_data/port_channel_estimator_test_input_rg75.dat"}, {"test_data/port_channel_estimator_test_pilots75.dat"}, {"test_data/port_channel_estimator_test_output_ch_est75.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0081, 2.0004, 20, 17.6393, 0.017403, 0.7487, 0, -1.1685, {"test_data/port_channel_estimator_test_input_rg76.dat"}, {"test_data/port_channel_estimator_test_pilots76.dat"}, {"test_data/port_channel_estimator_test_output_ch_est76.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0079, 2.0129, 20, 19.667, 0.010908, 0.42318, 105, 107.6942, {"test_data/port_channel_estimator_test_input_rg77.dat"}, {"test_data/port_channel_estimator_test_pilots77.dat"}, {"test_data/port_channel_estimator_test_output_ch_est77.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99379, 1.9953, 20, 20.642, 0.0085925, 0.11393, -195, -192.1146, {"test_data/port_channel_estimator_test_input_rg78.dat"}, {"test_data/port_channel_estimator_test_pilots78.dat"}, {"test_data/port_channel_estimator_test_output_ch_est78.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0113, 2.007, 20, 17.5543, 0.017803, 0.7487, 405, 408.1676, {"test_data/port_channel_estimator_test_input_rg79.dat"}, {"test_data/port_channel_estimator_test_pilots79.dat"}, {"test_data/port_channel_estimator_test_output_ch_est79.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.6678, 2.0086, 20, 17.8618, 0.016428, 1.0742, 0, -4.5655, {"test_data/port_channel_estimator_test_input_rg80.dat"}, {"test_data/port_channel_estimator_test_pilots80.dat"}, {"test_data/port_channel_estimator_test_output_ch_est80.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67482, 2.021, 20, 18.877, 0.01314, 0.86263, 105, 100.9382, {"test_data/port_channel_estimator_test_input_rg81.dat"}, {"test_data/port_channel_estimator_test_pilots81.dat"}, {"test_data/port_channel_estimator_test_output_ch_est81.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67082, 2.0317, 20, 20.2861, 0.0094433, 0, -195, -198.7191, {"test_data/port_channel_estimator_test_input_rg82.dat"}, {"test_data/port_channel_estimator_test_pilots82.dat"}, {"test_data/port_channel_estimator_test_output_ch_est82.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67019, 2.0078, 20, 18.8233, 0.013213, 0.86263, 405, 403.3202, {"test_data/port_channel_estimator_test_input_rg83.dat"}, {"test_data/port_channel_estimator_test_pilots83.dat"}, {"test_data/port_channel_estimator_test_output_ch_est83.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66909, 2.019, 20, 20.4387, 0.0090935, 0.32552, 0, 1.9934, {"test_data/port_channel_estimator_test_input_rg84.dat"}, {"test_data/port_channel_estimator_test_pilots84.dat"}, {"test_data/port_channel_estimator_test_output_ch_est84.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67545, 2.0289, 20, 20.1674, 0.0097717, 0.53711, 105, 103.7523, {"test_data/port_channel_estimator_test_input_rg85.dat"}, {"test_data/port_channel_estimator_test_pilots85.dat"}, {"test_data/port_channel_estimator_test_output_ch_est85.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66711, 1.9969, 20, 19.0322, 0.012534, 0.84635, -195, -197.4971, {"test_data/port_channel_estimator_test_input_rg86.dat"}, {"test_data/port_channel_estimator_test_pilots86.dat"}, {"test_data/port_channel_estimator_test_output_ch_est86.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66892, 2.0229, 20, 20.7913, 0.0083823, 0.11393, 405, 406.4799, {"test_data/port_channel_estimator_test_input_rg87.dat"}, {"test_data/port_channel_estimator_test_pilots87.dat"}, {"test_data/port_channel_estimator_test_output_ch_est87.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50109, 2.0137, 20, 19.5486, 0.011146, 0.42318, 0, -3.4959, {"test_data/port_channel_estimator_test_input_rg88.dat"}, {"test_data/port_channel_estimator_test_pilots88.dat"}, {"test_data/port_channel_estimator_test_output_ch_est88.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50441, 2.0199, 20, 19.552, 0.011211, 0.53711, 105, 104.6779, {"test_data/port_channel_estimator_test_input_rg89.dat"}, {"test_data/port_channel_estimator_test_pilots89.dat"}, {"test_data/port_channel_estimator_test_output_ch_est89.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49947, 2.0051, 20, 19.8947, 0.010259, 0.42318, -195, -190.4038, {"test_data/port_channel_estimator_test_input_rg90.dat"}, {"test_data/port_channel_estimator_test_pilots90.dat"}, {"test_data/port_channel_estimator_test_output_ch_est90.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50498, 2.015, 20, 18.5303, 0.0142, 0.7487, 405, 397.1786, {"test_data/port_channel_estimator_test_input_rg91.dat"}, {"test_data/port_channel_estimator_test_pilots91.dat"}, {"test_data/port_channel_estimator_test_output_ch_est91.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49973, 2.004, 20, 20.4167, 0.0091017, 0.42318, 0, 0.10287, {"test_data/port_channel_estimator_test_input_rg92.dat"}, {"test_data/port_channel_estimator_test_pilots92.dat"}, {"test_data/port_channel_estimator_test_output_ch_est92.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50549, 2.0376, 20, 21.2082, 0.0076727, 0.11393, 105, 105.2442, {"test_data/port_channel_estimator_test_input_rg93.dat"}, {"test_data/port_channel_estimator_test_pilots93.dat"}, {"test_data/port_channel_estimator_test_output_ch_est93.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.51011, 2.0322, 20, 19.0874, 0.012618, 0.7487, -195, -191.9705, {"test_data/port_channel_estimator_test_input_rg94.dat"}, {"test_data/port_channel_estimator_test_pilots94.dat"}, {"test_data/port_channel_estimator_test_output_ch_est94.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49996, 2.0163, 20, 21.1055, 0.0077705, 0, 405, 403.2931, {"test_data/port_channel_estimator_test_input_rg95.dat"}, {"test_data/port_channel_estimator_test_pilots95.dat"}, {"test_data/port_channel_estimator_test_output_ch_est95.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0205, 2.0019, 20, 20.4407, 0.009149, 0.31738, 0, 3.9038, {"test_data/port_channel_estimator_test_input_rg96.dat"}, {"test_data/port_channel_estimator_test_pilots96.dat"}, {"test_data/port_channel_estimator_test_output_ch_est96.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0105, 2.0069, 20, 19.8975, 0.010317, 0.21159, 210, 213.4159, {"test_data/port_channel_estimator_test_input_rg97.dat"}, {"test_data/port_channel_estimator_test_pilots97.dat"}, {"test_data/port_channel_estimator_test_output_ch_est97.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0199, 2.0022, 20, 19.9807, 0.010169, 0.31738, -390, -395.1789, {"test_data/port_channel_estimator_test_input_rg98.dat"}, {"test_data/port_channel_estimator_test_pilots98.dat"}, {"test_data/port_channel_estimator_test_output_ch_est98.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.014, 2.0203, 20, 20.0096, 0.010072, 0.10579, 810, 817.1675, {"test_data/port_channel_estimator_test_input_rg99.dat"}, {"test_data/port_channel_estimator_test_pilots99.dat"}, {"test_data/port_channel_estimator_test_output_ch_est99.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0097, 2.0048, 20, 20.4499, 0.0090809, 0.21159, 0, 0.47065, {"test_data/port_channel_estimator_test_input_rg100.dat"}, {"test_data/port_channel_estimator_test_pilots100.dat"}, {"test_data/port_channel_estimator_test_output_ch_est100.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0307, 2.0056, 20, 20.7432, 0.0085769, 0.37435, 210, 207.2361, {"test_data/port_channel_estimator_test_input_rg101.dat"}, {"test_data/port_channel_estimator_test_pilots101.dat"}, {"test_data/port_channel_estimator_test_output_ch_est101.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0384, 2.0089, 20, 20.5016, 0.0091017, 0.43132, -390, -390.7405, {"test_data/port_channel_estimator_test_input_rg102.dat"}, {"test_data/port_channel_estimator_test_pilots102.dat"}, {"test_data/port_channel_estimator_test_output_ch_est102.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0217, 2.0266, 20, 20.5888, 0.008848, 0.10579, 810, 817.1127, {"test_data/port_channel_estimator_test_input_rg103.dat"}, {"test_data/port_channel_estimator_test_pilots103.dat"}, {"test_data/port_channel_estimator_test_output_ch_est103.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0098, 2.018, 20, 19.2842, 0.011936, 0.21159, 0, 8.859, {"test_data/port_channel_estimator_test_input_rg104.dat"}, {"test_data/port_channel_estimator_test_pilots104.dat"}, {"test_data/port_channel_estimator_test_output_ch_est104.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99163, 1.9893, 20, 20.2464, 0.0093915, 0.10579, 210, 222.0675, {"test_data/port_channel_estimator_test_input_rg105.dat"}, {"test_data/port_channel_estimator_test_pilots105.dat"}, {"test_data/port_channel_estimator_test_output_ch_est105.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0077, 2.0143, 20, 19.1035, 0.012417, 0.21159, -390, -392.1142, {"test_data/port_channel_estimator_test_input_rg106.dat"}, {"test_data/port_channel_estimator_test_pilots106.dat"}, {"test_data/port_channel_estimator_test_output_ch_est106.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0019, 2.0126, 20, 20.3976, 0.0091645, 0, 810, 813.7262, {"test_data/port_channel_estimator_test_input_rg107.dat"}, {"test_data/port_channel_estimator_test_pilots107.dat"}, {"test_data/port_channel_estimator_test_output_ch_est107.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0066, 2.0142, 20, 20.5366, 0.008917, 0.16276, 0, -0.057647, {"test_data/port_channel_estimator_test_input_rg108.dat"}, {"test_data/port_channel_estimator_test_pilots108.dat"}, {"test_data/port_channel_estimator_test_output_ch_est108.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99102, 1.9875, 20, 20.6318, 0.0085888, 0.10579, 210, 215.8384, {"test_data/port_channel_estimator_test_input_rg109.dat"}, {"test_data/port_channel_estimator_test_pilots109.dat"}, {"test_data/port_channel_estimator_test_output_ch_est109.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0159, 2.0154, 20, 17.6851, 0.017352, 0.37435, -390, -391.7113, {"test_data/port_channel_estimator_test_input_rg110.dat"}, {"test_data/port_channel_estimator_test_pilots110.dat"}, {"test_data/port_channel_estimator_test_output_ch_est110.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0034, 2.0083, 20, 20.2554, 0.0094833, 0.16276, 810, 806.9353, {"test_data/port_channel_estimator_test_input_rg111.dat"}, {"test_data/port_channel_estimator_test_pilots111.dat"}, {"test_data/port_channel_estimator_test_output_ch_est111.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66763, 2.0118, 20, 19.9157, 0.010235, 0.21159, 0, 0.80271, {"test_data/port_channel_estimator_test_input_rg112.dat"}, {"test_data/port_channel_estimator_test_pilots112.dat"}, {"test_data/port_channel_estimator_test_output_ch_est112.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67573, 2.0256, 20, 19.109, 0.012474, 0.37435, 210, 208.6936, {"test_data/port_channel_estimator_test_input_rg113.dat"}, {"test_data/port_channel_estimator_test_pilots113.dat"}, {"test_data/port_channel_estimator_test_output_ch_est113.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66466, 2.0095, 20, 20.279, 0.0093718, 0.10579, -390, -391.3474, {"test_data/port_channel_estimator_test_input_rg114.dat"}, {"test_data/port_channel_estimator_test_pilots114.dat"}, {"test_data/port_channel_estimator_test_output_ch_est114.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66669, 2.0123, 20, 20.1243, 0.0097413, 0.16276, 810, 818.6492, {"test_data/port_channel_estimator_test_input_rg115.dat"}, {"test_data/port_channel_estimator_test_pilots115.dat"}, {"test_data/port_channel_estimator_test_output_ch_est115.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66518, 2.0105, 20, 20.6017, 0.0087074, 0.10579, 0, -6.5832, {"test_data/port_channel_estimator_test_input_rg116.dat"}, {"test_data/port_channel_estimator_test_pilots116.dat"}, {"test_data/port_channel_estimator_test_output_ch_est116.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67293, 2.0248, 20, 20.4893, 0.0090397, 0.21159, 210, 215.3395, {"test_data/port_channel_estimator_test_input_rg117.dat"}, {"test_data/port_channel_estimator_test_pilots117.dat"}, {"test_data/port_channel_estimator_test_output_ch_est117.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66855, 2.0197, 20, 20.7527, 0.0084524, 0.10579, -390, -388.1198, {"test_data/port_channel_estimator_test_input_rg118.dat"}, {"test_data/port_channel_estimator_test_pilots118.dat"}, {"test_data/port_channel_estimator_test_output_ch_est118.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66714, 2.0125, 20, 20.5341, 0.0088701, 0.16276, 810, 805.6388, {"test_data/port_channel_estimator_test_input_rg119.dat"}, {"test_data/port_channel_estimator_test_pilots119.dat"}, {"test_data/port_channel_estimator_test_output_ch_est119.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50159, 2.0125, 20, 20.1738, 0.0096611, 0.21159, 0, -6.1449, {"test_data/port_channel_estimator_test_input_rg120.dat"}, {"test_data/port_channel_estimator_test_pilots120.dat"}, {"test_data/port_channel_estimator_test_output_ch_est120.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50378, 2.0076, 20, 18.1971, 0.015296, 0.42318, 210, 208.1749, {"test_data/port_channel_estimator_test_input_rg121.dat"}, {"test_data/port_channel_estimator_test_pilots121.dat"}, {"test_data/port_channel_estimator_test_output_ch_est121.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50022, 1.9985, 20, 19.0711, 0.01242, 0.31738, -390, -386.1237, {"test_data/port_channel_estimator_test_input_rg122.dat"}, {"test_data/port_channel_estimator_test_pilots122.dat"}, {"test_data/port_channel_estimator_test_output_ch_est122.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49619, 1.9959, 20, 20.2163, 0.0094641, 0.16276, 810, 794.9531, {"test_data/port_channel_estimator_test_input_rg123.dat"}, {"test_data/port_channel_estimator_test_pilots123.dat"}, {"test_data/port_channel_estimator_test_output_ch_est123.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.4981, 2.0093, 20, 21.0292, 0.0078788, 0, 0, 1.7858, {"test_data/port_channel_estimator_test_input_rg124.dat"}, {"test_data/port_channel_estimator_test_pilots124.dat"}, {"test_data/port_channel_estimator_test_output_ch_est124.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50421, 2.0174, 20, 19.9983, 0.010112, 0.26855, 210, 211.7645, {"test_data/port_channel_estimator_test_input_rg125.dat"}, {"test_data/port_channel_estimator_test_pilots125.dat"}, {"test_data/port_channel_estimator_test_output_ch_est125.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50257, 2.0161, 20, 20.2702, 0.0094676, 0.21159, -390, -386.975, {"test_data/port_channel_estimator_test_input_rg126.dat"}, {"test_data/port_channel_estimator_test_pilots126.dat"}, {"test_data/port_channel_estimator_test_output_ch_est126.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, {0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50021, 2.0014, 20, 19.9853, 0.010062, 0.26855, 810, 822.3903, {"test_data/port_channel_estimator_test_input_rg127.dat"}, {"test_data/port_channel_estimator_test_pilots127.dat"}, {"test_data/port_channel_estimator_test_output_ch_est127.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0031, 2.0044, 20, 20.2326, 0.0095159, 0.32552, 0, -1.5753, {"test_data/port_channel_estimator_test_input_rg128.dat"}, {"test_data/port_channel_estimator_test_pilots128.dat"}, {"test_data/port_channel_estimator_test_output_ch_est128.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0192, 2.0081, 20, 20.1904, 0.0096858, 0.53711, 105, 102.873, {"test_data/port_channel_estimator_test_input_rg129.dat"}, {"test_data/port_channel_estimator_test_pilots129.dat"}, {"test_data/port_channel_estimator_test_output_ch_est129.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0465, 2.0146, 20, 20.046, 0.010149, 0.96029, -195, -192.0647, {"test_data/port_channel_estimator_test_input_rg130.dat"}, {"test_data/port_channel_estimator_test_pilots130.dat"}, {"test_data/port_channel_estimator_test_output_ch_est130.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0382, 2.0059, 20, 20.2459, 0.0096527, 1.0742, 405, 403.6491, {"test_data/port_channel_estimator_test_input_rg131.dat"}, {"test_data/port_channel_estimator_test_pilots131.dat"}, {"test_data/port_channel_estimator_test_output_ch_est131.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0023, 2.0031, 20, 19.2322, 0.011989, 0.42318, 0, 1.2194, {"test_data/port_channel_estimator_test_input_rg132.dat"}, {"test_data/port_channel_estimator_test_pilots132.dat"}, {"test_data/port_channel_estimator_test_output_ch_est132.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0097, 2.0049, 20, 17.3075, 0.018814, 0.7487, 105, 106.6063, {"test_data/port_channel_estimator_test_input_rg133.dat"}, {"test_data/port_channel_estimator_test_pilots133.dat"}, {"test_data/port_channel_estimator_test_output_ch_est133.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.011, 2.0054, 20, 16.6263, 0.022036, 0.86263, -195, -190.0453, {"test_data/port_channel_estimator_test_input_rg134.dat"}, {"test_data/port_channel_estimator_test_pilots134.dat"}, {"test_data/port_channel_estimator_test_output_ch_est134.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99654, 1.9988, 20, 20.2419, 0.009448, 0.21159, 405, 408.2831, {"test_data/port_channel_estimator_test_input_rg135.dat"}, {"test_data/port_channel_estimator_test_pilots135.dat"}, {"test_data/port_channel_estimator_test_output_ch_est135.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67179, 2.0204, 20, 19.6703, 0.010897, 0.53711, 0, -0.98628, {"test_data/port_channel_estimator_test_input_rg136.dat"}, {"test_data/port_channel_estimator_test_pilots136.dat"}, {"test_data/port_channel_estimator_test_output_ch_est136.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67282, 2.0272, 20, 19.968, 0.010191, 0.42318, 105, 106.0612, {"test_data/port_channel_estimator_test_input_rg137.dat"}, {"test_data/port_channel_estimator_test_pilots137.dat"}, {"test_data/port_channel_estimator_test_output_ch_est137.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67096, 2.0146, 20, 19.311, 0.011823, 0.63477, -195, -199.2933, {"test_data/port_channel_estimator_test_input_rg138.dat"}, {"test_data/port_channel_estimator_test_pilots138.dat"}, {"test_data/port_channel_estimator_test_output_ch_est138.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.6694, 2.0123, 20, 19.8672, 0.010377, 0.53711, 405, 404.8143, {"test_data/port_channel_estimator_test_input_rg139.dat"}, {"test_data/port_channel_estimator_test_pilots139.dat"}, {"test_data/port_channel_estimator_test_output_ch_est139.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50647, 2.0185, 20, 17.772, 0.01696, 0.96029, 0, -2.4305, {"test_data/port_channel_estimator_test_input_rg140.dat"}, {"test_data/port_channel_estimator_test_pilots140.dat"}, {"test_data/port_channel_estimator_test_output_ch_est140.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50382, 2.0127, 20, 17.2048, 0.019224, 1.0742, 105, 104.5748, {"test_data/port_channel_estimator_test_input_rg141.dat"}, {"test_data/port_channel_estimator_test_pilots141.dat"}, {"test_data/port_channel_estimator_test_output_ch_est141.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.4983, 2.0105, 20, 20.4591, 0.0089875, 0.11393, -195, -192.4195, {"test_data/port_channel_estimator_test_input_rg142.dat"}, {"test_data/port_channel_estimator_test_pilots142.dat"}, {"test_data/port_channel_estimator_test_output_ch_est142.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50432, 2.0116, 20, 18.665, 0.013749, 0.7487, 405, 405.9824, {"test_data/port_channel_estimator_test_input_rg143.dat"}, {"test_data/port_channel_estimator_test_pilots143.dat"}, {"test_data/port_channel_estimator_test_output_ch_est143.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0113, 2.007, 20, 20.2183, 0.0095864, 0.21159, 0, 1.8365, {"test_data/port_channel_estimator_test_input_rg144.dat"}, {"test_data/port_channel_estimator_test_pilots144.dat"}, {"test_data/port_channel_estimator_test_output_ch_est144.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.9937, 2.0035, 20, 20.2755, 0.0093781, 0, 210, 203.4591, {"test_data/port_channel_estimator_test_input_rg145.dat"}, {"test_data/port_channel_estimator_test_pilots145.dat"}, {"test_data/port_channel_estimator_test_output_ch_est145.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0252, 2.007, 20, 20.3149, 0.0094403, 0.31738, -390, -385.3278, {"test_data/port_channel_estimator_test_input_rg146.dat"}, {"test_data/port_channel_estimator_test_pilots146.dat"}, {"test_data/port_channel_estimator_test_output_ch_est146.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 2.0035, 2.0125, 20, 20.1143, 0.0097802, 0.056966, 810, 808.5222, {"test_data/port_channel_estimator_test_input_rg147.dat"}, {"test_data/port_channel_estimator_test_pilots147.dat"}, {"test_data/port_channel_estimator_test_output_ch_est147.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0088, 2.0065, 20, 18.0186, 0.015958, 0.31738, 0, 2.6344, {"test_data/port_channel_estimator_test_input_rg148.dat"}, {"test_data/port_channel_estimator_test_pilots148.dat"}, {"test_data/port_channel_estimator_test_output_ch_est148.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0137, 2.0103, 20, 16.7377, 0.021536, 0.43132, 210, 205.526, {"test_data/port_channel_estimator_test_input_rg149.dat"}, {"test_data/port_channel_estimator_test_pilots149.dat"}, {"test_data/port_channel_estimator_test_output_ch_est149.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0019, 1.9973, 20, 18.6008, 0.01386, 0.26855, -390, -388.0246, {"test_data/port_channel_estimator_test_input_rg150.dat"}, {"test_data/port_channel_estimator_test_pilots150.dat"}, {"test_data/port_channel_estimator_test_output_ch_est150.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0151, 2.0132, 20, 16.7126, 0.02169, 0.43132, 810, 806.6554, {"test_data/port_channel_estimator_test_input_rg151.dat"}, {"test_data/port_channel_estimator_test_pilots151.dat"}, {"test_data/port_channel_estimator_test_output_ch_est151.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66921, 2.0091, 20, 19.3339, 0.01173, 0.31738, 0, -2.6966, {"test_data/port_channel_estimator_test_input_rg152.dat"}, {"test_data/port_channel_estimator_test_pilots152.dat"}, {"test_data/port_channel_estimator_test_output_ch_est152.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.66779, 2.0164, 20, 20.1195, 0.0097681, 0.16276, 210, 200.7574, {"test_data/port_channel_estimator_test_input_rg153.dat"}, {"test_data/port_channel_estimator_test_pilots153.dat"}, {"test_data/port_channel_estimator_test_output_ch_est153.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.6697, 2.0128, 20, 18.0046, 0.015942, 0.53711, -390, -396.8997, {"test_data/port_channel_estimator_test_input_rg154.dat"}, {"test_data/port_channel_estimator_test_pilots154.dat"}, {"test_data/port_channel_estimator_test_output_ch_est154.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.67245, 2.0182, 20, 19.4428, 0.011495, 0.31738, 810, 813.7704, {"test_data/port_channel_estimator_test_input_rg155.dat"}, {"test_data/port_channel_estimator_test_pilots155.dat"}, {"test_data/port_channel_estimator_test_output_ch_est155.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50534, 2.0161, 20, 18.5851, 0.014032, 0.37435, 0, 6.6194, {"test_data/port_channel_estimator_test_input_rg156.dat"}, {"test_data/port_channel_estimator_test_pilots156.dat"}, {"test_data/port_channel_estimator_test_output_ch_est156.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.49749, 2.0085, 20, 20.3725, 0.0091537, 0, 210, 211.0696, {"test_data/port_channel_estimator_test_input_rg157.dat"}, {"test_data/port_channel_estimator_test_pilots157.dat"}, {"test_data/port_channel_estimator_test_output_ch_est157.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50028, 2.0167, 20, 20.2149, 0.0095452, 0.10579, -390, -390.5017, {"test_data/port_channel_estimator_test_input_rg158.dat"}, {"test_data/port_channel_estimator_test_pilots158.dat"}, {"test_data/port_channel_estimator_test_output_ch_est158.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}, {{1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, {}, std::nullopt, {0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1}}}}, {0}, 1.4125}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.50093, 2.016, 20, 20.0045, 0.010032, 0.16276, 810, 809.4857, {"test_data/port_channel_estimator_test_input_rg159.dat"}, {"test_data/port_channel_estimator_test_pilots159.dat"}, {"test_data/port_channel_estimator_test_output_ch_est159.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.98075, 1.0112, 20, 14.8936, 0.031783, 0, 0, 31.5254, {"test_data/port_channel_estimator_test_input_rg160.dat"}, {"test_data/port_channel_estimator_test_pilots160.dat"}, {"test_data/port_channel_estimator_test_output_ch_est160.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.9558, 0.97997, 20, 15.7852, 0.025226, 0, 105, 66.5697, {"test_data/port_channel_estimator_test_input_rg161.dat"}, {"test_data/port_channel_estimator_test_pilots161.dat"}, {"test_data/port_channel_estimator_test_output_ch_est161.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.95903, 1.0105, 20, 12.5144, 0.053751, 0, -195, -212.7781, {"test_data/port_channel_estimator_test_input_rg162.dat"}, {"test_data/port_channel_estimator_test_pilots162.dat"}, {"test_data/port_channel_estimator_test_output_ch_est162.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.9582, 1.0669, 20, 9.268, 0.11341, 0, 405, 358.2117, {"test_data/port_channel_estimator_test_input_rg163.dat"}, {"test_data/port_channel_estimator_test_pilots163.dat"}, {"test_data/port_channel_estimator_test_output_ch_est163.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.9728, 1.0552, 20, 10.5365, 0.085975, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg164.dat"}, {"test_data/port_channel_estimator_test_pilots164.dat"}, {"test_data/port_channel_estimator_test_output_ch_est164.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.93579, 1.0424, 20, 9.2502, 0.11121, 0, 105, {}, {"test_data/port_channel_estimator_test_input_rg165.dat"}, {"test_data/port_channel_estimator_test_pilots165.dat"}, {"test_data/port_channel_estimator_test_output_ch_est165.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.93935, 0.99594, 20, 12.0167, 0.059041, 0, -195, {}, {"test_data/port_channel_estimator_test_input_rg166.dat"}, {"test_data/port_channel_estimator_test_pilots166.dat"}, {"test_data/port_channel_estimator_test_output_ch_est166.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.98664, 1.0001, 20, 18.4807, 0.013999, 0, 405, {}, {"test_data/port_channel_estimator_test_input_rg167.dat"}, {"test_data/port_channel_estimator_test_pilots167.dat"}, {"test_data/port_channel_estimator_test_output_ch_est167.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.94921, 1.031, 20, 10.4616, 0.08535, 0, 0, 44.638, {"test_data/port_channel_estimator_test_input_rg168.dat"}, {"test_data/port_channel_estimator_test_pilots168.dat"}, {"test_data/port_channel_estimator_test_output_ch_est168.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.93823, 1.0393, 20, 9.4934, 0.10543, 0, 210, 309.6953, {"test_data/port_channel_estimator_test_input_rg169.dat"}, {"test_data/port_channel_estimator_test_pilots169.dat"}, {"test_data/port_channel_estimator_test_output_ch_est169.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.97524, 0.99639, 20, 16.4535, 0.022068, 0, -390, -444.5342, {"test_data/port_channel_estimator_test_input_rg170.dat"}, {"test_data/port_channel_estimator_test_pilots170.dat"}, {"test_data/port_channel_estimator_test_output_ch_est170.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.93187, 0.99746, 20, 11.3405, 0.06844, 0, 810, 649.9723, {"test_data/port_channel_estimator_test_input_rg171.dat"}, {"test_data/port_channel_estimator_test_pilots171.dat"}, {"test_data/port_channel_estimator_test_output_ch_est171.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.87688, 0.97597, 20, 9.2839, 0.10341, 0, 0, {}, {"test_data/port_channel_estimator_test_input_rg172.dat"}, {"test_data/port_channel_estimator_test_pilots172.dat"}, {"test_data/port_channel_estimator_test_output_ch_est172.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 1.0103, 1.0199, 20, 20.0477, 0.0099925, 0, 210, {}, {"test_data/port_channel_estimator_test_input_rg173.dat"}, {"test_data/port_channel_estimator_test_pilots173.dat"}, {"test_data/port_channel_estimator_test_output_ch_est173.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.94846, 1.0197, 20, 11.0582, 0.074336, 0, -390, {}, {"test_data/port_channel_estimator_test_input_rg174.dat"}, {"test_data/port_channel_estimator_test_pilots174.dat"}, {"test_data/port_channel_estimator_test_output_ch_est174.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 8, 4, {{{0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, 10, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.93199, 0.99693, 20, 11.3839, 0.067767, 0, 810, {}, {"test_data/port_channel_estimator_test_input_rg175.dat"}, {"test_data/port_channel_estimator_test_pilots175.dat"}, {"test_data/port_channel_estimator_test_output_ch_est175.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.94295, 1.0079, 20, 11.564, 0.06578, 0, 0, -10.9232, {"test_data/port_channel_estimator_test_input_rg176.dat"}, {"test_data/port_channel_estimator_test_pilots176.dat"}, {"test_data/port_channel_estimator_test_output_ch_est176.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.91816, 1.0107, 20, 9.9139, 0.093654, 0, 105, 71.9945, {"test_data/port_channel_estimator_test_input_rg177.dat"}, {"test_data/port_channel_estimator_test_pilots177.dat"}, {"test_data/port_channel_estimator_test_output_ch_est177.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.86905, 0.99308, 20, 8.4032, 0.12552, 0, -195, -180.263, {"test_data/port_channel_estimator_test_input_rg178.dat"}, {"test_data/port_channel_estimator_test_pilots178.dat"}, {"test_data/port_channel_estimator_test_output_ch_est178.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.55186, 1.028, 20, 0.58871, 0.4819, 0, 405, 397.309, {"test_data/port_channel_estimator_test_input_rg179.dat"}, {"test_data/port_channel_estimator_test_pilots179.dat"}, {"test_data/port_channel_estimator_test_output_ch_est179.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.98257, 0.9917, 20, 20.2669, 0.00924, 0, 0, 33.071, {"test_data/port_channel_estimator_test_input_rg180.dat"}, {"test_data/port_channel_estimator_test_pilots180.dat"}, {"test_data/port_channel_estimator_test_output_ch_est180.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.99032, 1.0123, 20, 16.4907, 0.022218, 0, 105, 120.9811, {"test_data/port_channel_estimator_test_input_rg181.dat"}, {"test_data/port_channel_estimator_test_pilots181.dat"}, {"test_data/port_channel_estimator_test_output_ch_est181.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.8915, 0.99116, 20, 9.4641, 0.10086, 0, -195, -218.0264, {"test_data/port_channel_estimator_test_input_rg182.dat"}, {"test_data/port_channel_estimator_test_pilots182.dat"}, {"test_data/port_channel_estimator_test_output_ch_est182.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.86475, 1.0097, 20, 7.706, 0.14665, 0, 405, 390.6045, {"test_data/port_channel_estimator_test_input_rg183.dat"}, {"test_data/port_channel_estimator_test_pilots183.dat"}, {"test_data/port_channel_estimator_test_output_ch_est183.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.9069, 1.0086, 20, 9.4484, 0.10297, 0, 0, -64.159, {"test_data/port_channel_estimator_test_input_rg184.dat"}, {"test_data/port_channel_estimator_test_pilots184.dat"}, {"test_data/port_channel_estimator_test_output_ch_est184.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.99956, 1.0462, 20, 13.2574, 0.047214, 0, 210, 124.8706, {"test_data/port_channel_estimator_test_input_rg185.dat"}, {"test_data/port_channel_estimator_test_pilots185.dat"}, {"test_data/port_channel_estimator_test_output_ch_est185.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.8534, 1.0215, 20, 7.0032, 0.17015, 0, -390, -337.389, {"test_data/port_channel_estimator_test_input_rg186.dat"}, {"test_data/port_channel_estimator_test_pilots186.dat"}, {"test_data/port_channel_estimator_test_output_ch_est186.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.50482, 0.9828, 20, 0.18525, 0.48374, 0, 810, 933.8251, {"test_data/port_channel_estimator_test_input_rg187.dat"}, {"test_data/port_channel_estimator_test_pilots187.dat"}, {"test_data/port_channel_estimator_test_output_ch_est187.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.97935, 0.98921, 20, 19.9174, 0.0099817, 0, 0, -32.2383, {"test_data/port_channel_estimator_test_input_rg188.dat"}, {"test_data/port_channel_estimator_test_pilots188.dat"}, {"test_data/port_channel_estimator_test_output_ch_est188.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.87925, 1.0088, 20, 8.263, 0.13116, 0, 210, 196.6504, {"test_data/port_channel_estimator_test_input_rg189.dat"}, {"test_data/port_channel_estimator_test_pilots189.dat"}, {"test_data/port_channel_estimator_test_output_ch_est189.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.89875, 1.0002, 20, 9.4208, 0.1027, 0, -390, -458.318, {"test_data/port_channel_estimator_test_input_rg190.dat"}, {"test_data/port_channel_estimator_test_pilots190.dat"}, {"test_data/port_channel_estimator_test_output_ch_est190.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 14, {{{1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 7, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::mean, false, 52, 0.80245, 1.0194, 20, 5.6284, 0.21957, 0, 810, 791.2853, {"test_data/port_channel_estimator_test_input_rg191.dat"}, {"test_data/port_channel_estimator_test_pilots191.dat"}, {"test_data/port_channel_estimator_test_output_ch_est191.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.94455, 0.98486, 20, 22.2251, 0.0056587, 0.84635, 0, -22.7317, {"test_data/port_channel_estimator_test_input_rg192.dat"}, {"test_data/port_channel_estimator_test_pilots192.dat"}, {"test_data/port_channel_estimator_test_output_ch_est192.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.9919, 0.99516, 20, 24.0566, 0.0038977, 0, 105, 149.2853, {"test_data/port_channel_estimator_test_input_rg193.dat"}, {"test_data/port_channel_estimator_test_pilots193.dat"}, {"test_data/port_channel_estimator_test_output_ch_est193.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0342, 1.038, 20, 20.4215, 0.0093859, 0.17904, -195, -23.9624, {"test_data/port_channel_estimator_test_input_rg194.dat"}, {"test_data/port_channel_estimator_test_pilots194.dat"}, {"test_data/port_channel_estimator_test_output_ch_est194.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.95935, 0.97837, 20, 21.271, 0.0071594, 0.35807, 405, 453.1327, {"test_data/port_channel_estimator_test_input_rg195.dat"}, {"test_data/port_channel_estimator_test_pilots195.dat"}, {"test_data/port_channel_estimator_test_output_ch_est195.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.98141, 0.99245, 20, 20.0843, 0.0096254, 0.31738, 0, {}, {"test_data/port_channel_estimator_test_input_rg196.dat"}, {"test_data/port_channel_estimator_test_pilots196.dat"}, {"test_data/port_channel_estimator_test_output_ch_est196.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.98156, 0.99998, 20, 24.9737, 0.0031228, 0.6429, 105, {}, {"test_data/port_channel_estimator_test_input_rg197.dat"}, {"test_data/port_channel_estimator_test_pilots197.dat"}, {"test_data/port_channel_estimator_test_output_ch_est197.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.91543, 0.96864, 20, 24.1975, 0.0034824, 0.96842, -195, {}, {"test_data/port_channel_estimator_test_input_rg198.dat"}, {"test_data/port_channel_estimator_test_pilots198.dat"}, {"test_data/port_channel_estimator_test_output_ch_est198.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.96485, 0.96237, 20, 25.2541, 0.0028777, 0.236, 405, {}, {"test_data/port_channel_estimator_test_input_rg199.dat"}, {"test_data/port_channel_estimator_test_pilots199.dat"}, {"test_data/port_channel_estimator_test_output_ch_est199.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0487, 1.0643, 20, 21.6288, 0.0072073, 0.04069, 0, 180.6473, {"test_data/port_channel_estimator_test_input_rg200.dat"}, {"test_data/port_channel_estimator_test_pilots200.dat"}, {"test_data/port_channel_estimator_test_output_ch_est200.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0081, 1.0297, 20, 21.9043, 0.0065025, 0.11393, 210, -218.5222, {"test_data/port_channel_estimator_test_input_rg201.dat"}, {"test_data/port_channel_estimator_test_pilots201.dat"}, {"test_data/port_channel_estimator_test_output_ch_est201.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.95746, 0.96478, 20, 23.5468, 0.0042309, 0.016276, -390, -811.3694, {"test_data/port_channel_estimator_test_input_rg202.dat"}, {"test_data/port_channel_estimator_test_pilots202.dat"}, {"test_data/port_channel_estimator_test_output_ch_est202.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0446, 1.0685, 20, 21.6689, 0.0071131, 0.30111, 810, 610.8116, {"test_data/port_channel_estimator_test_input_rg203.dat"}, {"test_data/port_channel_estimator_test_pilots203.dat"}, {"test_data/port_channel_estimator_test_output_ch_est203.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.99625, 1.0034, 20, 22.7754, 0.0052581, -0.012207, 0, {}, {"test_data/port_channel_estimator_test_input_rg204.dat"}, {"test_data/port_channel_estimator_test_pilots204.dat"}, {"test_data/port_channel_estimator_test_output_ch_est204.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.93415, 1.0102, 20, 22.4348, 0.0053326, 0.5778, 210, {}, {"test_data/port_channel_estimator_test_input_rg205.dat"}, {"test_data/port_channel_estimator_test_pilots205.dat"}, {"test_data/port_channel_estimator_test_output_ch_est205.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 0.98099, 1.0716, 20, 21.0267, 0.0077446, 0.58187, -390, {}, {"test_data/port_channel_estimator_test_input_rg206.dat"}, {"test_data/port_channel_estimator_test_pilots206.dat"}, {"test_data/port_channel_estimator_test_output_ch_est206.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 0, 2, {{{1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 1, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0514, 1.0672, 20, 20.3809, 0.0096313, 0.20752, 810, {}, {"test_data/port_channel_estimator_test_input_rg207.dat"}, {"test_data/port_channel_estimator_test_pilots207.dat"}, {"test_data/port_channel_estimator_test_output_ch_est207.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0111, 1.0115, 20, 21.6756, 0.0068744, 0.21159, 0, {}, {"test_data/port_channel_estimator_test_input_rg208.dat"}, {"test_data/port_channel_estimator_test_pilots208.dat"}, {"test_data/port_channel_estimator_test_output_ch_est208.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0372, 0.94078, 20, 21.7701, 0.0069003, 0.86263, 105, {}, {"test_data/port_channel_estimator_test_input_rg209.dat"}, {"test_data/port_channel_estimator_test_pilots209.dat"}, {"test_data/port_channel_estimator_test_output_ch_est209.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0419, 1.0205, 20, 20.8719, 0.0085242, 0.42318, -195, {}, {"test_data/port_channel_estimator_test_input_rg210.dat"}, {"test_data/port_channel_estimator_test_pilots210.dat"}, {"test_data/port_channel_estimator_test_output_ch_est210.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0498, 1.0179, 20, 21.9845, 0.0066476, 0.52083, 405, {}, {"test_data/port_channel_estimator_test_input_rg211.dat"}, {"test_data/port_channel_estimator_test_pilots211.dat"}, {"test_data/port_channel_estimator_test_output_ch_est211.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0154, 1.0154, 20, 22.4408, 0.0057882, 0.048828, 0, {}, {"test_data/port_channel_estimator_test_input_rg212.dat"}, {"test_data/port_channel_estimator_test_pilots212.dat"}, {"test_data/port_channel_estimator_test_output_ch_est212.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1693, 1.0343, 20, 20.1726, 0.011237, 0.53711, 210, {}, {"test_data/port_channel_estimator_test_input_rg213.dat"}, {"test_data/port_channel_estimator_test_pilots213.dat"}, {"test_data/port_channel_estimator_test_output_ch_est213.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0223, 0.98408, 20, 21.7481, 0.0068355, 0.26855, -390, {}, {"test_data/port_channel_estimator_test_input_rg214.dat"}, {"test_data/port_channel_estimator_test_pilots214.dat"}, {"test_data/port_channel_estimator_test_output_ch_est214.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 1, {{{0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0014, 1.0058, 20, 21.831, 0.0065689, 0.10579, 810, {}, {"test_data/port_channel_estimator_test_input_rg215.dat"}, {"test_data/port_channel_estimator_test_pilots215.dat"}, {"test_data/port_channel_estimator_test_output_ch_est215.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.13, 0.99786, 20, 19.4474, 0.012833, 1.0742, 0, -14.6325, {"test_data/port_channel_estimator_test_input_rg216.dat"}, {"test_data/port_channel_estimator_test_pilots216.dat"}, {"test_data/port_channel_estimator_test_output_ch_est216.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1127, 0.99807, 20, 19.9962, 0.011137, 0.96029, 105, 108.8613, {"test_data/port_channel_estimator_test_input_rg217.dat"}, {"test_data/port_channel_estimator_test_pilots217.dat"}, {"test_data/port_channel_estimator_test_output_ch_est217.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1136, 0.98528, 20, 19.3951, 0.0128, 1.0742, -195, -195.6815, {"test_data/port_channel_estimator_test_input_rg218.dat"}, {"test_data/port_channel_estimator_test_pilots218.dat"}, {"test_data/port_channel_estimator_test_output_ch_est218.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0871, 1.0133, 20, 20.3881, 0.0099421, 0.7487, 405, 374.2463, {"test_data/port_channel_estimator_test_input_rg219.dat"}, {"test_data/port_channel_estimator_test_pilots219.dat"}, {"test_data/port_channel_estimator_test_output_ch_est219.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1257, 1.0111, 20, 19.9926, 0.011276, 0.96029, 0, {}, {"test_data/port_channel_estimator_test_input_rg220.dat"}, {"test_data/port_channel_estimator_test_pilots220.dat"}, {"test_data/port_channel_estimator_test_output_ch_est220.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0475, 1.026, 20, 20.9158, 0.0084834, 0.42318, 105, {}, {"test_data/port_channel_estimator_test_input_rg221.dat"}, {"test_data/port_channel_estimator_test_pilots221.dat"}, {"test_data/port_channel_estimator_test_output_ch_est221.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0098, 1.008, 20, 22.4995, 0.0056792, 0.21159, -195, {}, {"test_data/port_channel_estimator_test_input_rg222.dat"}, {"test_data/port_channel_estimator_test_pilots222.dat"}, {"test_data/port_channel_estimator_test_output_ch_est222.dat"}}, + {{subcarrier_spacing::kHz15, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0167, 1.0079, 20, 21.5017, 0.0071951, 0.32552, 405, {}, {"test_data/port_channel_estimator_test_input_rg223.dat"}, {"test_data/port_channel_estimator_test_pilots223.dat"}, {"test_data/port_channel_estimator_test_output_ch_est223.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0227, 1.0222, 20, 20.8067, 0.0084934, 0.10579, 0, -29.2367, {"test_data/port_channel_estimator_test_input_rg224.dat"}, {"test_data/port_channel_estimator_test_pilots224.dat"}, {"test_data/port_channel_estimator_test_output_ch_est224.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0152, 1.0085, 20, 20.3372, 0.0093934, 0.16276, 210, 355.6766, {"test_data/port_channel_estimator_test_input_rg225.dat"}, {"test_data/port_channel_estimator_test_pilots225.dat"}, {"test_data/port_channel_estimator_test_output_ch_est225.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0281, 1.0265, 20, 21.5362, 0.0072178, 0.10579, -390, -404.4954, {"test_data/port_channel_estimator_test_input_rg226.dat"}, {"test_data/port_channel_estimator_test_pilots226.dat"}, {"test_data/port_channel_estimator_test_output_ch_est226.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {}, std::nullopt, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0087, 1.0159, 20, 20.3234, 0.0093628, 0.056966, 810, 809.6546, {"test_data/port_channel_estimator_test_input_rg227.dat"}, {"test_data/port_channel_estimator_test_pilots227.dat"}, {"test_data/port_channel_estimator_test_output_ch_est227.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.1346, 1.0002, 20, 19.7542, 0.012007, 0.53711, 0, {}, {"test_data/port_channel_estimator_test_input_rg228.dat"}, {"test_data/port_channel_estimator_test_pilots228.dat"}, {"test_data/port_channel_estimator_test_output_ch_est228.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0101, 1.0173, 20, 21.514, 0.0071278, 0, 210, {}, {"test_data/port_channel_estimator_test_input_rg229.dat"}, {"test_data/port_channel_estimator_test_pilots229.dat"}, {"test_data/port_channel_estimator_test_output_ch_est229.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.0149, 1.0203, 20, 21.3745, 0.0073955, 0.056966, -390, {}, {"test_data/port_channel_estimator_test_input_rg230.dat"}, {"test_data/port_channel_estimator_test_pilots230.dat"}, {"test_data/port_channel_estimator_test_output_ch_est230.dat"}}, + {{subcarrier_spacing::kHz30, cyclic_prefix::NORMAL, 5, 2, {{{0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0}, 6, {0, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0}}}, {0}, 1}, port_channel_estimator_fd_smoothing_strategy::filter, true, 52, 1.003, 1.0037, 20, 21.448, 0.0071862, 0.10579, 810, {}, {"test_data/port_channel_estimator_test_input_rg231.dat"}, {"test_data/port_channel_estimator_test_pilots231.dat"}, {"test_data/port_channel_estimator_test_output_ch_est231.dat"}}, // clang-format on }; diff --git a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.tar.gz b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.tar.gz index 0c32bfafb4..a3850ac637 100644 --- a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.tar.gz +++ b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test_data.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a9baf39fc6e42368ea152b6faa847bd10b57285517dca7258c87d65e21480541 -size 4422318 +oid sha256:5f3cc22805f105b085000c16418480160925021e211ea88c991fd02859208e3b +size 14196293 From bb4716eeb2399495808ccdf8b17ee128849f733f Mon Sep 17 00:00:00 2001 From: dvdgrgrtt Date: Mon, 23 Dec 2024 09:09:40 +0100 Subject: [PATCH 094/107] pusch: update capabilities to four layers --- .../upper/channel_processors/pusch/pusch_processor_impl.h | 2 +- .../pusch/pusch_processor_benchmark.cpp | 8 +++++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/lib/phy/upper/channel_processors/pusch/pusch_processor_impl.h b/lib/phy/upper/channel_processors/pusch/pusch_processor_impl.h index e5a850c9e9..08fa036d86 100644 --- a/lib/phy/upper/channel_processors/pusch/pusch_processor_impl.h +++ b/lib/phy/upper/channel_processors/pusch/pusch_processor_impl.h @@ -31,7 +31,7 @@ class pusch_processor_impl : public pusch_processor { public: /// The current maximum supported number of layers. - static constexpr unsigned max_nof_layers = 2; + static constexpr unsigned max_nof_layers = 4; /// Groups the PUSCH processor dependencies that can be reused locally by the same processing thread. class concurrent_dependencies diff --git a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp index 93332bdd34..7540e8f57b 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp @@ -13,6 +13,7 @@ #include "srsran/phy/support/resource_grid_writer.h" #include "srsran/phy/support/support_factories.h" #include "srsran/phy/upper/channel_processors/pusch/factories.h" +#include "srsran/phy/upper/channel_processors/pusch/pusch_processor_phy_capabilities.h" #include "srsran/phy/upper/channel_processors/pusch/pusch_processor_result_notifier.h" #include "srsran/ran/sch/tbs_calculator.h" #include "srsran/support/benchmark_utils.h" @@ -179,7 +180,7 @@ static const std::vector profile_set = { {{modulation_scheme::QAM256, 948.0F}}, {0}, 4, - {1, 2}}, + {1, 2, 3, 4}}, {"scs30_100MHz_256qam_rvall_1port_1layer", "Decodes PUSCH with 30 kHz SCS, 100 MHz of bandwidth, 256-QAM modulation, maximum code rate, RV 0-3, 1 port, " @@ -350,10 +351,15 @@ static std::vector generate_test_cases(const test_profile& profi { std::vector test_case_set; + unsigned max_nof_layers = get_pusch_processor_phy_capabilities().max_nof_layers; + for (sch_mcs_description mcs : profile.mcs_set) { for (unsigned nof_prb : profile.nof_prb_set) { for (unsigned rv : profile.rv_set) { for (unsigned nof_layers : profile.nof_layers_set) { + if (nof_layers > max_nof_layers) { + continue; + } // Determine the Transport Block Size. tbs_calculator_configuration tbs_config = {}; tbs_config.mcs_descr = mcs; From d5fce09952ee77267950f7855df8e4fd20f3808b Mon Sep 17 00:00:00 2001 From: dvdgrgrtt Date: Mon, 23 Dec 2024 14:20:01 +0100 Subject: [PATCH 095/107] phy: simplify channel estimates SINR, EPRE and noise variance are now estimated only per Rx port as opposed to per port-layer paths, which didn't make much sense. --- include/srsran/phy/upper/channel_estimation.h | 77 +++++++------------ .../phy/upper/pucch_formats3_4_helpers.h | 2 +- .../pucch/pucch_demodulator_format2.cpp | 2 +- .../pusch/pusch_demodulator_impl.cpp | 2 +- .../port_channel_estimator_average_impl.cpp | 6 +- .../pusch/pusch_demodulator_vectortest.cpp | 2 +- .../dmrs_pusch_estimator_test.cpp | 14 +++- .../port_channel_estimator_test.cpp | 27 ++++--- 8 files changed, 62 insertions(+), 70 deletions(-) diff --git a/include/srsran/phy/upper/channel_estimation.h b/include/srsran/phy/upper/channel_estimation.h index 17ca2f06d7..e44a7813ab 100644 --- a/include/srsran/phy/upper/channel_estimation.h +++ b/include/srsran/phy/upper/channel_estimation.h @@ -90,14 +90,14 @@ class channel_estimate std::fill(data.begin(), data.end(), 1.0F); // Reserve memory for the rest of channel statistics. - noise_variance.reserve(MAX_TX_RX_PATHS); - noise_variance.resize(nof_paths); - epre.reserve(MAX_TX_RX_PATHS); - epre.resize(nof_paths); + noise_variance.reserve(MAX_RX_PORTS); + noise_variance.resize(nof_rx_ports); + epre.reserve(MAX_RX_PORTS); + epre.resize(nof_rx_ports); rsrp.reserve(MAX_TX_RX_PATHS); rsrp.resize(nof_paths); - snr.reserve(MAX_TX_RX_PATHS); - snr.resize(nof_paths); + snr.reserve(MAX_RX_PORTS); + snr.resize(nof_rx_ports); time_alignment.reserve(MAX_TX_RX_PATHS); time_alignment.resize(nof_paths); cfo.reserve(MAX_TX_RX_PATHS); @@ -109,17 +109,11 @@ class channel_estimate /// \name Getters. ///@{ - /// Returns the estimated noise variance for the path between the given Rx port and Tx layer (linear scale). - float get_noise_variance(unsigned rx_port, unsigned tx_layer = 0) const - { - return noise_variance[path_to_index(rx_port, tx_layer)]; - } + /// Returns the estimated noise variance for the given Rx port (linear scale). + float get_noise_variance(unsigned rx_port) const { return noise_variance[rx_port]; } - /// Returns the estimated noise variance for the path between the given Rx port and Tx layer (dB scale). - float get_noise_variance_dB(unsigned rx_port, unsigned tx_layer = 0) const - { - return convert_power_to_dB(get_noise_variance(rx_port, tx_layer)); - } + /// Returns the estimated noise variance for the given Rx port (dB scale). + float get_noise_variance_dB(unsigned rx_port) const { return convert_power_to_dB(get_noise_variance(rx_port)); } /// Returns the estimated RSRP for the path between the given Rx port and Tx layer (linear scale). float get_rsrp(unsigned rx_port, unsigned tx_layer = 0) const { return rsrp[path_to_index(rx_port, tx_layer)]; } @@ -130,21 +124,18 @@ class channel_estimate return convert_power_to_dB(get_rsrp(rx_port, tx_layer)); } - /// \brief Returns the average EPRE for the path between the given Rx port and Tx layer (linear scale). + /// \brief Returns the average EPRE for the given Rx port (linear scale). /// /// \remark The EPRE is defined as the average received power (including noise) across all REs carrying DM-RS. - float get_epre(unsigned rx_port, unsigned tx_layer = 0) const { return epre[path_to_index(rx_port, tx_layer)]; } + float get_epre(unsigned rx_port) const { return epre[rx_port]; } - /// \brief Returns the average EPRE for the path between the given Rx port and Tx layer (dB scale). + /// \brief Returns the average EPRE for the given Rx port (dB scale). /// /// \remark The EPRE is defined as the average received power (including noise) across all REs carrying DM-RS. - float get_epre_dB(unsigned rx_port, unsigned tx_layer = 0) const - { - return convert_power_to_dB(get_epre(rx_port, tx_layer)); - } + float get_epre_dB(unsigned rx_port) const { return convert_power_to_dB(get_epre(rx_port)); } - /// Returns the estimated SNR for the path between the given Rx port and Tx layer (linear scale). - float get_snr(unsigned rx_port, unsigned tx_layer = 0) const { return snr[path_to_index(rx_port, tx_layer)]; } + /// Returns the estimated SNR for the given Rx port (linear scale). + float get_snr(unsigned rx_port) const { return snr[rx_port]; } /// Returns the estimated average SNR for a given layer (linear scale). float get_layer_average_snr(unsigned tx_layer = 0) const @@ -154,7 +145,7 @@ class channel_estimate // Add the noise and signal power contributions of all Rx ports. for (unsigned i_rx_port = 0; i_rx_port != nof_rx_ports; ++i_rx_port) { - noise_var_all_ports += noise_variance[path_to_index(i_rx_port, tx_layer)]; + noise_var_all_ports += noise_variance[i_rx_port]; rsrp_all_ports += rsrp[path_to_index(i_rx_port, tx_layer)]; } @@ -166,11 +157,8 @@ class channel_estimate return 1e6; } - /// Returns the estimated SNR for the path between the given Rx port and Tx layer (dB scale). - float get_snr_dB(unsigned rx_port, unsigned tx_layer = 0) const - { - return convert_power_to_dB(get_snr(rx_port, tx_layer)); - } + /// Returns the estimated SNR for the given Rx port (dB scale). + float get_snr_dB(unsigned rx_port) const { return convert_power_to_dB(get_snr(rx_port)); } /// Returns the estimated time alignment in PHY time units between the given Rx port and Tx layer. phy_time_unit get_time_alignment(unsigned rx_port, unsigned tx_layer = 0) const @@ -244,11 +232,11 @@ class channel_estimate float best_path_snr = 0.0F; for (unsigned i_rx_port = 0; i_rx_port != nof_rx_ports; ++i_rx_port) { // Accumulate EPRE and RSRP values. - epre_lin += get_epre(i_rx_port, 0); + epre_lin += get_epre(i_rx_port); rsrp_lin += get_rsrp(i_rx_port, 0); // Determine the Rx port with better SNR. - float port_snr = get_snr(i_rx_port, 0); + float port_snr = get_snr(i_rx_port); if (port_snr > best_path_snr) { best_path_snr = port_snr; best_rx_port = i_rx_port; @@ -279,29 +267,20 @@ class channel_estimate /// \name Setters. ///@{ - /// Sets the estimated noise variance for the path between the given Rx port and Tx layer (linear scale). - void set_noise_variance(float var_val, unsigned rx_port, unsigned tx_layer = 0) - { - noise_variance[path_to_index(rx_port, tx_layer)] = var_val; - } + /// Sets the estimated noise variance for the given Rx port (linear scale). + void set_noise_variance(float var_val, unsigned rx_port) { noise_variance[rx_port] = var_val; } - /// Sets the estimated RSRP for the path between the given Rx port and Tx layer (linear scale). + /// Sets the estimated RSRP for the given Rx port and Tx layer (linear scale). void set_rsrp(float rsrp_val, unsigned rx_port, unsigned tx_layer = 0) { rsrp[path_to_index(rx_port, tx_layer)] = rsrp_val; } - /// Sets the average EPRE for the path between the given Rx port and Tx layer (linear scale). - void set_epre(float epre_val, unsigned rx_port, unsigned tx_layer = 0) - { - epre[path_to_index(rx_port, tx_layer)] = epre_val; - } + /// Sets the average EPRE for the given Rx port (linear scale). + void set_epre(float epre_val, unsigned rx_port) { epre[rx_port] = epre_val; } - /// Sets the estimated SNR for the path between the given Rx port and Tx layer (linear scale). - void set_snr(float snr_val, unsigned rx_port, unsigned tx_layer = 0) - { - snr[path_to_index(rx_port, tx_layer)] = snr_val; - } + /// Sets the estimated SNR for the given Rx port (linear scale). + void set_snr(float snr_val, unsigned rx_port) { snr[rx_port] = snr_val; } /// Sets the estimated time alignment in PHY units of time for the path between the given Rx port and Tx layer. void set_time_alignment(phy_time_unit ta, unsigned rx_port, unsigned tx_layer = 0) diff --git a/include/srsran/phy/upper/pucch_formats3_4_helpers.h b/include/srsran/phy/upper/pucch_formats3_4_helpers.h index 6a112984fa..c63bb2cfa7 100644 --- a/include/srsran/phy/upper/pucch_formats3_4_helpers.h +++ b/include/srsran/phy/upper/pucch_formats3_4_helpers.h @@ -169,7 +169,7 @@ inline void pucch_3_4_extract_and_equalize(span eq_re, // Extract the Rx port noise variances from the channel estimation. std::array noise_var_estimates; for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { - noise_var_estimates[i_port] = estimates.get_noise_variance(i_port, 0); + noise_var_estimates[i_port] = estimates.get_noise_variance(i_port); } for (unsigned i_symbol = start_symbol_index, i_symbol_end = start_symbol_index + nof_symbols; diff --git a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp index ae934f1568..c62d27438c 100644 --- a/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp +++ b/lib/phy/upper/channel_processors/pucch/pucch_demodulator_format2.cpp @@ -125,7 +125,7 @@ void pucch_demodulator_format2::demodulate(span // Extract the Rx port noise variances from the channel estimation. for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { - noise_var_estimates[i_port] = estimates.get_noise_variance(i_port, 0); + noise_var_estimates[i_port] = estimates.get_noise_variance(i_port); } // Equalize the data RE. diff --git a/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.cpp b/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.cpp index 9c82824c18..700938d330 100644 --- a/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.cpp +++ b/lib/phy/upper/channel_processors/pusch/pusch_demodulator_impl.cpp @@ -175,7 +175,7 @@ void pusch_demodulator_impl::demodulate(pusch_codeword_buffer& codeword_buf // Extract the Rx port noise variances from the channel estimation. for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { - noise_var_estimates[i_port] = estimates.get_noise_variance(i_port, 0); + noise_var_estimates[i_port] = estimates.get_noise_variance(i_port); } // Equalize channels and, for each Tx layer, combine contribution from all Rx antenna ports. diff --git a/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp b/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp index 699a997142..734883acc5 100644 --- a/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp +++ b/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp @@ -134,7 +134,7 @@ void port_channel_estimator_average_impl::do_compute(channel_estimate& } estimate.set_rsrp(rsrp, port, layer0); - estimate.set_epre(epre, port, layer0); + estimate.set_epre(epre, port); estimate.set_time_alignment(phy_time_unit::from_seconds(time_alignment_s), port, layer0); estimate.set_cfo_Hz(cfo_normalized.has_value() ? std::optional(cfo_normalized.value() * scs_to_khz(cfg.scs) * 1000) @@ -143,9 +143,9 @@ void port_channel_estimator_average_impl::do_compute(channel_estimate& layer0); // Write the noise variance in the channel estimate result. - estimate.set_noise_variance(noise_var, port, layer0); + estimate.set_noise_variance(noise_var, port); - estimate.set_snr((noise_var != 0) ? datarp / noise_var : 1000, port, layer0); + estimate.set_snr((noise_var != 0) ? datarp / noise_var : 1000, port); } void port_channel_estimator_average_impl::compute_hop(srsran::channel_estimate& estimate, diff --git a/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_vectortest.cpp index c165bd7136..beeeaa983d 100644 --- a/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/pusch/pusch_demodulator_vectortest.cpp @@ -147,7 +147,7 @@ TEST_P(PuschDemodulatorFixture, PuschDemodulatorUnittest) for (unsigned i_rx_port = 0; i_rx_port != ce_dims.nof_rx_ports; ++i_rx_port) { for (unsigned i_layer = 0; i_layer != ce_dims.nof_tx_layers; ++i_layer) { // Set noise variance. - chan_estimates.set_noise_variance(test_case.context.noise_var, config.rx_ports[i_rx_port], i_layer); + chan_estimates.set_noise_variance(test_case.context.noise_var, config.rx_ports[i_rx_port]); // Copy port channel estimates. srsvec::convert(chan_estimates.get_path_ch_estimate(config.rx_ports[i_rx_port], i_layer), diff --git a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp index 7ee138e2ca..e82bf9ae4c 100644 --- a/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/dmrs_pusch_estimator_test.cpp @@ -257,16 +257,26 @@ TEST_P(DmrsPuschEstimatorFixture, Average) // Estimate. estimator->estimate(ch_est, grid, config); + unsigned nof_rx_ports = config.rx_ports.size(); + // First, assert the channel estimate gets the proper dimensions. channel_estimate::channel_estimate_dimensions ch_estimate_dims = ch_est.size(); ASSERT_EQ(ch_estimate_dims.nof_prb, config.rb_mask.size()) << "Wrong number of PRBs."; ASSERT_EQ(ch_estimate_dims.nof_symbols, config.nof_symbols + config.first_symbol) << "Wrong number of symbols."; - ASSERT_EQ(ch_estimate_dims.nof_rx_ports, config.rx_ports.size()) << "Wrong number of Rx ports."; + ASSERT_EQ(ch_estimate_dims.nof_rx_ports, nof_rx_ports) << "Wrong number of Rx ports."; ASSERT_EQ(ch_estimate_dims.nof_tx_layers, config.get_nof_tx_layers()) << "Wrong number of Tx layers."; // Assert that the channel estimates are correct. ASSERT_TRUE(are_estimates_ok(expected_estimates, ch_est)); - ASSERT_NEAR(ch_est.get_rsrp(0, 0), GetParam().est_rsrp, tolerance); + + // Assert port-dependent estimates. + for (unsigned i_port = 0; i_port != nof_rx_ports; ++i_port) { + ASSERT_NEAR(ch_est.get_noise_variance(i_port), GetParam().est_noise_var, tolerance) + << "Wrong noise variance estimation."; + for (unsigned i_layer = 0; i_layer != nof_layers; ++i_layer) { + ASSERT_NEAR(ch_est.get_rsrp(i_port, i_layer), GetParam().est_rsrp, tolerance) << "Wrong RSRP estimation."; + } + } } // Creates test suite with all the test cases. diff --git a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp index d869ab019b..71c88d6b5d 100644 --- a/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp +++ b/tests/unittests/phy/upper/signal_processors/port_channel_estimator_test.cpp @@ -175,18 +175,21 @@ TEST_P(ChannelEstFixture, test) // Calculate the tolerance for the measured TA. It assumes a DFT size of 4096 and a maximum error of ±1 sample. double tolerance_ta_us = 1e6 * phy_time_unit::from_timing_advance(1, test_params.cfg.scs).to_seconds(); ASSERT_TRUE(are_estimates_ok(expected_estimates, estimates)); - ASSERT_NEAR(estimates.get_rsrp(0, 0), test_params.rsrp, 5e-4); - ASSERT_NEAR(estimates.get_epre(0, 0), test_params.epre, 5e-4); - ASSERT_NEAR(estimates.get_noise_variance(0, 0), test_params.noise_var_est, 5e-4); - ASSERT_NEAR(estimates.get_snr_dB(0, 0), test_params.snr_est, 0.002 * std::abs(test_params.snr_est)); - ASSERT_NEAR(estimates.get_time_alignment(0, 0).to_seconds() * 1e6, test_params.ta_us, tolerance_ta_us); - if (test_params.cfo_est_Hz.has_value()) { - ASSERT_TRUE(estimates.get_cfo_Hz(0, 0).has_value()) << "CFO estimation was expected, none obtained."; - ASSERT_NEAR(estimates.get_cfo_Hz(0, 0).value(), - test_params.cfo_est_Hz.value(), - 0.001 * std::abs(test_params.cfo_est_Hz.value())); - } else { - ASSERT_FALSE(estimates.get_cfo_Hz(0, 0).has_value()) << "No CFO estimation was expected."; + ASSERT_NEAR(estimates.get_epre(0), test_params.epre, 5e-4); + ASSERT_NEAR(estimates.get_noise_variance(0), test_params.noise_var_est, 5e-4); + ASSERT_NEAR(estimates.get_snr_dB(0), test_params.snr_est, 0.002 * std::abs(test_params.snr_est)); + + for (unsigned i_layer = 0; i_layer != nof_layers; ++i_layer) { + ASSERT_NEAR(estimates.get_rsrp(0, i_layer), test_params.rsrp, 5e-4); + ASSERT_NEAR(estimates.get_time_alignment(0, i_layer).to_seconds() * 1e6, test_params.ta_us, tolerance_ta_us); + if (test_params.cfo_est_Hz.has_value()) { + ASSERT_TRUE(estimates.get_cfo_Hz(0, i_layer).has_value()) << "CFO estimation was expected, none obtained."; + ASSERT_NEAR(estimates.get_cfo_Hz(0, i_layer).value(), + test_params.cfo_est_Hz.value(), + 0.001 * std::abs(test_params.cfo_est_Hz.value())); + } else { + ASSERT_FALSE(estimates.get_cfo_Hz(0, i_layer).has_value()) << "No CFO estimation was expected."; + } } } From 47f0012a66ebc7807164b27a2e0113e9b330d3b6 Mon Sep 17 00:00:00 2001 From: dvdgrgrtt Date: Mon, 23 Dec 2024 14:54:45 +0100 Subject: [PATCH 096/107] phy, ch estimator: minor refactor Move an auxiliary function from the main file to the helpers. --- .../port_channel_estimator_average_impl.cpp | 59 ------------------- .../port_channel_estimator_helpers.cpp | 48 +++++++++++++++ .../port_channel_estimator_helpers.h | 18 ++++++ 3 files changed, 66 insertions(+), 59 deletions(-) diff --git a/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp b/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp index 734883acc5..6d5ee6f547 100644 --- a/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp +++ b/lib/phy/upper/signal_processors/port_channel_estimator_average_impl.cpp @@ -27,21 +27,6 @@ using namespace srsran; #ifndef SRSRAN_HAS_ENTERPRISE -/// \brief Extracts channel observations corresponding to DM-RS pilots from the resource grid for one layer, one hop -/// and for the selected port. -/// \param[out] rx_symbols Symbol buffer destination. -/// \param[in] grid Resource grid. -/// \param[in] port Port index. -/// \param[in] cfg Configuration parameters of the current context. -/// \param[in] hop Intra-slot frequency hopping index: 0 for first position (before hopping), 1 for second -/// position (after hopping). -/// \return The number of OFDM symbols containing DM-RS for the given layer and hop. -static unsigned extract_layer_hop_rx_pilots(dmrs_symbol_list& rx_symbols, - const resource_grid_reader& grid, - unsigned port, - const port_channel_estimator::configuration& cfg, - unsigned hop); - /// \brief Estimates the noise energy of one hop. /// /// The layers in the given range are assumed to transmit DM-RS on the same resources. @@ -366,50 +351,6 @@ void port_channel_estimator_average_impl:: srsran_assertion_failure("Function not implemented."); } -static unsigned extract_layer_hop_rx_pilots(dmrs_symbol_list& rx_symbols, - const resource_grid_reader& grid, - unsigned port, - const port_channel_estimator::configuration& cfg, - unsigned hop) -{ - constexpr unsigned layer0 = 0; - // Select DM-RS pattern. - const port_channel_estimator::layer_dmrs_pattern& pattern = cfg.dmrs_pattern[layer0]; - - const bounded_bitset& hop_rb_mask = (hop == 0) ? pattern.rb_mask : pattern.rb_mask2; - - // Prepare RE mask, common for all symbols carrying DM-RS. - bounded_bitset re_mask = hop_rb_mask.kronecker_product(pattern.re_pattern); - - unsigned symbol_index = ((hop == 1) && pattern.hopping_symbol_index.has_value()) - ? pattern.hopping_symbol_index.value() - : cfg.first_symbol; - unsigned symbol_index_end = ((hop == 0) && pattern.hopping_symbol_index.has_value()) - ? pattern.hopping_symbol_index.value() - : cfg.first_symbol + cfg.nof_symbols; - unsigned dmrs_symbol_index = 0; - // For each OFDM symbol in the transmission... - for (; symbol_index != symbol_index_end; ++symbol_index) { - // Skip if the symbol does not carry DM-RS. - if (!pattern.symbols.test(symbol_index)) { - continue; - } - - // Select symbol buffer for the selected layer and symbol. - span layer_dmrs_symbols = rx_symbols.get_symbol(dmrs_symbol_index++, layer0); - - // Get DM-RS symbols from the resource grid. - layer_dmrs_symbols = grid.get(layer_dmrs_symbols, cfg.rx_ports[port], symbol_index, 0, re_mask); - - // The DM-RS symbol buffer must be complete. - srsran_assert(layer_dmrs_symbols.empty(), - "The DM-RS buffer is not completed. {} samples have not been read.", - layer_dmrs_symbols.size()); - } - - return dmrs_symbol_index; -} - static float estimate_noise(const dmrs_symbol_list& pilots, const dmrs_symbol_list& rx_pilots, const re_buffer_reader& estimates, diff --git a/lib/phy/upper/signal_processors/port_channel_estimator_helpers.cpp b/lib/phy/upper/signal_processors/port_channel_estimator_helpers.cpp index 161129def9..5713d6d118 100644 --- a/lib/phy/upper/signal_processors/port_channel_estimator_helpers.cpp +++ b/lib/phy/upper/signal_processors/port_channel_estimator_helpers.cpp @@ -107,6 +107,54 @@ static void compute_v_pilots(span out, span in_abs, span new_pilots, span old_pilots); +unsigned srsran::extract_layer_hop_rx_pilots(dmrs_symbol_list& rx_symbols, + const resource_grid_reader& grid, + unsigned port, + const port_channel_estimator::configuration& cfg, + unsigned hop, + unsigned i_layer) +{ + // CDM group the layer belongs to. Note that layers of the same CDM group (i.e., {0, 1} and {2, 3}) share DM-RS + // resources. + unsigned i_cdm = i_layer / 2; + + // Select DM-RS pattern. + const port_channel_estimator::layer_dmrs_pattern& pattern = cfg.dmrs_pattern[i_layer]; + + const bounded_bitset& hop_rb_mask = (hop == 0) ? pattern.rb_mask : pattern.rb_mask2; + + // Prepare RE mask, common for all symbols carrying DM-RS. + bounded_bitset re_mask = hop_rb_mask.kronecker_product(pattern.re_pattern); + + unsigned symbol_index = ((hop == 1) && pattern.hopping_symbol_index.has_value()) + ? pattern.hopping_symbol_index.value() + : cfg.first_symbol; + unsigned symbol_index_end = ((hop == 0) && pattern.hopping_symbol_index.has_value()) + ? pattern.hopping_symbol_index.value() + : cfg.first_symbol + cfg.nof_symbols; + unsigned dmrs_symbol_index = 0; + // For each OFDM symbol in the transmission... + for (; symbol_index != symbol_index_end; ++symbol_index) { + // Skip if the symbol does not carry DM-RS. + if (!pattern.symbols.test(symbol_index)) { + continue; + } + + // Select symbol buffer for the selected layer and symbol. + span layer_dmrs_symbols = rx_symbols.get_symbol(dmrs_symbol_index++, i_cdm); + + // Get DM-RS symbols from the resource grid. + layer_dmrs_symbols = grid.get(layer_dmrs_symbols, cfg.rx_ports[port], symbol_index, 0, re_mask); + + // The DM-RS symbol buffer must be complete. + srsran_assert(layer_dmrs_symbols.empty(), + "The DM-RS buffer is not completed. {} samples have not been read.", + layer_dmrs_symbols.size()); + } + + return dmrs_symbol_index; +} + void srsran::apply_fd_smoothing(span enlarged_filtered_pilots_out, span enlarged_pilots_in, unsigned nof_rb, diff --git a/lib/phy/upper/signal_processors/port_channel_estimator_helpers.h b/lib/phy/upper/signal_processors/port_channel_estimator_helpers.h index 8cb51f6a98..94f59c179c 100644 --- a/lib/phy/upper/signal_processors/port_channel_estimator_helpers.h +++ b/lib/phy/upper/signal_processors/port_channel_estimator_helpers.h @@ -14,12 +14,30 @@ #include "srsran/adt/span.h" #include "srsran/phy/support/interpolator.h" #include "srsran/phy/support/re_buffer.h" +#include "srsran/phy/support/resource_grid_reader.h" #include "srsran/phy/support/time_alignment_estimator/time_alignment_estimator.h" #include "srsran/phy/upper/signal_processors/port_channel_estimator.h" #include "srsran/phy/upper/signal_processors/port_channel_estimator_parameters.h" namespace srsran { +/// \brief Extracts channel observations corresponding to DM-RS pilots from the resource grid for one layer, one hop +/// and for the selected port. +/// \param[out] rx_symbols Symbol buffer destination. +/// \param[in] grid Resource grid. +/// \param[in] port Port index. +/// \param[in] cfg Configuration parameters of the current context. +/// \param[in] hop Intra-slot frequency hopping index: 0 for first position (before hopping), 1 for second +/// position (after hopping). +/// \param[in] i_layer Layer index. +/// \return The number of OFDM symbols containing DM-RS for the given layer and hop. +unsigned extract_layer_hop_rx_pilots(dmrs_symbol_list& rx_symbols, + const resource_grid_reader& grid, + unsigned port, + const port_channel_estimator::configuration& cfg, + unsigned hop, + unsigned i_layer = 0); + /// \brief Applies frequency domain smoothing strategy. /// \param[out] enlarged_filtered_pilots_out Smoothed pilots estimates. /// \param[in] enlarged_pilots_in Pilots estimates. From d2f1aa82b96ad29d4364efc23992db3778bd208a Mon Sep 17 00:00:00 2001 From: dvdgrgrtt Date: Tue, 31 Dec 2024 14:45:52 +0100 Subject: [PATCH 097/107] phy, equalizer: simplify 1xn case For one Tx layer, and after scaling for better LLR computation, the MMSE and ZF equalizer are equivalent. --- .../channel_equalizer_generic_impl.cpp | 41 +-- .../upper/equalization/equalize_mmse_1xn.h | 85 ------ .../channel_equalizer_test_data.h | 248 ++++++++---------- .../channel_equalizer_test_data.tar.gz | 4 +- 4 files changed, 121 insertions(+), 257 deletions(-) delete mode 100644 lib/phy/upper/equalization/equalize_mmse_1xn.h diff --git a/lib/phy/upper/equalization/channel_equalizer_generic_impl.cpp b/lib/phy/upper/equalization/channel_equalizer_generic_impl.cpp index e48000129f..c168f86bd4 100644 --- a/lib/phy/upper/equalization/channel_equalizer_generic_impl.cpp +++ b/lib/phy/upper/equalization/channel_equalizer_generic_impl.cpp @@ -12,7 +12,6 @@ /// \brief Channel equalizer implementation for the Zero Forcing and the Minimum Mean Square Error. #include "channel_equalizer_generic_impl.h" -#include "equalize_mmse_1xn.h" #include "equalize_zf_1xn.h" #include "equalize_zf_2xn.h" #include "srsran/adt/interval.h" @@ -175,40 +174,6 @@ void equalize_zf_single_tx_layer_reduction(span nof_valid_noise_var, eq_symbols, eq_noise_vars, ch_symbols, ch_estimates, noise_var, tx_scaling); } -/// Calls the equalizer function for receive spatial diversity with the appropriate number of receive ports. -template -void equalize_mmse_single_tx_layer(unsigned nof_ports, - span eq_symbols, - span eq_noise_vars, - const re_buffer_reader& ch_symbols, - const channel_equalizer::ch_est_list& ch_estimates, - span noise_var, - float tx_scaling) -{ - if (NOF_PORTS != nof_ports) { - // Recursive call. - return equalize_mmse_single_tx_layer( - nof_ports, eq_symbols, eq_noise_vars, ch_symbols, ch_estimates, noise_var, tx_scaling); - } - - // Perform equalization. - equalize_mmse_1xn(eq_symbols, eq_noise_vars, ch_symbols, ch_estimates, noise_var, tx_scaling); -} - -/// Specialization for a single receive port. -template <> -void equalize_mmse_single_tx_layer<1>(unsigned /**/, - span eq_symbols, - span eq_noise_vars, - const re_buffer_reader& ch_symbols, - const channel_equalizer::ch_est_list& ch_estimates, - span noise_var, - float tx_scaling) -{ - // Perform equalization. - equalize_mmse_1xn<1>(eq_symbols, eq_noise_vars, ch_symbols, ch_estimates, noise_var, tx_scaling); -} - #ifndef SRSRAN_HAS_ENTERPRISE void channel_equalizer_generic_impl::equalize_zf_3x4(span /* eq_symbols */, span /* noise_vars */, @@ -363,8 +328,10 @@ void channel_equalizer_generic_impl::equalize(span e if (type == channel_equalizer_algorithm_type::mmse) { // Single transmit layer and any number of receive ports. if (nof_tx_layers == 1) { - equalize_mmse_single_tx_layer( - nof_rx_ports, eq_symbols, eq_noise_vars, ch_symbols, ch_estimates, noise_var_estimates, tx_scaling); + // For one Tx layer, and including scaling for better LLR calculation, the MMSE equalizer is equivalent to the ZF + // one. + equalize_zf_single_tx_layer_reduction( + eq_symbols, eq_noise_vars, ch_symbols, ch_estimates, noise_var_estimates, tx_scaling); return; } diff --git a/lib/phy/upper/equalization/equalize_mmse_1xn.h b/lib/phy/upper/equalization/equalize_mmse_1xn.h deleted file mode 100644 index a9d1d90e75..0000000000 --- a/lib/phy/upper/equalization/equalize_mmse_1xn.h +++ /dev/null @@ -1,85 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the distribution. - * - */ - -/// \file -/// \brief Minimum Mean Square Error equalizer algorithm for a SIMO 1 X N channel. - -#pragma once - -#include "srsran/srsvec/simd.h" -#include "srsran/srsvec/zero.h" - -namespace srsran { - -/// \brief Implementation of an MMSE equalizer for a SIMO 1 X \c RX_PORTS channel. -/// \tparam RX_PORTS Number of receive antenna ports. -/// \param[out] eq_symbols Resultant equalized symbols. -/// \param[out] noise_vars Noise variances after equalization. -/// \param[in] ch_symbols Channel symbols, i.e., complex samples from the receive ports. -/// \param[in] ch_estimates Channel estimation coefficients. -/// \param[in] noise_var_est Estimated noise variance for each port. -/// \param[in] tx_scaling Transmission gain scaling factor. -template -void equalize_mmse_1xn(span symbols_out, - span nvars_out, - const re_buffer_reader& ch_symbols, - const channel_equalizer::ch_est_list& ch_estimates, - span noise_var_est, - float tx_scaling) -{ - // Number of RE to process. - unsigned nof_re = ch_symbols.get_nof_re(); - - unsigned i_re = 0; - - for (; i_re != nof_re; ++i_re) { - float ch_mod_sq = 0.0F; - float nvar_acc = 0.0F; - cf_t re_out = cf_t(); - - for (unsigned i_port = 0; i_port != RX_PORTS; ++i_port) { - // Get the input RE and channel estimate coefficient. - cf_t re_in = to_cf(ch_symbols.get_slice(i_port)[i_re]); - cf_t ch_est = to_cf(ch_estimates.get_channel(i_port, 0)[i_re]) * tx_scaling; - - // Compute the channel square norm. - float ch_est_norm = std::norm(ch_est); - - if (std::isnormal(ch_est_norm) && std::isnormal(noise_var_est[i_port]) && (noise_var_est[i_port] > 0)) { - // Accumulate the channel square absolute values. - ch_mod_sq += ch_est_norm; - - // Accumulate the noise variance weighted with the channel estimate norm. - nvar_acc += ch_est_norm * noise_var_est[i_port]; - - // Apply the matched channel filter to each received RE and accumulate the results. - re_out += re_in * std::conj(ch_est); - } - } - - // Return values in case of abnormal computation parameters. These include negative, zero, NAN or INF noise - // variances and zero, NAN or INF channel estimation coefficients. - symbols_out[i_re] = 0; - nvars_out[i_re] = std::numeric_limits::infinity(); - - if (std::isnormal(ch_mod_sq) && std::isnormal(nvar_acc)) { - // Calculate the reciprocal of the equalizer denominator. - float d_pinv_rcp = 1.0F / (ch_mod_sq * ch_mod_sq + nvar_acc); - - // Normalize the gain of the channel combined with the equalization to unity. - symbols_out[i_re] = re_out * ch_mod_sq * d_pinv_rcp; - - // Calculate noise variances. - nvars_out[i_re] = nvar_acc * d_pinv_rcp; - } - } -} - -} // namespace srsran diff --git a/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.h b/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.h index c0d77318dc..cd71ee7f1d 100644 --- a/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.h +++ b/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.h @@ -10,7 +10,7 @@ #pragma once -// This file was generated using the following MATLAB class on 30-10-2024 (seed 0): +// This file was generated using the following MATLAB class on 31-12-2024 (seed 0): // + "srsChEqualizerUnittest.m" #include "srsran/adt/complex.h" @@ -46,138 +46,120 @@ static const std::vector channel_equalizer_test_data = { {{12, 1, 2, 0.5634, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols18.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars18.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols18.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates18.dat"}}, {{12, 1, 2, 1.2814, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols20.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars20.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols20.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates20.dat"}}, {{12, 1, 2, 1.0312, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols22.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars22.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols22.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates22.dat"}}, - {{12, 1, 3, 0.72377, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols24.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars24.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols24.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates24.dat"}}, - {{12, 1, 3, 0.83042, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols26.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars26.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols26.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates26.dat"}}, - {{12, 1, 3, 0.88888, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols28.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars28.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols28.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates28.dat"}}, - {{12, 1, 3, 1.3639, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols30.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars30.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols30.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates30.dat"}}, - {{12, 1, 3, 1.1723, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols32.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars32.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols32.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates32.dat"}}, - {{12, 1, 3, 1.4924, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols34.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars34.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols34.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates34.dat"}}, - {{12, 1, 4, 0.66689, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols36.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars36.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols36.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates36.dat"}}, - {{12, 1, 4, 0.8302, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols38.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars38.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols38.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates38.dat"}}, - {{12, 1, 4, 0.90683, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols40.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars40.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols40.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates40.dat"}}, - {{12, 1, 4, 0.92408, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols42.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars42.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols42.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates42.dat"}}, - {{12, 1, 4, 0.75375, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols44.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars44.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols44.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates44.dat"}}, - {{12, 1, 4, 0.58399, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols46.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars46.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols46.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates46.dat"}}, - {{12, 2, 2, 0.538, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols48.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars48.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols48.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates48.dat"}}, - {{12, 2, 2, 1.3859, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols50.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars50.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols50.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates50.dat"}}, - {{12, 2, 2, 1.1877, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols52.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars52.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols52.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates52.dat"}}, - {{12, 2, 2, 0.60371, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols54.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars54.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols54.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates54.dat"}}, - {{12, 2, 2, 0.73338, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols56.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars56.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols56.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates56.dat"}}, - {{12, 2, 2, 0.9494, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols58.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars58.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols58.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates58.dat"}}, - {{12, 2, 4, 1.1617, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols60.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars60.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols60.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates60.dat"}}, - {{12, 2, 4, 0.96881, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols62.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars62.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols62.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates62.dat"}}, - {{12, 2, 4, 0.6577, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols64.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars64.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols64.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates64.dat"}}, - {{12, 2, 4, 0.50736, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols66.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars66.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols66.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates66.dat"}}, - {{12, 2, 4, 1.0069, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols68.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars68.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols68.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates68.dat"}}, - {{12, 2, 4, 0.76368, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols70.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars70.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols70.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates70.dat"}}, - {{12, 3, 4, 0.64595, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols72.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars72.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols72.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates72.dat"}}, - {{12, 3, 4, 1.4277, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols74.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars74.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols74.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates74.dat"}}, - {{12, 3, 4, 0.86573, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols76.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars76.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols76.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates76.dat"}}, - {{12, 3, 4, 0.62542, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols78.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars78.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols78.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates78.dat"}}, - {{12, 3, 4, 1.374, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols80.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars80.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols80.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates80.dat"}}, - {{12, 3, 4, 0.53093, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols82.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars82.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols82.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates82.dat"}}, - {{12, 4, 4, 0.55244, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols84.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars84.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols84.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates84.dat"}}, - {{12, 4, 4, 0.70667, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols86.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars86.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols86.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates86.dat"}}, - {{12, 4, 4, 1.0793, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols88.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars88.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols88.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates88.dat"}}, - {{12, 4, 4, 0.80486, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols90.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars90.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols90.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates90.dat"}}, - {{12, 4, 4, 1.0133, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols92.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars92.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols92.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates92.dat"}}, - {{12, 4, 4, 0.98787, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols94.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars94.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols94.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates94.dat"}}, - {{123, 1, 1, 1.2551, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols96.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars96.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols96.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates96.dat"}}, - {{123, 1, 1, 1.1721, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols98.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars98.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols98.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates98.dat"}}, - {{123, 1, 1, 0.56838, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols100.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars100.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols100.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates100.dat"}}, - {{123, 1, 1, 0.92984, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols102.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars102.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols102.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates102.dat"}}, - {{123, 1, 1, 0.94414, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols104.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars104.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols104.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates104.dat"}}, - {{123, 1, 1, 1.1354, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols106.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars106.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols106.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates106.dat"}}, - {{123, 1, 2, 1.3772, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols108.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars108.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols108.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates108.dat"}}, - {{123, 1, 2, 0.9402, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols110.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars110.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols110.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates110.dat"}}, - {{123, 1, 2, 0.88842, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols112.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars112.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols112.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates112.dat"}}, - {{123, 1, 2, 0.7754, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols114.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars114.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols114.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates114.dat"}}, - {{123, 1, 2, 1.0888, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols116.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars116.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols116.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates116.dat"}}, - {{123, 1, 2, 1.1649, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols118.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars118.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols118.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates118.dat"}}, - {{123, 1, 3, 1.4656, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols120.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars120.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols120.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates120.dat"}}, - {{123, 1, 3, 1.2798, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols122.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars122.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols122.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates122.dat"}}, - {{123, 1, 3, 0.95022, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols124.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars124.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols124.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates124.dat"}}, - {{123, 1, 3, 1.2338, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols126.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars126.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols126.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates126.dat"}}, - {{123, 1, 3, 0.52166, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols128.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars128.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols128.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates128.dat"}}, - {{123, 1, 3, 1.113, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols130.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars130.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols130.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates130.dat"}}, - {{123, 1, 4, 1.0704, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols132.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars132.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols132.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates132.dat"}}, - {{123, 1, 4, 0.53834, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols134.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars134.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols134.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates134.dat"}}, - {{123, 1, 4, 1.2245, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols136.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars136.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols136.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates136.dat"}}, - {{123, 1, 4, 0.67394, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols138.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars138.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols138.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates138.dat"}}, - {{123, 1, 4, 0.94875, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols140.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars140.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols140.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates140.dat"}}, - {{123, 1, 4, 1.0117, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols142.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars142.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols142.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates142.dat"}}, - {{123, 2, 2, 1.4714, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols144.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars144.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols144.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates144.dat"}}, - {{123, 2, 2, 0.52387, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols146.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars146.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols146.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates146.dat"}}, - {{123, 2, 2, 0.96166, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols148.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars148.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols148.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates148.dat"}}, - {{123, 2, 2, 0.63389, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols150.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars150.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols150.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates150.dat"}}, - {{123, 2, 2, 0.93844, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols152.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars152.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols152.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates152.dat"}}, - {{123, 2, 2, 1.1995, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols154.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars154.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols154.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates154.dat"}}, - {{123, 2, 4, 0.81616, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols156.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars156.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols156.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates156.dat"}}, - {{123, 2, 4, 1.3773, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols158.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars158.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols158.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates158.dat"}}, - {{123, 2, 4, 0.78607, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols160.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars160.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols160.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates160.dat"}}, - {{123, 2, 4, 1.4465, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols162.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars162.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols162.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates162.dat"}}, - {{123, 2, 4, 0.92831, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols164.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars164.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols164.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates164.dat"}}, - {{123, 2, 4, 1.2488, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols166.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars166.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols166.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates166.dat"}}, - {{123, 3, 4, 0.94925, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols168.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars168.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols168.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates168.dat"}}, - {{123, 3, 4, 1.3845, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols170.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars170.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols170.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates170.dat"}}, - {{123, 3, 4, 0.84941, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols172.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars172.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols172.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates172.dat"}}, - {{123, 3, 4, 0.88369, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols174.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars174.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols174.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates174.dat"}}, - {{123, 3, 4, 1.1078, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols176.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars176.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols176.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates176.dat"}}, - {{123, 3, 4, 0.60663, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols178.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars178.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols178.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates178.dat"}}, - {{123, 4, 4, 0.84477, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols180.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars180.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols180.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates180.dat"}}, - {{123, 4, 4, 0.80021, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols182.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars182.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols182.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates182.dat"}}, - {{123, 4, 4, 0.72526, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols184.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars184.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols184.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates184.dat"}}, - {{123, 4, 4, 0.69193, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols186.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars186.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols186.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates186.dat"}}, - {{123, 4, 4, 1.1803, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols188.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars188.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols188.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates188.dat"}}, - {{123, 4, 4, 0.57174, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols190.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars190.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols190.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates190.dat"}}, - {{1000, 1, 1, 1.3126, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols192.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars192.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols192.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates192.dat"}}, - {{1000, 1, 1, 1.1317, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols194.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars194.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols194.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates194.dat"}}, - {{1000, 1, 1, 0.91506, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols196.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars196.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols196.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates196.dat"}}, - {{1000, 1, 1, 0.96117, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols198.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars198.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols198.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates198.dat"}}, - {{1000, 1, 1, 0.82829, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols200.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars200.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols200.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates200.dat"}}, - {{1000, 1, 1, 0.66337, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols202.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars202.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols202.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates202.dat"}}, - {{1000, 1, 2, 0.98357, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols204.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars204.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols204.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates204.dat"}}, - {{1000, 1, 2, 1.1373, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols206.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars206.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols206.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates206.dat"}}, - {{1000, 1, 2, 0.88062, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols208.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars208.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols208.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates208.dat"}}, - {{1000, 1, 2, 0.60396, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols210.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars210.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols210.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates210.dat"}}, - {{1000, 1, 2, 1.2035, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols212.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars212.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols212.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates212.dat"}}, - {{1000, 1, 2, 1.2054, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols214.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars214.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols214.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates214.dat"}}, - {{1000, 1, 3, 1.3278, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols216.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars216.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols216.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates216.dat"}}, - {{1000, 1, 3, 0.66418, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols218.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars218.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols218.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates218.dat"}}, - {{1000, 1, 3, 1.3527, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols220.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars220.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols220.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates220.dat"}}, - {{1000, 1, 3, 1.1065, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols222.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars222.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols222.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates222.dat"}}, - {{1000, 1, 3, 0.99419, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols224.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars224.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols224.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates224.dat"}}, - {{1000, 1, 3, 0.60139, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols226.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars226.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols226.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates226.dat"}}, - {{1000, 1, 4, 0.64839, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols228.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars228.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols228.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates228.dat"}}, - {{1000, 1, 4, 1.3774, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols230.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars230.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols230.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates230.dat"}}, - {{1000, 1, 4, 0.77943, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols232.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars232.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols232.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates232.dat"}}, - {{1000, 1, 4, 1.1469, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols234.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars234.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols234.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates234.dat"}}, - {{1000, 1, 4, 1.3728, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols236.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars236.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols236.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates236.dat"}}, - {{1000, 1, 4, 1.3852, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols238.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars238.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols238.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates238.dat"}}, - {{1000, 2, 2, 1.3243, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols240.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars240.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols240.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates240.dat"}}, - {{1000, 2, 2, 1.0728, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols242.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars242.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols242.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates242.dat"}}, - {{1000, 2, 2, 0.92915, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols244.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars244.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols244.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates244.dat"}}, - {{1000, 2, 2, 0.97321, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols246.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars246.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols246.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates246.dat"}}, - {{1000, 2, 2, 1.2893, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols248.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars248.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols248.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates248.dat"}}, - {{1000, 2, 2, 0.63602, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols250.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars250.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols250.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates250.dat"}}, - {{1000, 2, 4, 1.0267, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols252.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars252.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols252.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates252.dat"}}, - {{1000, 2, 4, 0.96395, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols254.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars254.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols254.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates254.dat"}}, - {{1000, 2, 4, 0.91864, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols256.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars256.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols256.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates256.dat"}}, - {{1000, 2, 4, 0.50686, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols258.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars258.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols258.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates258.dat"}}, - {{1000, 2, 4, 1.4626, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols260.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars260.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols260.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates260.dat"}}, - {{1000, 2, 4, 0.63135, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols262.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars262.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols262.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates262.dat"}}, - {{1000, 3, 4, 1.4109, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols264.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars264.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols264.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates264.dat"}}, - {{1000, 3, 4, 0.93746, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols266.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars266.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols266.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates266.dat"}}, - {{1000, 3, 4, 0.61719, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols268.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars268.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols268.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates268.dat"}}, - {{1000, 3, 4, 0.92604, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols270.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars270.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols270.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates270.dat"}}, - {{1000, 3, 4, 1.2671, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols272.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars272.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols272.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates272.dat"}}, - {{1000, 3, 4, 0.67037, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols274.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars274.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols274.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates274.dat"}}, - {{1000, 4, 4, 1.3331, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols276.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars276.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols276.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates276.dat"}}, - {{1000, 4, 4, 0.56418, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols278.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars278.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols278.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates278.dat"}}, - {{1000, 4, 4, 1.1597, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols280.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars280.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols280.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates280.dat"}}, - {{1000, 4, 4, 0.52164, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols282.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars282.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols282.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates282.dat"}}, - {{1000, 4, 4, 0.68873, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols284.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars284.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols284.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates284.dat"}}, - {{1000, 4, 4, 1.3982, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols286.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars286.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols286.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates286.dat"}}, + {{12, 1, 4, 0.91063, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols24.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars24.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols24.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates24.dat"}}, + {{12, 1, 4, 1.1468, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols26.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars26.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols26.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates26.dat"}}, + {{12, 1, 4, 1.0201, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols28.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars28.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols28.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates28.dat"}}, + {{12, 1, 4, 1.0694, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols30.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars30.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols30.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates30.dat"}}, + {{12, 1, 4, 0.50865, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols32.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars32.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols32.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates32.dat"}}, + {{12, 1, 4, 1.0735, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols34.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars34.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols34.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates34.dat"}}, + {{12, 2, 2, 0.92719, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols36.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars36.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols36.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates36.dat"}}, + {{12, 2, 2, 1.2284, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols38.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars38.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols38.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates38.dat"}}, + {{12, 2, 2, 0.93342, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols40.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars40.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols40.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates40.dat"}}, + {{12, 2, 2, 1.2485, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols42.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars42.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols42.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates42.dat"}}, + {{12, 2, 2, 0.83591, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols44.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars44.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols44.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates44.dat"}}, + {{12, 2, 2, 0.58399, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols46.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars46.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols46.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates46.dat"}}, + {{12, 2, 4, 1.4845, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols48.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars48.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols48.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates48.dat"}}, + {{12, 2, 4, 1.1457, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols50.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars50.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols50.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates50.dat"}}, + {{12, 2, 4, 0.7915, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols52.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars52.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols52.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates52.dat"}}, + {{12, 2, 4, 1.4308, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols54.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars54.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols54.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates54.dat"}}, + {{12, 2, 4, 1.483, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols56.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars56.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols56.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates56.dat"}}, + {{12, 2, 4, 0.75372, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols58.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars58.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols58.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates58.dat"}}, + {{12, 3, 4, 0.50361, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols60.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars60.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols60.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates60.dat"}}, + {{12, 3, 4, 0.95688, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols62.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars62.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols62.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates62.dat"}}, + {{12, 3, 4, 0.53055, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols64.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars64.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols64.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates64.dat"}}, + {{12, 3, 4, 0.53057, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols66.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars66.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols66.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates66.dat"}}, + {{12, 3, 4, 0.99839, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols68.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars68.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols68.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates68.dat"}}, + {{12, 3, 4, 1.36, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols70.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars70.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols70.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates70.dat"}}, + {{12, 4, 4, 0.62798, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols72.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars72.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols72.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates72.dat"}}, + {{12, 4, 4, 1.4557, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols74.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars74.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols74.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates74.dat"}}, + {{12, 4, 4, 0.50863, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols76.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars76.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols76.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates76.dat"}}, + {{12, 4, 4, 0.99653, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols78.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars78.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols78.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates78.dat"}}, + {{12, 4, 4, 1.1268, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols80.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars80.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols80.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates80.dat"}}, + {{12, 4, 4, 1.2925, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols82.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars82.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols82.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates82.dat"}}, + {{123, 1, 1, 0.89742, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols84.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars84.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols84.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates84.dat"}}, + {{123, 1, 1, 1.4562, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols86.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars86.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols86.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates86.dat"}}, + {{123, 1, 1, 1.1024, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols88.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars88.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols88.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates88.dat"}}, + {{123, 1, 1, 1.0024, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols90.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars90.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols90.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates90.dat"}}, + {{123, 1, 1, 1.1912, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols92.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars92.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols92.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates92.dat"}}, + {{123, 1, 1, 1.157, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols94.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars94.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols94.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates94.dat"}}, + {{123, 1, 2, 1.1651, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols96.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars96.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols96.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates96.dat"}}, + {{123, 1, 2, 1.4941, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols98.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars98.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols98.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates98.dat"}}, + {{123, 1, 2, 0.648, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols100.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars100.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols100.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates100.dat"}}, + {{123, 1, 2, 1.263, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols102.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars102.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols102.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates102.dat"}}, + {{123, 1, 2, 1.4397, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols104.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars104.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols104.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates104.dat"}}, + {{123, 1, 2, 1.3747, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols106.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars106.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols106.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates106.dat"}}, + {{123, 1, 4, 1.0909, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols108.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars108.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols108.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates108.dat"}}, + {{123, 1, 4, 0.77213, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols110.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars110.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols110.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates110.dat"}}, + {{123, 1, 4, 1.1579, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols112.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars112.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols112.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates112.dat"}}, + {{123, 1, 4, 0.76396, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols114.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars114.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols114.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates114.dat"}}, + {{123, 1, 4, 1.1059, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols116.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars116.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols116.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates116.dat"}}, + {{123, 1, 4, 1.4302, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols118.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars118.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols118.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates118.dat"}}, + {{123, 2, 2, 1.0747, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols120.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars120.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols120.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates120.dat"}}, + {{123, 2, 2, 1.1666, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols122.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars122.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols122.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates122.dat"}}, + {{123, 2, 2, 0.51195, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols124.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars124.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols124.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates124.dat"}}, + {{123, 2, 2, 0.66005, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols126.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars126.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols126.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates126.dat"}}, + {{123, 2, 2, 0.64217, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols128.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars128.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols128.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates128.dat"}}, + {{123, 2, 2, 1.0807, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols130.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars130.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols130.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates130.dat"}}, + {{123, 2, 4, 0.83351, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols132.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars132.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols132.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates132.dat"}}, + {{123, 2, 4, 0.85772, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols134.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars134.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols134.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates134.dat"}}, + {{123, 2, 4, 0.61611, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols136.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars136.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols136.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates136.dat"}}, + {{123, 2, 4, 1.439, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols138.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars138.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols138.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates138.dat"}}, + {{123, 2, 4, 0.67274, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols140.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars140.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols140.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates140.dat"}}, + {{123, 2, 4, 1.032, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols142.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars142.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols142.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates142.dat"}}, + {{123, 3, 4, 0.58345, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols144.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars144.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols144.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates144.dat"}}, + {{123, 3, 4, 1.2112, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols146.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars146.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols146.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates146.dat"}}, + {{123, 3, 4, 1.1894, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols148.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars148.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols148.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates148.dat"}}, + {{123, 3, 4, 0.67474, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols150.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars150.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols150.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates150.dat"}}, + {{123, 3, 4, 1.4084, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols152.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars152.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols152.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates152.dat"}}, + {{123, 3, 4, 1.2403, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols154.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars154.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols154.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates154.dat"}}, + {{123, 4, 4, 1.3686, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols156.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars156.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols156.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates156.dat"}}, + {{123, 4, 4, 0.82971, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols158.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars158.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols158.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates158.dat"}}, + {{123, 4, 4, 1.3119, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols160.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars160.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols160.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates160.dat"}}, + {{123, 4, 4, 1.1753, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols162.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars162.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols162.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates162.dat"}}, + {{123, 4, 4, 0.81579, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols164.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars164.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols164.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates164.dat"}}, + {{123, 4, 4, 0.56883, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols166.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars166.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols166.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates166.dat"}}, + {{1000, 1, 1, 0.68266, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols168.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars168.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols168.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates168.dat"}}, + {{1000, 1, 1, 0.92758, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols170.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars170.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols170.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates170.dat"}}, + {{1000, 1, 1, 1.4669, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols172.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars172.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols172.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates172.dat"}}, + {{1000, 1, 1, 0.62655, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols174.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars174.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols174.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates174.dat"}}, + {{1000, 1, 1, 1.2798, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols176.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars176.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols176.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates176.dat"}}, + {{1000, 1, 1, 1.389, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols178.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars178.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols178.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates178.dat"}}, + {{1000, 1, 2, 0.62718, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols180.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars180.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols180.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates180.dat"}}, + {{1000, 1, 2, 0.85815, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols182.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars182.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols182.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates182.dat"}}, + {{1000, 1, 2, 0.8199, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols184.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars184.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols184.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates184.dat"}}, + {{1000, 1, 2, 0.66555, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols186.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars186.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols186.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates186.dat"}}, + {{1000, 1, 2, 1.1021, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols188.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars188.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols188.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates188.dat"}}, + {{1000, 1, 2, 0.83672, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols190.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars190.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols190.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates190.dat"}}, + {{1000, 1, 4, 0.53914, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols192.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars192.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols192.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates192.dat"}}, + {{1000, 1, 4, 0.96616, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols194.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars194.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols194.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates194.dat"}}, + {{1000, 1, 4, 1.018, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols196.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars196.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols196.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates196.dat"}}, + {{1000, 1, 4, 1.4265, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols198.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars198.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols198.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates198.dat"}}, + {{1000, 1, 4, 1.0638, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols200.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars200.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols200.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates200.dat"}}, + {{1000, 1, 4, 0.74973, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols202.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars202.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols202.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates202.dat"}}, + {{1000, 2, 2, 0.77504, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols204.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars204.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols204.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates204.dat"}}, + {{1000, 2, 2, 0.70287, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols206.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars206.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols206.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates206.dat"}}, + {{1000, 2, 2, 1.2176, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols208.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars208.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols208.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates208.dat"}}, + {{1000, 2, 2, 0.55121, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols210.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars210.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols210.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates210.dat"}}, + {{1000, 2, 2, 1.2485, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols212.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars212.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols212.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates212.dat"}}, + {{1000, 2, 2, 1.1148, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols214.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars214.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols214.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates214.dat"}}, + {{1000, 2, 4, 0.90941, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols216.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars216.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols216.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates216.dat"}}, + {{1000, 2, 4, 0.88403, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols218.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars218.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols218.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates218.dat"}}, + {{1000, 2, 4, 0.67498, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols220.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars220.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols220.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates220.dat"}}, + {{1000, 2, 4, 0.7498, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols222.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars222.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols222.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates222.dat"}}, + {{1000, 2, 4, 0.55899, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols224.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars224.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols224.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates224.dat"}}, + {{1000, 2, 4, 1.4957, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols226.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars226.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols226.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates226.dat"}}, + {{1000, 3, 4, 1.1404, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols228.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars228.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols228.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates228.dat"}}, + {{1000, 3, 4, 0.81633, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols230.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars230.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols230.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates230.dat"}}, + {{1000, 3, 4, 1.2295, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols232.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars232.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols232.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates232.dat"}}, + {{1000, 3, 4, 0.57511, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols234.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars234.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols234.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates234.dat"}}, + {{1000, 3, 4, 1.4479, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols236.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars236.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols236.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates236.dat"}}, + {{1000, 3, 4, 0.90776, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols238.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars238.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols238.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates238.dat"}}, + {{1000, 4, 4, 1.1536, 1, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols240.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars240.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols240.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates240.dat"}}, + {{1000, 4, 4, 0.96676, 1.4142, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols242.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars242.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols242.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates242.dat"}}, + {{1000, 4, 4, 0.84355, 0.5, "MMSE"}, {"test_data/channel_equalizer_test_output_eq_symbols244.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars244.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols244.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates244.dat"}}, + {{1000, 4, 4, 0.79398, 1, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols246.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars246.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols246.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates246.dat"}}, + {{1000, 4, 4, 0.52366, 1.4142, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols248.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars248.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols248.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates248.dat"}}, + {{1000, 4, 4, 1.0884, 0.5, "ZF"}, {"test_data/channel_equalizer_test_output_eq_symbols250.dat"}, {"test_data/channel_equalizer_test_output_eq_noise_vars250.dat"}, {"test_data/channel_equalizer_test_input_rx_symbols250.dat"}, {"test_data/channel_equalizer_test_input_ch_estimates250.dat"}}, // clang-format on }; diff --git a/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.tar.gz b/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.tar.gz index 620c5a9ae0..2a246f92ab 100644 --- a/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.tar.gz +++ b/tests/unittests/phy/upper/equalization/channel_equalizer_test_data.tar.gz @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:35343ce8d528b9a63e89d72a215b71c9b7b156ff220aa2cc8ce9ea78e4f2e6e8 -size 4435104 +oid sha256:ffe4d7a21b692af7a512d47829cb03bb9efbbc3c318412a85545fb671565cf14 +size 4068655 From 0c716444468d77a0ff3ded906e8c31421029dcdb Mon Sep 17 00:00:00 2001 From: dvdgrgrtt Date: Tue, 31 Dec 2024 16:35:00 +0100 Subject: [PATCH 098/107] simd: new *= operator Multiply a complex register by a real one and write the result in the complex register we started with. --- include/srsran/srsvec/simd.h | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/srsran/srsvec/simd.h b/include/srsran/srsvec/simd.h index c84cf194e0..8280d39d5d 100644 --- a/include/srsran/srsvec/simd.h +++ b/include/srsran/srsvec/simd.h @@ -1373,6 +1373,13 @@ inline simd_cf_t& operator*=(simd_cf_t& left, float right) return left; } +/// Multiplies a complex and a real SIMD registers. +inline simd_cf_t& operator*=(simd_cf_t& left, simd_f_t right) +{ + left = srsran_simd_cf_mul(left, right); + return left; +} + /// Subtract two complex SIMD registers. inline simd_cf_t& operator-=(simd_cf_t& left, const simd_cf_t& right) { From c222c468e66003369e2f95ff38f91b6842808d71 Mon Sep 17 00:00:00 2001 From: dvdgrgrtt Date: Fri, 10 Jan 2025 16:49:21 +0100 Subject: [PATCH 099/107] tests, phy: update pdcch test vectors The PDCCH processor test vector was not the standard one. --- .../channel_processors/pdcch/pdcch_processor_test_data.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.h b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.h index ec9fb6086b..f95b02e109 100644 --- a/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.h +++ b/tests/unittests/phy/upper/channel_processors/pdcch/pdcch_processor_test_data.h @@ -28,7 +28,7 @@ struct test_case_t { }; static const std::vector pdcch_processor_test_data = { -// clang-format off + // clang-format off {{std::nullopt, {0, 999}, cyclic_prefix::NORMAL, {25, 0, 12, 1, {1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 0, 0, 0}, {8320, 821, 821, 8320, 0, 1, 0, 0, {1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1}, default_precoding}}, {"test_data/pdcch_processor_test_output0.dat"}}, {{std::nullopt, {0, 3481}, cyclic_prefix::NORMAL, {25, 0, 7, 1, {1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 0, 0, 0}, {7796, 660, 660, 7796, 0, 2, 0, 0, {1, 0, 1, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1}, default_precoding}}, {"test_data/pdcch_processor_test_output1.dat"}}, {{std::nullopt, {0, 777}, cyclic_prefix::NORMAL, {25, 0, 5, 1, {1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 0, 0, 0}, {49384, 288, 288, 49384, 0, 4, 0, 0, {1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0}, default_precoding}}, {"test_data/pdcch_processor_test_output2.dat"}}, @@ -143,7 +143,7 @@ static const std::vector pdcch_processor_test_data = { {{std::nullopt, {0, 2981}, cyclic_prefix::NORMAL, {270, 0, 8, 3, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 0, 0, 0}, {4021, 199, 199, 4021, 60, 4, 0, 0, {0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 0}, default_precoding}}, {"test_data/pdcch_processor_test_output111.dat"}}, {{std::nullopt, {0, 7000}, cyclic_prefix::NORMAL, {270, 0, 10, 3, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 0, 0, 0}, {20902, 909, 909, 20902, 48, 8, 0, 0, {1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0}, default_precoding}}, {"test_data/pdcch_processor_test_output112.dat"}}, {{std::nullopt, {0, 5322}, cyclic_prefix::NORMAL, {270, 0, 11, 3, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 0, 0, 0}, {20174, 480, 480, 20174, 32, 16, 0, 0, {1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0}, default_precoding}}, {"test_data/pdcch_processor_test_output113.dat"}}, -// clang-format on + // clang-format on }; } // namespace srsran From 87ba1f268d3492d5330004f8270e0eedc465cac4 Mon Sep 17 00:00:00 2001 From: frankist Date: Fri, 10 Jan 2025 18:41:45 +0100 Subject: [PATCH 100/107] Revert last UL grant extension feature --- .../ue_scheduling/ue_cell_grid_allocator.cpp | 302 ++++-------------- .../ue_scheduling/ue_cell_grid_allocator.h | 2 - 2 files changed, 60 insertions(+), 244 deletions(-) diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index e96998c793..92c6450a7e 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -510,41 +510,6 @@ dl_alloc_result ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& return {alloc_status::invalid_params}; } -static crb_interval get_ul_rb_limits(const scheduler_ue_expert_config& expert_cfg, const search_space_info& ss_info) -{ - const unsigned start_rb = std::max(expert_cfg.pusch_crb_limits.start(), ss_info.ul_crb_lims.start()); - const unsigned end_rb = std::min(expert_cfg.pusch_crb_limits.stop(), ss_info.ul_crb_lims.stop()); - return {start_rb, std::max(start_rb, end_rb)}; -} - -static unsigned -adjust_nof_rbs_to_transform_precoding(unsigned rbs, const ue_cell& ue_cc, dci_ul_rnti_config_type dci_type) -{ - // Ensure the number of PRB is valid if the transform precoder is used. The condition the PUSCH bandwidth with - // transform precoder is defined in TS 38.211 Section 6.1.3. The number of PRB must be lower than or equal to - // current number of PRB. - bool use_transform_precoding = dci_type == dci_ul_rnti_config_type::c_rnti_f0_1 - ? ue_cc.cfg().use_pusch_transform_precoding_dci_0_1() - : ue_cc.cfg().cell_cfg_common.use_msg3_transform_precoder(); - if (use_transform_precoding) { - rbs = get_transform_precoding_nearest_lower_nof_prb_valid(rbs).value_or(rbs); - } - return rbs; -} - -static unsigned adjust_ue_max_ul_nof_rbs(const scheduler_ue_expert_config& expert_cfg, - const ue_cell& ue_cc, - dci_ul_rnti_config_type dci_type, - unsigned max_rbs) -{ - max_rbs = std::min(expert_cfg.pusch_nof_rbs.stop(), max_rbs); - max_rbs = std::min(max_rbs, ue_cc.cfg().rrm_cfg().pusch_grant_size_limits.stop()); - max_rbs = ue_cc.get_ul_power_controller().adapt_pusch_prbs_to_phr(max_rbs); - max_rbs = adjust_nof_rbs_to_transform_precoding(max_rbs, ue_cc, dci_type); - - return max_rbs; -} - ul_alloc_result ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice_id_t slice_id, slot_point pusch_slot) { @@ -665,6 +630,28 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice return {alloc_status::skip_slot}; } + // When checking the number of remaining grants for PUSCH, take into account that the PUCCH grants for this UE will + // be removed when multiplexing the UCI on PUSCH. + unsigned pusch_pdu_rem_space = get_space_left_for_pusch_pdus(pusch_alloc.result, u.crnti, expert_cfg); + if (pusch_pdu_rem_space == 0) { + if (pusch_alloc.result.ul.puschs.size() >= expert_cfg.max_puschs_per_slot) { + logger.info( + "ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Max number of PUSCHs per slot {} was reached.", + fmt::underlying(u.ue_index), + u.crnti, + pusch_alloc.slot, + expert_cfg.max_puschs_per_slot); + } else { + logger.info("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Max number of UL grants per slot {} " + "was reached.", + fmt::underlying(u.ue_index), + u.crnti, + pusch_alloc.slot, + expert_cfg.max_puschs_per_slot); + } + return {alloc_status::skip_slot}; + } + // [Implementation-defined] We skip allocation of PUSCH if there is already a PUCCH grant scheduled using common // PUCCH resources. if (get_uci_alloc(grant.cell_index).has_uci_harq_on_common_pucch_res(u.crnti, pusch_alloc.slot)) { @@ -690,30 +677,36 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice } // Apply RB allocation limits. - crb_interval ul_crb_lims = get_ul_rb_limits(expert_cfg, ss_info); - if (ul_crb_lims.empty()) { - logger.debug("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Invalid RB allocation range {}", + const unsigned start_rb = std::max(expert_cfg.pusch_crb_limits.start(), ss_info.ul_crb_lims.start()); + const unsigned end_rb = std::min(expert_cfg.pusch_crb_limits.stop(), ss_info.ul_crb_lims.stop()); + if (start_rb >= end_rb) { + logger.debug("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Invalid RB allocation range [{}, {})", fmt::underlying(u.ue_index), u.crnti, pusch_alloc.slot, - ul_crb_lims); + start_rb, + end_rb); return {alloc_status::skip_slot}; } - const prb_bitmap used_crbs = pusch_alloc.ul_res_grid.used_crbs(scs, ul_crb_lims, pusch_td_cfg.symbols); + const crb_interval ul_crb_lims = {start_rb, end_rb}; + const prb_bitmap used_crbs = pusch_alloc.ul_res_grid.used_crbs(scs, ul_crb_lims, pusch_td_cfg.symbols); if (used_crbs.all()) { slots_with_no_pusch_space.push_back(pusch_alloc.slot); return {alloc_status::skip_slot}; } // Compute the MCS and the number of PRBs, depending on the pending bytes to transmit. - grant_prbs_mcs mcs_prbs; - if (is_retx) { - mcs_prbs = grant_prbs_mcs{h_ul->get_grant_params().mcs, h_ul->get_grant_params().rbs.type1().length()}; - } else { - // Compute MCS and PRBs based on grant parameters. - mcs_prbs = ue_cc->required_ul_prbs(pusch_td_cfg, grant.recommended_nof_bytes.value(), dci_type); - + grant_prbs_mcs mcs_prbs = + is_retx ? grant_prbs_mcs{h_ul->get_grant_params().mcs, h_ul->get_grant_params().rbs.type1().length()} + : ue_cc->required_ul_prbs(pusch_td_cfg, grant.recommended_nof_bytes.value(), dci_type); + // Try to limit the grant PRBs. + if (not is_retx) { + // [Implementation-defined] Check whether max. UL grants per slot is reached if PUSCH for current UE succeeds. If + // so, allocate remaining RBs to the current UE only if it's a new Tx. + if (pusch_pdu_rem_space == 1 and not u.has_pending_sr()) { + mcs_prbs.n_prbs = rb_helper::find_empty_interval_of_length(used_crbs, used_crbs.size(), 0).length(); + } // Due to the pre-allocated UCI bits, MCS 0 and PRB 1 would not leave any space for the payload on the TBS, as // all the space would be taken by the UCI bits. As a result of this, the effective code rate would be 0 and the // allocation would fail and be postponed to the next slot. @@ -725,26 +718,33 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice if (mcs_prbs.mcs < min_mcs_for_1_prb and mcs_prbs.n_prbs <= min_allocable_prbs) { ++mcs_prbs.n_prbs; } - // [Implementation-defined] - // Sometimes just a few 1-4 RBs are left in the grid, and the scheduler policy will try to fit a tiny PUSCH in it. - // This will lead to a waste of PDCCHs that could be used for another k2 value. - // We want to avoid this, by ensuring this grant fills the remaining RBs. - unsigned nof_rbs_left = (~used_crbs).count(); - nof_rbs_left -= std::min(nof_rbs_left, mcs_prbs.n_prbs); - if (nof_rbs_left > 0 and nof_rbs_left < 5) { - mcs_prbs.n_prbs += nof_rbs_left; + // Check whether to allocate all remaining RBs or not. This is done to ensure we allocate only X nof. UEs per slot + // and not X+1 nof. UEs. One way is by checking if the emtpy interval is less than 2 times the required RBs. If + // so, allocate all remaining RBs. NOTE: This approach won't hold good in case of low traffic scenario. + const unsigned twice_grant_crbs_length = + rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs * 2, 0).length(); + if (twice_grant_crbs_length < (mcs_prbs.n_prbs * 2)) { + mcs_prbs.n_prbs = twice_grant_crbs_length; } - // Limit nof. RBs to allocate to maximum RBs provided in grant. if (grant.max_nof_rbs.has_value()) { mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, grant.max_nof_rbs.value()); } - - // Apply nof. PUSCH RBs to allocate limits. + // Re-apply nof. PUSCH RBs to allocate limits. mcs_prbs.n_prbs = std::max(mcs_prbs.n_prbs, expert_cfg.pusch_nof_rbs.start()); + mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, expert_cfg.pusch_nof_rbs.stop()); mcs_prbs.n_prbs = std::max(mcs_prbs.n_prbs, ue_cell_cfg.rrm_cfg().pusch_grant_size_limits.start()); - mcs_prbs.n_prbs = adjust_ue_max_ul_nof_rbs(expert_cfg, *ue_cc, dci_type, mcs_prbs.n_prbs); + mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, ue_cell_cfg.rrm_cfg().pusch_grant_size_limits.stop()); + // Ensure the number of PRB is valid if the transform precoder is used. The condition the PUSCH bandwidth with + // transform precoder is defined in TS 38.211 Section 6.1.3. The number of PRB must be lower than or equal to + // current number of PRB. + if ((dci_type == dci_ul_rnti_config_type::c_rnti_f0_1) + ? ue_cell_cfg.use_pusch_transform_precoding_dci_0_1() + : ue_cell_cfg.cell_cfg_common.use_msg3_transform_precoder()) { + mcs_prbs.n_prbs = + get_transform_precoding_nearest_lower_nof_prb_valid(mcs_prbs.n_prbs).value_or(mcs_prbs.n_prbs); + } } // NOTE: This should never happen, but it's safe not to proceed if we get n_prbs == 0. @@ -1051,187 +1051,5 @@ ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant, ran_slice void ue_cell_grid_allocator::post_process_results() { - for (const cell_t& cell : cells) { - auto& pdcch_alloc = get_res_alloc(cell.cell_index)[0]; - - // In case PUSCHs allocations have been made, we try to ensure that RBs are not left empty. - auto& ul_pdcchs = pdcch_alloc.result.dl.ul_pdcchs; - if (ul_pdcchs.empty()) { - continue; - } - - // For the same slot, PDCCHs with different k2 values could have been scheduled. We will go through allocated - // PUSCH slots and see if there are any RBs remaining to be scheduled. - bounded_bitset traversed_k2s(SCHEDULER_MAX_K2); - for (auto& pdcch : ul_pdcchs) { - if (pdcch.dci.type != dci_ul_rnti_config_type::c_rnti_f0_1) { - continue; - } - const auto& u = *ues.find_by_rnti(pdcch.ctx.rnti); - const ue_cell& ue_cell = *u.find_cell(cell.cell_index); - const uint8_t pusch_td_idx = pdcch.dci.c_rnti_f0_1.time_resource; - const search_space_info& ss = ue_cell.cfg().search_space(pdcch.ctx.context.ss_id); - const uint8_t k2 = ss.pusch_time_domain_list[pusch_td_idx].k2; - if (not traversed_k2s.test(k2 - 1)) { - post_process_ul_results(cell.cell_index, pdcch_alloc.slot + k2); - traversed_k2s.set(k2 - 1); - } - } - } -} - -void ue_cell_grid_allocator::post_process_ul_results(du_cell_index_t cell_idx, slot_point pusch_slot) -{ - // Note: For now, we just expand the last allocation to fill empty RBs. - // TODO: Fairer algorithm to fill remaining RBs that accounts for the UE buffer states as well. - - const cell_t& cell = cells[cell_idx]; - cell_resource_allocator& cell_alloc = get_res_alloc(cell.cell_index); - auto& pusch_alloc = cell_alloc[pusch_slot]; - if (pusch_alloc.result.ul.puschs.empty()) { - // There are no UL allocations for this slot. Move on to next cell. - return; - } - const cell_configuration& cell_cfg = cell_alloc.cfg; - const subcarrier_spacing scs = cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.scs; - - // Use last PUSCH to get reference UE config for this candidate. - ul_sched_info* last_pusch = nullptr; - for (auto& pusch : pusch_alloc.result.ul.puschs) { - if (not pusch.pusch_cfg.rbs.is_type1()) { - // Type 0 not supported yet. - continue; - } - if (last_pusch == nullptr or last_pusch->pusch_cfg.rbs.type1().stop() < pusch.pusch_cfg.rbs.type1().stop()) { - last_pusch = &pusch; - } - } - if (last_pusch == nullptr) { - // There were no candidates for extension. - return; - } - if (not last_pusch->pusch_cfg.new_data) { - // It is a retx. We cannot resize it. - return; - } - - vrb_interval vrbs = last_pusch->pusch_cfg.rbs.type1(); - if (vrbs.length() >= expert_cfg.pusch_nof_rbs.length()) { - // The last UE reached max grant size. - return; - } - if (not ues.contains(last_pusch->context.ue_index)) { - // In case of TC-RNTI, the UE index might not yet exist. - return; - } - ue& ue_ref = ues[last_pusch->context.ue_index]; - ue_cell& ue_cc = *ue_ref.find_cell(cell_cfg.cell_index); - - if (ue_ref.pending_ul_newtx_bytes() == 0) { - // No point in expanding UE grant if it has no more bytes to transmit. - return; - } - - const search_space_info& ss_info = ue_cc.cfg().search_space(last_pusch->context.ss_id); - const crb_interval ul_crb_lims = get_ul_rb_limits(expert_cfg, ss_info); - - const prb_bitmap used_crbs = pusch_alloc.ul_res_grid.used_crbs(scs, ul_crb_lims, last_pusch->pusch_cfg.symbols); - if (used_crbs.all()) { - // All CRBs were filled. - return; - } - - std::optional h_ul = ue_cc.harqs.ul_harq(to_harq_id(last_pusch->pusch_cfg.harq_id)); - if (not h_ul.has_value()) { - logger.error("Could not find HARQ id for existing PUSCH"); - return; - } - const dci_ul_rnti_config_type dci_type = h_ul->get_grant_params().dci_cfg_type; - if (dci_type != dci_ul_rnti_config_type::c_rnti_f0_1) { - // Only expansion for C-RNTI f0_1 supported. - return; - } - - // Check if there is a gap at the right of the last UE. - const bwp_configuration& active_bwp = *last_pusch->pusch_cfg.bwp_cfg; - crb_interval crbs = rb_helper::vrb_to_crb_ul_non_interleaved(vrbs, active_bwp.crbs.start()); - // Account for limits in number of RBs that can be allocated. - const unsigned max_crbs = - adjust_ue_max_ul_nof_rbs(expert_cfg, ue_cc, dci_type, crbs.length() + (active_bwp.crbs.stop() - crbs.stop())); - if (max_crbs <= crbs.length()) { - return; - } - - crb_interval empty_crbs = rb_helper::find_empty_interval_of_length(used_crbs, max_crbs - crbs.length(), crbs.stop()); - if (empty_crbs.empty() or empty_crbs.start() != crbs.stop()) { - // Could not extend existing PUSCH. - return; - } - // There are RBs empty to the left of the last allocation. Let's expand the allocation. - - crb_interval old_crbs = crbs; - crbs = {old_crbs.start(), empty_crbs.stop()}; - crbs.resize(adjust_nof_rbs_to_transform_precoding(crbs.length(), ue_cc, dci_type)); - - // Find respective PDCCH. - auto& pdcch_alloc = cell_alloc[pusch_slot - last_pusch->context.k2]; - auto it = - std::find_if(pdcch_alloc.result.dl.ul_pdcchs.begin(), - pdcch_alloc.result.dl.ul_pdcchs.end(), - [&](const pdcch_ul_information& pdcch) { return pdcch.ctx.rnti == last_pusch->pusch_cfg.rnti; }); - if (it == pdcch_alloc.result.dl.ul_pdcchs.end()) { - logger.error("rnti={}: Cannot find PDCCH associated with the given PUSCH at slot={}", - last_pusch->pusch_cfg.rnti, - pusch_slot); - return; - } - pdcch_ul_information& pdcch = *it; - - // Derive new VRBs - vrbs = rb_helper::crb_to_vrb_ul_non_interleaved(crbs, active_bwp.crbs.start()); - - // Recompute MCS and TBS. - const search_space_info& ss = ue_cc.cfg().search_space(pdcch.ctx.context.ss_id); - const auto& pusch_td_cfg = ss.pusch_time_domain_list[pdcch.dci.c_rnti_f0_1.time_resource]; - const unsigned nof_harq_bits = - last_pusch->uci.has_value() and last_pusch->uci->harq.has_value() ? last_pusch->uci->harq->harq_ack_nof_bits : 0; - const unsigned is_csi_rep = last_pusch->uci.has_value() and last_pusch->uci->csi.has_value() - ? last_pusch->uci->csi.value().csi_part1_nof_bits > 0 - : 0; - auto pusch_cfg = get_pusch_config_f0_1_c_rnti( - ue_cc.cfg(), pusch_td_cfg, last_pusch->pusch_cfg.nof_layers, nof_harq_bits, is_csi_rep); - bool contains_dc = - dc_offset_helper::is_contained(cell_cfg.expert_cfg.ue.initial_ul_dc_offset, cell_cfg.nof_ul_prbs, crbs); - std::optional mcs_tbs_info = - compute_ul_mcs_tbs(pusch_cfg, &ue_cc.cfg(), last_pusch->pusch_cfg.mcs_index, crbs.length(), contains_dc); - if (not mcs_tbs_info.has_value()) { - return; - } - - // Mark resources as occupied in the ResourceGrid. - pusch_alloc.ul_res_grid.fill(grant_info{scs, last_pusch->pusch_cfg.symbols, empty_crbs}); - - // Update DCI. - pdcch.dci.c_rnti_f0_1.frequency_resource = ra_frequency_type1_get_riv( - ra_frequency_type1_configuration{active_bwp.crbs.length(), vrbs.start(), vrbs.length()}); - - // Update PUSCH. - last_pusch->pusch_cfg.rbs = vrbs; - last_pusch->pusch_cfg.tb_size_bytes = mcs_tbs_info->tbs; - - // Update the number of PRBs used in the PUSCH allocation. - ue_cc.get_ul_power_controller().update_pusch_pw_ctrl_state(pusch_alloc.slot, crbs.length()); - - // Update HARQ. - ul_harq_alloc_context pusch_sched_ctx; - pusch_sched_ctx.dci_cfg_type = dci_type; - pusch_sched_ctx.olla_mcs = h_ul->get_grant_params().olla_mcs; - pusch_sched_ctx.slice_id = h_ul->get_grant_params().slice_id; - h_ul->save_grant_params(pusch_sched_ctx, last_pusch->pusch_cfg); - - logger.debug("rnti={} h_id={}: Extended PUSCH grant from {} to {} CRBs", - ue_ref.crnti, - fmt::underlying(h_ul->id()), - old_crbs, - crbs); + // TODO } diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h index eef77de1e3..ac7af4454a 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h @@ -66,8 +66,6 @@ class ue_cell_grid_allocator return *cells[cell_index].cell_alloc; } - void post_process_ul_results(du_cell_index_t cell_idx, slot_point pusch_slot); - const scheduler_ue_expert_config& expert_cfg; ue_repository& ues; From 5205349b7d834f9a270fc63810bccc1a4a1369e2 Mon Sep 17 00:00:00 2001 From: Jonathan Pichel Carrera Date: Thu, 19 Dec 2024 16:03:05 +0100 Subject: [PATCH 101/107] mac: prepare pucch test_bench for testing formats 3 and 4 --- .../pucch_alloc_common_harq_test.cpp | 2 +- .../pucch_alloc_harq_sr_csi_test.cpp | 2 +- .../uci_and_pucch/uci_test_utils.cpp | 66 ++++++++++++------- .../scheduler/uci_and_pucch/uci_test_utils.h | 32 +++++---- 4 files changed, 61 insertions(+), 41 deletions(-) diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp index 7b876d479d..e6a60dc66b 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp @@ -87,7 +87,7 @@ class test_pucch_harq_common_output : public ::testing::TestWithParam(t_bench.cell_cfg.pci), + t_bench.cell_cfg.pci, params.output_params.format, params.output_params.prbs, params.output_params.second_hop_prbs, diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_harq_sr_csi_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_harq_sr_csi_test.cpp index 7ce4d320a3..0793d30967 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_harq_sr_csi_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_harq_sr_csi_test.cpp @@ -18,7 +18,7 @@ using namespace srsran; class test_pucch_f2_alloc_several_prbs : public ::testing::Test, public pucch_allocator_base_tester { public: - test_pucch_f2_alloc_several_prbs() : pucch_allocator_base_tester(test_bench_params{.pucch_f2_more_prbs = true}) + test_pucch_f2_alloc_several_prbs() : pucch_allocator_base_tester(test_bench_params{.pucch_f2_f3_more_prbs = true}) { // This PUCCH grant will be for 5 HARQ bits, which fit in 1 PRB. pucch_expected_harq_only.format = srsran::pucch_format::FORMAT_2; diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp index 91ea1c2bb1..6eaca82adb 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp @@ -156,8 +156,9 @@ test_bench::test_bench(const test_bench_params& params, k0(cell_cfg.dl_cfg_common.init_dl_bwp.pdsch_common.pdsch_td_alloc_list[0].k0), max_pucchs_per_slot{max_pucchs_per_slot_}, max_ul_grants_per_slot{max_ul_grants_per_slot_}, - pucch_f2_more_prbs{params.pucch_f2_more_prbs}, + pucch_f2_f3_more_prbs{params.pucch_f2_f3_more_prbs}, use_format_0(params.use_format_0), + set1_format(params.set1_format), pucch_alloc{cell_cfg, max_pucchs_per_slot, max_ul_grants_per_slot}, uci_alloc(pucch_alloc), uci_sched{cell_cfg, uci_alloc, ues}, @@ -185,17 +186,50 @@ test_bench::test_bench(const test_bench_params& params, ul_cfg.init_ul_bwp.pucch_cfg->sr_res_list[0].period = params.period; ul_cfg.init_ul_bwp.pucch_cfg->sr_res_list[0].offset = params.offset; - // Change the number of PRBs for PUCCH format 2 if the test bench parameter is set. - if (pucch_f2_more_prbs) { - const unsigned pucch_f2_nof_prbs = 3U; + auto& csi_report = std::get( + ue_req.cfg.cells.value().back().serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type); + csi_report.report_slot_period = params.csi_period; + csi_report.report_slot_offset = params.csi_offset; + + if (set1_format != pucch_format::FORMAT_2 || use_format_0) { + srs_du::pucch_builder_params pucch_params{}; + if (use_format_0) { + pucch_params.f0_or_f1_params.emplace(); + pucch_params.nof_ue_pucch_f0_or_f1_res_harq = 6; + pucch_params.nof_ue_pucch_f2_or_f3_or_f4_res_harq = 6; + } + switch (set1_format) { + case pucch_format::FORMAT_2: + break; + case pucch_format::FORMAT_3: + pucch_params.f2_or_f3_or_f4_params.emplace(); + break; + case pucch_format::FORMAT_4: + pucch_params.f2_or_f3_or_f4_params.emplace(); + break; + default: + srsran_assertion_failure("Invalid PUCCH Format for Set Id 1 (valid values are 2, 3 or 4)"); + } + pucch_builder.setup( + cell_cfg.ul_cfg_common.init_ul_bwp, params.is_tdd ? cell_cfg.tdd_cfg_common : std::nullopt, pucch_params); + // This function is called so that the PUCCH resource list is generated with the . + bool new_ue_added = pucch_builder.add_build_new_ue_pucch_cfg(ue_req.cfg.cells.value().back().serv_cell_cfg); + if (not new_ue_added) { + srsran_terminate("UE PUCCH configuration couldn't be built"); + } + } + + // Change the number of PRBs for PUCCH format 2/3 if the test bench parameter is set. + if (pucch_f2_f3_more_prbs) { + static constexpr unsigned pucch_f2_f3_nof_prbs = 3U; for (auto& pucch_res : ul_cfg.init_ul_bwp.pucch_cfg.value().pucch_res_list) { - if (pucch_res.format == pucch_format::FORMAT_2 and - std::holds_alternative(pucch_res.format_params)) { - std::get(pucch_res.format_params).nof_prbs = pucch_f2_nof_prbs; + if (pucch_res.format == set1_format and std::holds_alternative(pucch_res.format_params)) { + std::get(pucch_res.format_params).nof_prbs = pucch_f2_f3_nof_prbs; } } } + // TODO: extend for PUCCH Formats 3/4 if (params.cfg_for_mimo_4x4) { auto& pucch_cfg = ue_req.cfg.cells->back().serv_cell_cfg.ul_config->init_ul_bwp.pucch_cfg.value(); pucch_cfg.format_2_common_param.value().max_c_rate = max_pucch_code_rate::dot_35; @@ -218,24 +252,6 @@ test_bench::test_bench(const test_bench_params& params, beta_offsets.beta_offset_csi_p2_idx_1.value() = 6; } - auto& csi_report = std::get( - ue_req.cfg.cells->back().serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type); - csi_report.report_slot_period = params.csi_period; - csi_report.report_slot_offset = params.csi_offset; - - if (use_format_0) { - srs_du::pucch_builder_params pucch_params{}; - pucch_params.nof_ue_pucch_f0_or_f1_res_harq = 6; - pucch_params.nof_ue_pucch_f2_or_f3_or_f4_res_harq = 6; - pucch_params.f0_or_f1_params.emplace(); - pucch_builder.setup( - cell_cfg.ul_cfg_common.init_ul_bwp, params.is_tdd ? cell_cfg.tdd_cfg_common : std::nullopt, pucch_params); - bool new_ue_added = pucch_builder.add_build_new_ue_pucch_cfg(ue_req.cfg.cells.value().back().serv_cell_cfg); - if (not new_ue_added) { - srsran_terminate("UE PUCCH configuration couldn't be built"); - } - } - ue_ded_cfgs.push_back(std::make_unique(ue_req.ue_index, ue_req.crnti, cell_cfg_list, ue_req.cfg)); ues.add_ue(std::make_unique(ue_creation_command{*ue_ded_cfgs.back(), ue_req.starts_in_fallback, cell_harqs, {}})); uci_sched.add_ue(ues[ue_req.ue_index].get_pcell().cfg()); diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.h b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.h index c46a06d84c..cf657eca96 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.h +++ b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.h @@ -26,6 +26,8 @@ namespace srsran { namespace test_helpers { + +/// Creates a test uplink configuration using Formats 1 and 2, given the extended parameters. inline uplink_config make_test_ue_uplink_config(const config_helpers::cell_config_builder_params_extended& params) { // > UL Config. @@ -155,7 +157,7 @@ inline uplink_config make_test_ue_uplink_config(const config_helpers::cell_confi } // > PUSCH config. - ul_config.init_ul_bwp.pusch_cfg.emplace(config_helpers::make_default_pusch_config(params)); + ul_config.init_ul_bwp.pusch_cfg.emplace(make_default_pusch_config(params)); if (band_helper::get_duplex_mode(band) == duplex_mode::TDD) { ul_config.init_ul_bwp.pusch_cfg->pusch_td_alloc_list = config_helpers::generate_k2_candidates(cyclic_prefix::NORMAL, params.tdd_ul_dl_cfg_common.value()); @@ -168,7 +170,7 @@ inline uplink_config make_test_ue_uplink_config(const config_helpers::cell_confi res_f2.nof_prbs, res_f2.nof_symbols, to_max_code_rate_float(pucch_cfg.format_2_common_param.value().max_c_rate)); // > SRS config. - ul_config.init_ul_bwp.srs_cfg.emplace(config_helpers::make_default_srs_config(params)); + ul_config.init_ul_bwp.srs_cfg.emplace(make_default_srs_config(params)); return ul_config; } @@ -241,16 +243,17 @@ inline sched_cell_configuration_request_message make_custom_sched_cell_configura ///////// TEST BENCH for PUCCH scheduler ///////// struct test_bench_params { - unsigned pucch_res_common = 11; - unsigned n_cces = 0; - sr_periodicity period = sr_periodicity::sl_40; - unsigned offset = 0; - csi_report_periodicity csi_period = csi_report_periodicity::slots320; - unsigned csi_offset = 9; - bool is_tdd = false; - bool pucch_f2_more_prbs = false; - bool cfg_for_mimo_4x4 = false; - bool use_format_0 = false; + unsigned pucch_res_common = 11; + unsigned n_cces = 0; + sr_periodicity period = sr_periodicity::sl_40; + unsigned offset = 0; + csi_report_periodicity csi_period = csi_report_periodicity::slots320; + unsigned csi_offset = 9; + bool is_tdd = false; + bool pucch_f2_f3_more_prbs = false; + bool cfg_for_mimo_4x4 = false; + bool use_format_0 = false; + pucch_format set1_format = pucch_format::FORMAT_2; }; // Test bench with all that is needed for the PUCCH. @@ -285,10 +288,11 @@ class test_bench const unsigned k1{4}; const unsigned max_pucchs_per_slot; const unsigned max_ul_grants_per_slot; - du_ue_index_t main_ue_idx{du_ue_index_t::MIN_DU_UE_INDEX}; + du_ue_index_t main_ue_idx{MIN_DU_UE_INDEX}; ue_repository ues; - bool pucch_f2_more_prbs; + bool pucch_f2_f3_more_prbs; const bool use_format_0; + const pucch_format set1_format; // last_allocated_rnti keeps track of the last RNTI allocated. rnti_t last_allocated_rnti; From 13cfa6fa438c300f3de008c4cc3c5af6bb307cfb Mon Sep 17 00:00:00 2001 From: Jonathan Pichel Carrera Date: Wed, 8 Jan 2025 17:46:36 +0100 Subject: [PATCH 102/107] mac: test pucch allocation with f1 and f3 --- .../scheduler/uci_and_pucch/CMakeLists.txt | 1 + .../uci_and_pucch/pucch_alloc_base_tester.h | 2 + .../pucch_alloc_ded_resources_test.cpp | 27 +- .../pucch_alloc_format_3_test.cpp | 612 ++++++++++++++++++ .../uci_and_pucch/uci_test_utils.cpp | 8 +- .../scheduler/uci_and_pucch/uci_test_utils.h | 5 + 6 files changed, 636 insertions(+), 19 deletions(-) create mode 100644 tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp diff --git a/tests/unittests/scheduler/uci_and_pucch/CMakeLists.txt b/tests/unittests/scheduler/uci_and_pucch/CMakeLists.txt index fcc54b63cf..04a2333516 100644 --- a/tests/unittests/scheduler/uci_and_pucch/CMakeLists.txt +++ b/tests/unittests/scheduler/uci_and_pucch/CMakeLists.txt @@ -11,6 +11,7 @@ add_executable(uci_pucch_sched_test pucch_alloc_common_harq_test.cpp pucch_alloc_ded_resources_test.cpp pucch_alloc_format_0_test.cpp + pucch_alloc_format_3_test.cpp pucch_alloc_harq_sr_csi_test.cpp pucch_guardbands_sched_test.cpp pucch_res_manager_test.cpp diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h index 44ed06d0fd..a88ccf0bb3 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h @@ -14,6 +14,8 @@ using namespace srsran; +constexpr unsigned NOF_RBS = 52; + class pucch_allocator_base_tester { public: diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_ded_resources_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_ded_resources_test.cpp index bcd6ad73b3..f88a6e2ccc 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_ded_resources_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_ded_resources_test.cpp @@ -15,8 +15,6 @@ using namespace srsran; -constexpr unsigned NOF_RBS = 52; - /////// Test allocation of dedicated PUCCH resources /////// class test_pucch_allocator_ded_resources : public ::testing::Test, public pucch_allocator_base_tester @@ -532,22 +530,25 @@ TEST_F(test_pucch_allocator_ded_resources, test_harq_alloc_3bits_over_csi) t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; - // Expect 1 HARQ and 1 SR. + ASSERT_TRUE(test_pucch_res_indicator.has_value()); ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); - ASSERT_EQ(2, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(slot_grid.result.ul.pucchs.size() == 1 or slot_grid.result.ul.pucchs.size() == 2); const auto& pucch_pdus = slot_grid.result.ul.pucchs; ASSERT_TRUE(find_pucch_pdu(pucch_pdus, [](const auto& pdu) { return pdu.csi_rep_cfg.has_value(); })); - - ASSERT_TRUE( - find_pucch_pdu(pucch_pdus, - [&expected = pucch_expected_f2](const auto& pdu) { return pucch_info_match(expected, pdu); }) or - find_pucch_pdu(pucch_pdus, - [&expected = pucch_expected_csi](const auto& pdu) { return pucch_info_match(expected, pdu); }) or - find_pucch_pdu(pucch_pdus, [&expected = pucch_f2_harq_csi_mplexed](const auto& pdu) { - return pucch_info_match(expected, pdu); - })); + if (slot_grid.result.ul.pucchs.size() == 2) { + // Separate resources. + ASSERT_TRUE(find_pucch_pdu( + pucch_pdus, [&expected = pucch_expected_f2](const auto& pdu) { return pucch_info_match(expected, pdu); })); + ASSERT_TRUE(find_pucch_pdu( + pucch_pdus, [&expected = pucch_expected_csi](const auto& pdu) { return pucch_info_match(expected, pdu); })); + } else { + // Multiplexed. + ASSERT_TRUE(find_pucch_pdu(pucch_pdus, [&expected = pucch_f2_harq_csi_mplexed](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + } } TEST_F(test_pucch_allocator_ded_resources, test_harq_alloc_3bits_over_sr_and_csi) diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp new file mode 100644 index 0000000000..16efabb568 --- /dev/null +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp @@ -0,0 +1,612 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * By using this file, you agree to the terms and conditions set + * forth in the LICENSE file which can be found at the top level of + * the distribution. + * + */ + +#include "../test_utils/scheduler_test_suite.h" +#include "pucch_alloc_base_tester.h" +#include "uci_test_utils.h" +#include + +using namespace srsran; + +/////// Test PUCCH Format 3. /////// + +class test_pucch_allocator_format_3 : public ::testing::Test, public pucch_allocator_base_tester +{ +public: + test_pucch_allocator_format_3() : + pucch_allocator_base_tester(test_bench_params{.pucch_res_common = 0, .set1_format = pucch_format::FORMAT_3}) + { + // Set expected grant for PUCCH Format 1 SR. + // The expected resource for SR corresponds to the second to last of the FORMAT 1 resources in the resource list. + pucch_expected_f1_sr.format = pucch_format::FORMAT_1; + pucch_expected_f1_sr.crnti = to_rnti(0x4601); + pucch_expected_f1_sr.bwp_cfg = &t_bench.cell_cfg.ul_cfg_common.init_ul_bwp.generic_params; + + pucch_expected_f1_sr.resources.prbs = prb_interval{3, 4}; + pucch_expected_f1_sr.resources.second_hop_prbs = prb_interval{0, 0}; + pucch_expected_f1_sr.resources.symbols = ofdm_symbol_range{0, 14}; + + pucch_expected_f1_sr.format_1.initial_cyclic_shift = 0; + pucch_expected_f1_sr.format_1.sr_bits = sr_nof_bits::one; + pucch_expected_f1_sr.format_1.harq_ack_nof_bits = 0; + pucch_expected_f1_sr.format_1.time_domain_occ = 0; + + pucch_expected_f1_sr.format_1.group_hopping = pucch_group_hopping::NEITHER; + pucch_expected_f1_sr.format_1.n_id_hopping = t_bench.cell_cfg.pci; + pucch_expected_f1_sr.format_1.slot_repetition = pucch_repetition_tx_slot::no_multi_slot; + + // Set expected grant for PUCCH Format 1 HARQ. + // The expected resource for HARQ corresponds to the first resource in the resource list. + pucch_expected_f1_harq.format = pucch_format::FORMAT_1; + pucch_expected_f1_harq.crnti = to_rnti(0x4601); + pucch_expected_f1_harq.bwp_cfg = &t_bench.cell_cfg.ul_cfg_common.init_ul_bwp.generic_params; + + pucch_expected_f1_harq.resources.prbs = prb_interval{0, 1}; + pucch_expected_f1_harq.resources.second_hop_prbs = prb_interval{0, 0}; + pucch_expected_f1_harq.resources.symbols = ofdm_symbol_range{0, 14}; + + pucch_expected_f1_harq.format_1.initial_cyclic_shift = 0; + pucch_expected_f1_harq.format_1.sr_bits = sr_nof_bits::no_sr; + pucch_expected_f1_harq.format_1.harq_ack_nof_bits = 1; + pucch_expected_f1_harq.format_1.time_domain_occ = 0; + + // Set expected grant for PUCCH Format 1. + pucch_expected_f1_harq.format_1.group_hopping = pucch_group_hopping::NEITHER; + pucch_expected_f1_harq.format_1.n_id_hopping = t_bench.cell_cfg.pci; + pucch_expected_f1_harq.format_1.slot_repetition = pucch_repetition_tx_slot::no_multi_slot; + + // The expected resource for HARQ corresponds to the first of the FORMAT 3 resources in the resource list. + pucch_expected_f3.format = pucch_format::FORMAT_3; + pucch_expected_f3.crnti = to_rnti(0x4601); + pucch_expected_f3.bwp_cfg = &t_bench.cell_cfg.ul_cfg_common.init_ul_bwp.generic_params; + pucch_expected_f3.resources.prbs = prb_interval{4, 5}; + pucch_expected_f3.resources.second_hop_prbs = prb_interval{0, 0}; + pucch_expected_f3.resources.symbols = ofdm_symbol_range{0, 4}; + + pucch_expected_f3.format_3.max_code_rate = max_pucch_code_rate::dot_25; + pucch_expected_f3.format_3.n_id_hopping = t_bench.cell_cfg.pci; + pucch_expected_f3.format_3.n_id_scrambling = t_bench.cell_cfg.pci; + pucch_expected_f3.format_3.n_id_0_scrambling = t_bench.cell_cfg.pci; + + // The expected resource for CSI corresponds to the last resource in the resource list. + pucch_expected_csi.format = pucch_format::FORMAT_3; + pucch_expected_csi.crnti = to_rnti(0x4601); + pucch_expected_csi.bwp_cfg = &t_bench.cell_cfg.ul_cfg_common.init_ul_bwp.generic_params; + pucch_expected_csi.resources.prbs = prb_interval{5, 6}; + pucch_expected_csi.resources.second_hop_prbs = prb_interval{0, 0}; + pucch_expected_csi.resources.symbols = ofdm_symbol_range{0, 4}; + + pucch_expected_csi.format_3.max_code_rate = max_pucch_code_rate::dot_25; + pucch_expected_csi.format_3.n_id_hopping = t_bench.cell_cfg.pci; + pucch_expected_csi.format_3.n_id_scrambling = t_bench.cell_cfg.pci; + pucch_expected_csi.format_3.n_id_0_scrambling = t_bench.cell_cfg.pci; + }; + +protected: + // Parameters that are passed by the routine to run the tests. + pucch_info pucch_expected_f1_sr; + pucch_info pucch_expected_f1_harq; + pucch_info pucch_expected_f3; + pucch_info pucch_expected_csi; +}; + +/////////////// Tests PUCCH allocator for SR. + +TEST_F(test_pucch_allocator_format_3, test_sr_allocation_only) +{ + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + t_bench.pucch_alloc.pucch_allocate_sr_opportunity( + slot_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg()); + + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_sr](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); +} + +/////////////// Tests PUCCH allocator for CSI. + +TEST_F(test_pucch_allocator_format_3, test_csi_alloc_only) +{ + static constexpr unsigned csi_part1_bits = 4; + pucch_expected_csi.format_3.harq_ack_nof_bits = 0; + pucch_expected_csi.format_3.sr_bits = sr_nof_bits::no_sr; + pucch_expected_csi.format_3.csi_part1_bits = csi_part1_bits; + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + t_bench.pucch_alloc.pucch_allocate_csi_opportunity( + slot_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), csi_part1_bits); + + // Expect 1 PUCCH PDU. + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_csi](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + ASSERT_TRUE(slot_grid.result.ul.pucchs[0].csi_rep_cfg.has_value()); +} + +TEST_F(test_pucch_allocator_format_3, test_csi_alloc_over_sr) +{ + static constexpr unsigned csi_part1_bits = 4; + pucch_expected_csi.format_3.harq_ack_nof_bits = 0; + pucch_expected_csi.format_3.sr_bits = sr_nof_bits::one; + pucch_expected_csi.format_3.csi_part1_bits = csi_part1_bits; + + add_sr_grant(); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + t_bench.pucch_alloc.pucch_allocate_csi_opportunity( + slot_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), csi_part1_bits); + + // Expect 1 PUCCH PDU. + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_csi](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + ASSERT_TRUE(slot_grid.result.ul.pucchs[0].csi_rep_cfg.has_value()); +} + +TEST_F(test_pucch_allocator_format_3, test_csi_alloc_when_no_free_csi_resources) +{ + static constexpr unsigned csi_part1_bits = 4; + pucch_expected_csi.format_3.harq_ack_nof_bits = 0; + pucch_expected_csi.format_3.sr_bits = sr_nof_bits::no_sr; + pucch_expected_csi.format_3.csi_part1_bits = csi_part1_bits; + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + t_bench.add_ue(); + t_bench.pucch_alloc.pucch_allocate_csi_opportunity(slot_grid, + t_bench.last_allocated_rnti, + t_bench.get_ue(t_bench.last_allocated_ue_idx).get_pcell().cfg(), + csi_part1_bits); + // Expect 1 PUCCH PDU. + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + + t_bench.pucch_alloc.pucch_allocate_csi_opportunity( + slot_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), csi_part1_bits); + + // The last CSI allocation should have failed. Expect still the same nof of PUCCH PDUs. + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); +} + +TEST_F(test_pucch_allocator_format_3, test_csi_alloc_over_common_harq_grant) +{ + static constexpr unsigned csi_part1_bits = 4; + pucch_expected_csi.format_3.harq_ack_nof_bits = 0; + pucch_expected_csi.format_3.sr_bits = sr_nof_bits::no_sr; + pucch_expected_csi.format_3.csi_part1_bits = csi_part1_bits; + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + std::optional pucch_res_indicator = t_bench.pucch_alloc.alloc_common_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.k0, t_bench.k1, t_bench.dci_info); + ASSERT_TRUE(pucch_res_indicator.has_value()); + // Expect 1 PUCCH PDU. + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + + t_bench.pucch_alloc.pucch_allocate_csi_opportunity( + slot_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), csi_part1_bits); + + // Expect 1 PUCCH PDU, as the CSI over common PUCCH should be scheduled following CSI first, then the PUCCH common, + // not the other way around. + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); +} + +/////// Test HARQ-ACK allocation on ded. resources - Format 1 /////// + +TEST_F(test_pucch_allocator_format_3, test_harq_allocation_only) +{ + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + const std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_harq](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_allocation_over_sr) +{ + add_sr_grant(); + const std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + // Expect 1 HARQ and 1 SR. + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + ASSERT_EQ(2, slot_grid.result.ul.pucchs.size()); + // Verify that the UCI bits grants are correct. + pucch_expected_f1_sr.format_1.harq_ack_nof_bits = 1; + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_sr](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_harq](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_2bits) +{ + const std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + const auto first_alloc = slot_grid.result.ul.pucchs.front(); + + pucch_expected_f1_harq.format_1.harq_ack_nof_bits = 2; + const std::optional test_pucch_res_indicator_1 = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_TRUE(test_pucch_res_indicator_1.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator_1.value()); + // Expect 1 PUCCH HARQ. + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_harq](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + + // Make sure the second PUCCH allocation uses the same PRBs and symbols as the first one. + const auto second_alloc = slot_grid.result.ul.pucchs.front(); + ASSERT_EQ(first_alloc.resources.prbs, second_alloc.resources.prbs); + ASSERT_EQ(first_alloc.resources.symbols, second_alloc.resources.symbols); + ASSERT_EQ(first_alloc.resources.second_hop_prbs, second_alloc.resources.second_hop_prbs); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_2bits_over_sr) +{ + add_sr_grant(); + + const std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + const auto* first_alloc = std::find_if(slot_grid.result.ul.pucchs.begin(), + slot_grid.result.ul.pucchs.end(), + [rnti = t_bench.get_main_ue().crnti](const auto& pdu) { + return pdu.crnti == rnti and pdu.format_1.harq_ack_nof_bits == 1U and + pdu.format_1.sr_bits == sr_nof_bits::no_sr; + }); + ASSERT_TRUE(first_alloc != slot_grid.result.ul.pucchs.end()); + + pucch_expected_f1_harq.format_1.harq_ack_nof_bits = 2; + pucch_expected_f1_sr.format_1.harq_ack_nof_bits = 2; + const std::optional test_pucch_res_indicator_1 = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + // Expect 1 HARQ and 1 SR. + ASSERT_TRUE(test_pucch_res_indicator_1.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator_1.value()); + ASSERT_EQ(2, slot_grid.result.ul.pucchs.size()); + // Verify that the UCI bits grants are correct. + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_sr](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_harq](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + + // Make sure the second PUCCH allocation uses the same PRBs and symbols as the first one. + const auto* second_alloc = std::find_if(slot_grid.result.ul.pucchs.begin(), + slot_grid.result.ul.pucchs.end(), + [rnti = t_bench.get_main_ue().crnti](const auto& pdu) { + return pdu.crnti == rnti and pdu.format_1.harq_ack_nof_bits == 2U and + pdu.format_1.sr_bits == sr_nof_bits::no_sr; + }); + ASSERT_TRUE(first_alloc != slot_grid.result.ul.pucchs.end()); + ASSERT_EQ(first_alloc->resources.prbs, second_alloc->resources.prbs); + ASSERT_EQ(first_alloc->resources.symbols, second_alloc->resources.symbols); + ASSERT_EQ(first_alloc->resources.second_hop_prbs, second_alloc->resources.second_hop_prbs); +} + +/////// Test HARQ-ACK allocation on ded. resources - Format 3 /////// + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_3bits) +{ + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + // By allocating the HARQ-ACK 3 times, the PUCCH is forced to convert the Format 1 into format 3, as Format 1 can + // carry 2 HARQ bits, at most. + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_EQ(pucch_format::FORMAT_1, slot_grid.result.ul.pucchs[0].format); + ASSERT_EQ(1, slot_grid.result.ul.pucchs[0].format_1.harq_ack_nof_bits); + + test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_EQ(pucch_format::FORMAT_1, slot_grid.result.ul.pucchs[0].format); + ASSERT_EQ(2, slot_grid.result.ul.pucchs[0].format_1.harq_ack_nof_bits); + + // Convert to Format 3 and with 3 bits HARQ. + test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_EQ(pucch_format::FORMAT_3, slot_grid.result.ul.pucchs[0].format); + ASSERT_EQ(3, slot_grid.result.ul.pucchs[0].format_3.harq_ack_nof_bits); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_3bits_over_sr) +{ + pucch_expected_f3.format_3.harq_ack_nof_bits = 3; + pucch_expected_f3.format_3.sr_bits = sr_nof_bits::one; + pucch_expected_f3.format_3.csi_part1_bits = 0; + + add_sr_grant(); + add_harq_grant(); + add_harq_grant(); + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + // Expect 1 resource with HARQ+SR. + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f3](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_1bit_over_csi) +{ + // With 1 HARQ-ACK bit, the F1 HARQ-ACK resource overlaps with the F3 resource for CSI, thus the 2 resources will be + // multiplexed into 1, which is the PUCCH HARQ resource from set 1 (Format 3). + pucch_expected_f3.format_3.harq_ack_nof_bits = 1; + pucch_expected_f3.format_3.sr_bits = sr_nof_bits::no_sr; + pucch_expected_f3.format_3.csi_part1_bits = 4; + + add_csi_grant(); + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + // Expect 1 resource with HARQ+CSI. + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + + const auto& pucch_pdus = slot_grid.result.ul.pucchs; + ASSERT_TRUE(find_pucch_pdu(pucch_pdus, [](const auto& pdu) { return pdu.csi_rep_cfg.has_value(); })); + + ASSERT_TRUE(find_pucch_pdu( + pucch_pdus, [&expected = pucch_expected_f3](const auto& pdu) { return pucch_info_match(expected, pdu); })); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_2bits_over_csi) +{ + // With 2 HARQ-ACK bits, the F1 HARQ-ACK resource overlaps with the F3 resource for CSI, thus the 2 resources will be + // multiplexed into 1, which is the PUCCH HARQ resource from set 1 (Format 3). + pucch_expected_f3.format_3.harq_ack_nof_bits = 2; + pucch_expected_f3.format_3.sr_bits = sr_nof_bits::no_sr; + pucch_expected_f3.format_3.csi_part1_bits = 4; + + add_csi_grant(); + add_harq_grant(); + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + // Expect 1 resource with HARQ+CSI. + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + + const auto& pucch_pdus = slot_grid.result.ul.pucchs; + ASSERT_TRUE(find_pucch_pdu(pucch_pdus, [](const auto& pdu) { return pdu.csi_rep_cfg.has_value(); })); + + ASSERT_TRUE(find_pucch_pdu( + pucch_pdus, [&expected = pucch_expected_f3](const auto& pdu) { return pucch_info_match(expected, pdu); })); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_3bits_over_csi) +{ + // We don't know a-priori whether CSI and HARQ will be multiplexed within the same resource; we need to consider both + // possibilities, (i) 2 separate PUCCH resources HARQ + CSI, and (ii) 1 PUCCH resource with both HARQ and CSI. + pucch_expected_f3.format_3.harq_ack_nof_bits = 3; + pucch_expected_f3.format_3.sr_bits = sr_nof_bits::no_sr; + pucch_expected_f3.format_3.csi_part1_bits = 0; + + pucch_expected_csi.format_3.harq_ack_nof_bits = 0; + pucch_expected_csi.format_3.sr_bits = sr_nof_bits::no_sr; + pucch_expected_csi.format_3.csi_part1_bits = 4; + + pucch_info pucch_f3_harq_csi_mplexed = pucch_expected_f3; + pucch_f3_harq_csi_mplexed.format_3.harq_ack_nof_bits = 3; + pucch_f3_harq_csi_mplexed.format_3.sr_bits = sr_nof_bits::no_sr; + pucch_f3_harq_csi_mplexed.format_3.csi_part1_bits = 4; + + add_csi_grant(); + add_harq_grant(); + add_harq_grant(); + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_TRUE(slot_grid.result.ul.pucchs.size() == 1 or slot_grid.result.ul.pucchs.size() == 2); + + const auto& pucch_pdus = slot_grid.result.ul.pucchs; + ASSERT_TRUE(find_pucch_pdu(pucch_pdus, [](const auto& pdu) { return pdu.csi_rep_cfg.has_value(); })); + if (slot_grid.result.ul.pucchs.size() == 2) { + // Separate resources. + ASSERT_TRUE(find_pucch_pdu( + pucch_pdus, [&expected = pucch_expected_f3](const auto& pdu) { return pucch_info_match(expected, pdu); })); + ASSERT_TRUE(find_pucch_pdu( + pucch_pdus, [&expected = pucch_expected_csi](const auto& pdu) { return pucch_info_match(expected, pdu); })); + } else { + // Multiplexed. + ASSERT_TRUE(find_pucch_pdu(pucch_pdus, [&expected = pucch_f3_harq_csi_mplexed](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + } +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_3bits_over_sr_and_csi) +{ + // With SR and with PUCCH Format 1 it is guaranteed that the resources will be multiplexed, as PUCCH Format 1 for SR + // spans over the 14 symbols. + pucch_expected_f3.format_3.harq_ack_nof_bits = 3; + pucch_expected_f3.format_3.sr_bits = sr_nof_bits::one; + pucch_expected_f3.format_3.csi_part1_bits = 4; + + add_sr_grant(); + add_csi_grant(); + add_harq_grant(); + add_harq_grant(); + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + // Expect 1 resource with HARQ+SR+CSI. + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f3](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_4bits_over_sr_and_csi) +{ + // With SR and with PUCCH Format 1 it is guaranteed that the resources will be multiplexed, as PUCCH Format 1 for SR + // spans over the 14 symbols. + pucch_expected_f3.format_3.harq_ack_nof_bits = 4; + pucch_expected_f3.format_3.sr_bits = sr_nof_bits::one; + pucch_expected_f3.format_3.csi_part1_bits = 4; + + add_sr_grant(); + add_csi_grant(); + add_harq_grant(); + add_harq_grant(); + add_harq_grant(); + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + // Expect 1 resource with HARQ+SR+CSI. + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); + ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f3](const auto& pdu) { + return pucch_info_match(expected, pdu); + })); + ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [](const auto& pdu) { return pdu.csi_rep_cfg.has_value(); })); +} + +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_11bits_over_sr_and_csi_fails) +{ + add_sr_grant(); + add_csi_grant(); + for (int i = 0; i != 10; i++) { + add_harq_grant(); + } + + // This should fail, as the PUCCH F3 payload for this configuration allows max 12 UCI bits. + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_FALSE(test_pucch_res_indicator.has_value()); +} + +TEST_F(test_pucch_allocator_format_3, when_converting_harq_f1_to_f3_during_mplexing_csi_preserve_res_indicator) +{ + // This makes PUCCH resource indicator 0 busy for PUCCH resource set 0. + add_ue_with_harq_grant(); + add_csi_grant(); + + // At the end of the PUCCH allocation with Format 3, we expect the same PUCCH as for PUCCH format 1. + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + // PUCCH resource indicator 0 is used by the first UE that got allocated. + ASSERT_EQ(1U, test_pucch_res_indicator.value()); +} + +TEST_F(test_pucch_allocator_format_3, when_converting_harq_f1_to_f3_during_mplexing_sr_csi_preserve_res_indicator) +{ + // This makes PUCCH resource indicator 0 busy for PUCCH resource set 0. + add_ue_with_harq_grant(); + add_sr_grant(); + add_csi_grant(); + + // At the end of the PUCCH allocation with Format 3, we expect the same PUCCH as for PUCCH format 1. + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + // PUCCH resource indicator 0 is used by the first UE that got allocated. + ASSERT_EQ(1U, test_pucch_res_indicator.value()); +} + +TEST_F(test_pucch_allocator_format_3, with_f3_res_1_harq_bit_adding_adding_extra_bit_doesnt_change_res_indicator) +{ + // This makes PUCCH resource indicator 0 busy for PUCCH resource set 0. + add_ue_with_harq_grant(); + add_csi_grant(); + + // After the second PUCCH allocation with Format 3, we expect the same PUCCH res indicator as for the first + // allocation of HARQ-ACK bit. + std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_TRUE(test_pucch_res_indicator.has_value()); + // PUCCH resource indicator 0 is used by the first UE that got allocated. + ASSERT_EQ(1U, test_pucch_res_indicator.value()); + + auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; + + const auto* first_alloc = + std::find_if(slot_grid.result.ul.pucchs.begin(), + slot_grid.result.ul.pucchs.end(), + [rnti = t_bench.get_main_ue().crnti](const auto& pdu) { return pdu.crnti == rnti; }); + ASSERT_TRUE(first_alloc != slot_grid.result.ul.pucchs.end()); + + std::optional test_pucch_res_indicator_new = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( + t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); + + ASSERT_TRUE(test_pucch_res_indicator_new.has_value()); + // PUCCH resource indicator after the second allocation should not have changed. + ASSERT_EQ(test_pucch_res_indicator.value(), test_pucch_res_indicator_new.value()); + + // Make sure the second PUCCH allocation uses the same PRBs and symbols as the first one. + const auto* second_alloc = + std::find_if(slot_grid.result.ul.pucchs.begin(), + slot_grid.result.ul.pucchs.end(), + [rnti = t_bench.get_main_ue().crnti](const auto& pdu) { return pdu.crnti == rnti; }); + ASSERT_TRUE(second_alloc != slot_grid.result.ul.pucchs.end()); + ASSERT_EQ(first_alloc->resources.prbs, second_alloc->resources.prbs); + ASSERT_EQ(first_alloc->resources.symbols, second_alloc->resources.symbols); + ASSERT_EQ(first_alloc->resources.second_hop_prbs, second_alloc->resources.second_hop_prbs); +} diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp index 6eaca82adb..aa708e9248 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp @@ -252,6 +252,7 @@ test_bench::test_bench(const test_bench_params& params, beta_offsets.beta_offset_csi_p2_idx_1.value() = 6; } + ue_req_main = ue_req; ue_ded_cfgs.push_back(std::make_unique(ue_req.ue_index, ue_req.crnti, cell_cfg_list, ue_req.cfg)); ues.add_ue(std::make_unique(ue_creation_command{*ue_ded_cfgs.back(), ue_req.starts_in_fallback, cell_harqs, {}})); uci_sched.add_ue(ues[ue_req.ue_index].get_pcell().cfg()); @@ -273,16 +274,11 @@ const ue& test_bench::get_ue(du_ue_index_t ue_idx) const void test_bench::add_ue() { - cell_config_builder_params cfg_params{}; - cfg_params.csi_rs_enabled = true; - sched_ue_creation_request_message ue_req = sched_config_helper::create_default_sched_ue_creation_request(cfg_params); + sched_ue_creation_request_message ue_req = ue_req_main; last_allocated_ue_idx = to_du_ue_index(static_cast::type>(last_allocated_ue_idx) + 1); ue_req.ue_index = last_allocated_ue_idx; - ue_req.cfg.cells->begin()->serv_cell_cfg.ul_config.reset(); - ue_req.cfg.cells->begin()->serv_cell_cfg.ul_config.emplace(test_helpers::make_test_ue_uplink_config(cfg_params)); - ue_req.crnti = to_rnti(static_cast::type>(last_allocated_rnti) + 1); srsran_assert(not use_format_0 or diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.h b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.h index cf657eca96..cc054c6d62 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.h +++ b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.h @@ -139,6 +139,8 @@ inline uplink_config make_test_ue_uplink_config(const config_helpers::cell_confi pucch_cfg.format_1_common_param.emplace(); pucch_cfg.format_2_common_param.emplace( pucch_common_all_formats{.max_c_rate = max_pucch_code_rate::dot_25, .simultaneous_harq_ack_csi = true}); + pucch_cfg.format_3_common_param.emplace( + pucch_common_all_formats{.max_c_rate = max_pucch_code_rate::dot_25, .simultaneous_harq_ack_csi = true}); // >>> dl-DataToUl-Ack // TS38.213, 9.1.2.1 - "If a UE is provided dl-DataToUL-ACK, the UE does not expect to be indicated by DCI format 1_0 @@ -304,6 +306,9 @@ class test_bench pucch_res_builder_test_helper pucch_builder; srslog::basic_logger& mac_logger = srslog::fetch_basic_logger("SCHED", true); srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); + + // Scheduler creation request message used to create the main UE. + sched_ue_creation_request_message ue_req_main; }; } // namespace srsran From 33c8d74dbc925151e4d0dd16812574fde4abbcfe Mon Sep 17 00:00:00 2001 From: JPichel Date: Thu, 9 Jan 2025 15:19:14 +0100 Subject: [PATCH 103/107] mac: remove harq f1 allocation test cases from pucch_alloc_format_3_test.cpp --- .../pucch_alloc_format_3_test.cpp | 116 ------------------ 1 file changed, 116 deletions(-) diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp index 16efabb568..a316ba3df4 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp @@ -201,122 +201,6 @@ TEST_F(test_pucch_allocator_format_3, test_csi_alloc_over_common_harq_grant) ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); } -/////// Test HARQ-ACK allocation on ded. resources - Format 1 /////// - -TEST_F(test_pucch_allocator_format_3, test_harq_allocation_only) -{ - auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; - const std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( - t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); - - ASSERT_TRUE(test_pucch_res_indicator.has_value()); - ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator); - ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); - ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_harq](const auto& pdu) { - return pucch_info_match(expected, pdu); - })); -} - -TEST_F(test_pucch_allocator_format_3, test_harq_allocation_over_sr) -{ - add_sr_grant(); - const std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( - t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); - - // Expect 1 HARQ and 1 SR. - ASSERT_TRUE(test_pucch_res_indicator.has_value()); - ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); - auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; - ASSERT_EQ(2, slot_grid.result.ul.pucchs.size()); - // Verify that the UCI bits grants are correct. - pucch_expected_f1_sr.format_1.harq_ack_nof_bits = 1; - ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_sr](const auto& pdu) { - return pucch_info_match(expected, pdu); - })); - ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_harq](const auto& pdu) { - return pucch_info_match(expected, pdu); - })); -} - -TEST_F(test_pucch_allocator_format_3, test_harq_alloc_2bits) -{ - const std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( - t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); - ASSERT_TRUE(test_pucch_res_indicator.has_value()); - ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); - - auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; - - const auto first_alloc = slot_grid.result.ul.pucchs.front(); - - pucch_expected_f1_harq.format_1.harq_ack_nof_bits = 2; - const std::optional test_pucch_res_indicator_1 = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( - t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); - - ASSERT_TRUE(test_pucch_res_indicator_1.has_value()); - ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator_1.value()); - // Expect 1 PUCCH HARQ. - ASSERT_EQ(1, slot_grid.result.ul.pucchs.size()); - ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_harq](const auto& pdu) { - return pucch_info_match(expected, pdu); - })); - - // Make sure the second PUCCH allocation uses the same PRBs and symbols as the first one. - const auto second_alloc = slot_grid.result.ul.pucchs.front(); - ASSERT_EQ(first_alloc.resources.prbs, second_alloc.resources.prbs); - ASSERT_EQ(first_alloc.resources.symbols, second_alloc.resources.symbols); - ASSERT_EQ(first_alloc.resources.second_hop_prbs, second_alloc.resources.second_hop_prbs); -} - -TEST_F(test_pucch_allocator_format_3, test_harq_alloc_2bits_over_sr) -{ - add_sr_grant(); - - const std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( - t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); - ASSERT_TRUE(test_pucch_res_indicator.has_value()); - ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator.value()); - - auto& slot_grid = t_bench.res_grid[t_bench.k0 + t_bench.k1]; - - const auto* first_alloc = std::find_if(slot_grid.result.ul.pucchs.begin(), - slot_grid.result.ul.pucchs.end(), - [rnti = t_bench.get_main_ue().crnti](const auto& pdu) { - return pdu.crnti == rnti and pdu.format_1.harq_ack_nof_bits == 1U and - pdu.format_1.sr_bits == sr_nof_bits::no_sr; - }); - ASSERT_TRUE(first_alloc != slot_grid.result.ul.pucchs.end()); - - pucch_expected_f1_harq.format_1.harq_ack_nof_bits = 2; - pucch_expected_f1_sr.format_1.harq_ack_nof_bits = 2; - const std::optional test_pucch_res_indicator_1 = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( - t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); - - // Expect 1 HARQ and 1 SR. - ASSERT_TRUE(test_pucch_res_indicator_1.has_value()); - ASSERT_EQ(pucch_res_idx, test_pucch_res_indicator_1.value()); - ASSERT_EQ(2, slot_grid.result.ul.pucchs.size()); - // Verify that the UCI bits grants are correct. - ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_sr](const auto& pdu) { - return pucch_info_match(expected, pdu); - })); - ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [&expected = pucch_expected_f1_harq](const auto& pdu) { - return pucch_info_match(expected, pdu); - })); - - // Make sure the second PUCCH allocation uses the same PRBs and symbols as the first one. - const auto* second_alloc = std::find_if(slot_grid.result.ul.pucchs.begin(), - slot_grid.result.ul.pucchs.end(), - [rnti = t_bench.get_main_ue().crnti](const auto& pdu) { - return pdu.crnti == rnti and pdu.format_1.harq_ack_nof_bits == 2U and - pdu.format_1.sr_bits == sr_nof_bits::no_sr; - }); - ASSERT_TRUE(first_alloc != slot_grid.result.ul.pucchs.end()); - ASSERT_EQ(first_alloc->resources.prbs, second_alloc->resources.prbs); - ASSERT_EQ(first_alloc->resources.symbols, second_alloc->resources.symbols); - ASSERT_EQ(first_alloc->resources.second_hop_prbs, second_alloc->resources.second_hop_prbs); -} - /////// Test HARQ-ACK allocation on ded. resources - Format 3 /////// TEST_F(test_pucch_allocator_format_3, test_harq_alloc_3bits) From facfa18fed67d37eee2f23892576fb6dd6812ee7 Mon Sep 17 00:00:00 2001 From: JPichel Date: Fri, 10 Jan 2025 10:33:05 +0100 Subject: [PATCH 104/107] mac: review test pucch allocation with f1 and f3 --- .../scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp | 2 +- tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp index e6a60dc66b..7b876d479d 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_common_harq_test.cpp @@ -87,7 +87,7 @@ class test_pucch_harq_common_output : public ::testing::TestWithParam(t_bench.cell_cfg.pci), params.output_params.format, params.output_params.prbs, params.output_params.second_hop_prbs, diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp index aa708e9248..b0b2490860 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp @@ -212,7 +212,7 @@ test_bench::test_bench(const test_bench_params& params, } pucch_builder.setup( cell_cfg.ul_cfg_common.init_ul_bwp, params.is_tdd ? cell_cfg.tdd_cfg_common : std::nullopt, pucch_params); - // This function is called so that the PUCCH resource list is generated with the . + // This function is called so that the PUCCH resource list is generated again with the new parameters. bool new_ue_added = pucch_builder.add_build_new_ue_pucch_cfg(ue_req.cfg.cells.value().back().serv_cell_cfg); if (not new_ue_added) { srsran_terminate("UE PUCCH configuration couldn't be built"); From 7d8898355bf37a04b8b2e7475f0adad4a636527f Mon Sep 17 00:00:00 2001 From: JPichel Date: Fri, 10 Jan 2025 13:14:23 +0100 Subject: [PATCH 105/107] mac: fix memcheck for pucch_alloc_format_3_test --- .../scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp index a316ba3df4..eaa2d99dc9 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp @@ -71,9 +71,13 @@ class test_pucch_allocator_format_3 : public ::testing::Test, public pucch_alloc pucch_expected_f3.resources.symbols = ofdm_symbol_range{0, 4}; pucch_expected_f3.format_3.max_code_rate = max_pucch_code_rate::dot_25; + pucch_expected_f3.format_3.group_hopping = pucch_group_hopping::NEITHER; + pucch_expected_f3.format_3.slot_repetition = pucch_repetition_tx_slot::no_multi_slot; pucch_expected_f3.format_3.n_id_hopping = t_bench.cell_cfg.pci; pucch_expected_f3.format_3.n_id_scrambling = t_bench.cell_cfg.pci; pucch_expected_f3.format_3.n_id_0_scrambling = t_bench.cell_cfg.pci; + pucch_expected_f3.format_3.pi_2_bpsk = false; + pucch_expected_f3.format_3.additional_dmrs = false; // The expected resource for CSI corresponds to the last resource in the resource list. pucch_expected_csi.format = pucch_format::FORMAT_3; @@ -84,9 +88,13 @@ class test_pucch_allocator_format_3 : public ::testing::Test, public pucch_alloc pucch_expected_csi.resources.symbols = ofdm_symbol_range{0, 4}; pucch_expected_csi.format_3.max_code_rate = max_pucch_code_rate::dot_25; + pucch_expected_csi.format_3.group_hopping = pucch_group_hopping::NEITHER; + pucch_expected_csi.format_3.slot_repetition = pucch_repetition_tx_slot::no_multi_slot; pucch_expected_csi.format_3.n_id_hopping = t_bench.cell_cfg.pci; pucch_expected_csi.format_3.n_id_scrambling = t_bench.cell_cfg.pci; pucch_expected_csi.format_3.n_id_0_scrambling = t_bench.cell_cfg.pci; + pucch_expected_csi.format_3.pi_2_bpsk = false; + pucch_expected_csi.format_3.additional_dmrs = false; }; protected: From 8a6fdf4754af70f767c269c342a4b770f8ca9737 Mon Sep 17 00:00:00 2001 From: JPichel Date: Fri, 10 Jan 2025 16:33:52 +0100 Subject: [PATCH 106/107] mac: improve pucch_alloc_format_3_test comments and modify test cases --- .../pucch_alloc_format_3_test.cpp | 21 ++++++++++++------- 1 file changed, 13 insertions(+), 8 deletions(-) diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp index eaa2d99dc9..6f4beb2c51 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_format_3_test.cpp @@ -23,6 +23,7 @@ class test_pucch_allocator_format_3 : public ::testing::Test, public pucch_alloc test_pucch_allocator_format_3() : pucch_allocator_base_tester(test_bench_params{.pucch_res_common = 0, .set1_format = pucch_format::FORMAT_3}) { + // Set the expected grants. The values are those generated by the `pucch_builder.add_build_new_ue_pucch_cfg`. // Set expected grant for PUCCH Format 1 SR. // The expected resource for SR corresponds to the second to last of the FORMAT 1 resources in the resource list. pucch_expected_f1_sr.format = pucch_format::FORMAT_1; @@ -57,11 +58,11 @@ class test_pucch_allocator_format_3 : public ::testing::Test, public pucch_alloc pucch_expected_f1_harq.format_1.harq_ack_nof_bits = 1; pucch_expected_f1_harq.format_1.time_domain_occ = 0; - // Set expected grant for PUCCH Format 1. pucch_expected_f1_harq.format_1.group_hopping = pucch_group_hopping::NEITHER; pucch_expected_f1_harq.format_1.n_id_hopping = t_bench.cell_cfg.pci; pucch_expected_f1_harq.format_1.slot_repetition = pucch_repetition_tx_slot::no_multi_slot; + // Set expected grant for PUCCH Format 3 HARQ. // The expected resource for HARQ corresponds to the first of the FORMAT 3 resources in the resource list. pucch_expected_f3.format = pucch_format::FORMAT_3; pucch_expected_f3.crnti = to_rnti(0x4601); @@ -79,6 +80,7 @@ class test_pucch_allocator_format_3 : public ::testing::Test, public pucch_alloc pucch_expected_f3.format_3.pi_2_bpsk = false; pucch_expected_f3.format_3.additional_dmrs = false; + // Set expected grant for PUCCH Format 3 CSI. // The expected resource for CSI corresponds to the last resource in the resource list. pucch_expected_csi.format = pucch_format::FORMAT_3; pucch_expected_csi.crnti = to_rnti(0x4601); @@ -389,19 +391,20 @@ TEST_F(test_pucch_allocator_format_3, test_harq_alloc_3bits_over_sr_and_csi) })); } -TEST_F(test_pucch_allocator_format_3, test_harq_alloc_4bits_over_sr_and_csi) +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_7bits_over_sr_and_csi) { + constexpr unsigned nof_harq_bits = 7; // With SR and with PUCCH Format 1 it is guaranteed that the resources will be multiplexed, as PUCCH Format 1 for SR // spans over the 14 symbols. - pucch_expected_f3.format_3.harq_ack_nof_bits = 4; + pucch_expected_f3.format_3.harq_ack_nof_bits = nof_harq_bits; pucch_expected_f3.format_3.sr_bits = sr_nof_bits::one; pucch_expected_f3.format_3.csi_part1_bits = 4; add_sr_grant(); add_csi_grant(); - add_harq_grant(); - add_harq_grant(); - add_harq_grant(); + for (int i = 0; i != nof_harq_bits - 1; ++i) { + add_harq_grant(); + } std::optional test_pucch_res_indicator = t_bench.pucch_alloc.alloc_ded_pucch_harq_ack_ue( t_bench.res_grid, t_bench.get_main_ue().crnti, t_bench.get_main_ue().get_pcell().cfg(), t_bench.k0, t_bench.k1); @@ -416,11 +419,13 @@ TEST_F(test_pucch_allocator_format_3, test_harq_alloc_4bits_over_sr_and_csi) ASSERT_TRUE(find_pucch_pdu(slot_grid.result.ul.pucchs, [](const auto& pdu) { return pdu.csi_rep_cfg.has_value(); })); } -TEST_F(test_pucch_allocator_format_3, test_harq_alloc_11bits_over_sr_and_csi_fails) +TEST_F(test_pucch_allocator_format_3, test_harq_alloc_8bits_over_sr_and_csi_fails) { + // 1 SR + 4 CSI + 8 HARQ = 13 UCI bits (> PUCCH capacity). + constexpr unsigned nof_harq_bits = 8; add_sr_grant(); add_csi_grant(); - for (int i = 0; i != 10; i++) { + for (int i = 0; i != nof_harq_bits - 1; ++i) { add_harq_grant(); } From 27b2e38c747121b4d4daca06035f64be10af2e83 Mon Sep 17 00:00:00 2001 From: JPichel Date: Fri, 10 Jan 2025 19:52:19 +0100 Subject: [PATCH 107/107] mac: add comment for NOF_RBS --- .../unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h index a88ccf0bb3..a0f8107c5b 100644 --- a/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h +++ b/tests/unittests/scheduler/uci_and_pucch/pucch_alloc_base_tester.h @@ -14,6 +14,7 @@ using namespace srsran; +// Number of RBs configured for the test cases. constexpr unsigned NOF_RBS = 52; class pucch_allocator_base_tester