diff --git a/lib/include/srsran/adt/circular_buffer.h b/lib/include/srsran/adt/circular_buffer.h index d92b865a4..67682b670 100644 --- a/lib/include/srsran/adt/circular_buffer.h +++ b/lib/include/srsran/adt/circular_buffer.h @@ -291,10 +291,13 @@ public: bool push_blocking(const T& t) { return push_(t, true); } srsran::error_type push_blocking(T&& t) { return push_(std::move(t), true); } bool try_pop(T& obj) { return pop_(obj, false); } - T pop_blocking() + T pop_blocking(bool* success = nullptr) { - T obj{}; - pop_(obj, true); + T obj{}; + bool ret = pop_(obj, true); + if (success != nullptr) { + *success = ret; + } return obj; } bool pop_wait_until(T& obj, const std::chrono::system_clock::time_point& until) { return pop_(obj, true, &until); } @@ -599,12 +602,6 @@ public: base_t(push_callback, pop_callback, size) {} void set_size(size_t size) { base_t::circ_buffer.set_size(size); } - - template - bool apply_first(const F& func) - { - return base_t::apply_first(func); - } }; } // namespace srsran diff --git a/lib/include/srsran/asn1/asn1_utils.h b/lib/include/srsran/asn1/asn1_utils.h index 266cb56cc..e30e55df8 100644 --- a/lib/include/srsran/asn1/asn1_utils.h +++ b/lib/include/srsran/asn1/asn1_utils.h @@ -608,7 +608,7 @@ public: IntType value; integer() = default; integer(IntType value_) : value(value_) {} - operator IntType() { return value; } + operator IntType() const { return value; } SRSASN_CODE pack(bit_ref& bref) const { return pack_integer(bref, value, lb, ub, has_ext, is_aligned); } SRSASN_CODE unpack(cbit_ref& bref) { return unpack_integer(value, bref, lb, ub, has_ext, is_aligned); } }; @@ -1320,7 +1320,7 @@ private: bit_ref brefstart; // bit_ref bref0; bit_ref* bref_tracker; - uint8_t buffer[2048]; + uint8_t buffer[4096]; bool align; }; @@ -1368,6 +1368,33 @@ private: separator_t sep; }; +template +inline auto to_json(json_writer& j, const T& obj) -> decltype(obj.to_json(j)) +{ + obj.to_json(j); +} + +template +inline void to_json(json_writer& j, const asn1::enumerated& obj) +{ + j.write_str(obj.to_string()); +} + +template +inline void to_json(json_writer& j, const asn1::dyn_array& lst) +{ + j.start_array(); + for (const auto& o : lst) { + to_json(j, o); + } + j.end_array(); +} + +inline void to_json(json_writer& j, int64_t number) +{ + j.write_int(number); +} + /******************* Test pack/unpack *******************/ diff --git a/lib/include/srsran/asn1/rrc_nr.h b/lib/include/srsran/asn1/rrc_nr.h index 69edb5400..b7088d9de 100644 --- a/lib/include/srsran/asn1/rrc_nr.h +++ b/lib/include/srsran/asn1/rrc_nr.h @@ -2263,7 +2263,7 @@ struct setup_release_c { // choice methods setup_release_c() = default; - types type() const { return type_; } + types type() const { return type_; } // getters elem_type_paramT_& setup() @@ -2276,7 +2276,7 @@ struct setup_release_c { assert_choice_type(types::setup, type_, "SetupRelease"); return c; } - void set_release() { set(types::release); } + void set_release() { set(types::release); } void set(typename types::options e) { type_ = e; } @@ -2285,8 +2285,11 @@ struct setup_release_c { j.start_obj(); switch (type_) { case types::release: + j.write_null("release"); break; case types::setup: + j.write_fieldname("setup"); + asn1::to_json(j, setup()); break; default: log_invalid_choice_id(type_, "setup_release_c"); diff --git a/lib/include/srsran/asn1/rrc_nr_utils.h b/lib/include/srsran/asn1/rrc_nr_utils.h index 6e444187b..6aa160849 100644 --- a/lib/include/srsran/asn1/rrc_nr_utils.h +++ b/lib/include/srsran/asn1/rrc_nr_utils.h @@ -66,6 +66,8 @@ struct pdsch_serving_cell_cfg_s; struct freq_info_dl_s; struct serving_cell_cfg_common_s; struct serving_cell_cfg_s; +struct pdcch_cfg_common_s; +struct pdcch_cfg_s; } // namespace rrc_nr } // namespace asn1 @@ -123,7 +125,12 @@ bool make_phy_carrier_cfg(const asn1::rrc_nr::freq_info_dl_s& freq_info_dl, srsr bool make_phy_ssb_cfg(const srsran_carrier_nr_t& carrier, const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg, phy_cfg_nr_t::ssb_cfg_t* ssb); -bool make_pdsch_cfg_from_serv_cell(asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_sch_hl_cfg_nr_t* sch_hl); +bool make_pdsch_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_sch_hl_cfg_nr_t* sch_hl); +bool make_csi_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_csi_hl_cfg_t* csi_hl); +bool make_duplex_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell, + srsran_duplex_config_nr_t* duplex_cfg); +bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch); +bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch); /*************************** * MAC Config diff --git a/lib/include/srsran/common/bearer_manager.h b/lib/include/srsran/common/bearer_manager.h index 1aba22f28..1861f8079 100644 --- a/lib/include/srsran/common/bearer_manager.h +++ b/lib/include/srsran/common/bearer_manager.h @@ -60,7 +60,7 @@ public: radio_bearer_t get_radio_bearer(uint32_t eps_bearer_id); - radio_bearer_t get_lcid_bearer(uint32_t lcid); + radio_bearer_t get_eps_bearer_id_for_lcid(uint32_t lcid); private: using eps_rb_map_t = std::map; @@ -128,7 +128,7 @@ public: radio_bearer_t get_lcid_bearer(uint32_t lcid) { srsran::rwlock_read_guard rw_lock(rwlock); - return impl.get_lcid_bearer(lcid); + return impl.get_eps_bearer_id_for_lcid(lcid); } private: diff --git a/lib/include/srsran/common/interfaces_common.h b/lib/include/srsran/common/interfaces_common.h index 1d0ec28d4..b7be668ad 100644 --- a/lib/include/srsran/common/interfaces_common.h +++ b/lib/include/srsran/common/interfaces_common.h @@ -29,20 +29,20 @@ namespace srsran { -typedef struct { +struct phy_log_args_t { std::string phy_level = "none"; std::string phy_lib_level = "none"; std::string id_preamble = ""; int phy_hex_limit = -1; -} phy_log_args_t; +}; -typedef struct { +struct rf_args_band_t { float min; float max; -} rf_args_band_t; +}; // RF/radio args -typedef struct { +struct rf_args_t { std::string type; std::string log_level; double srate_hz; @@ -65,8 +65,7 @@ typedef struct { std::array ch_rx_bands; std::array ch_tx_bands; - -} rf_args_t; +}; struct vnf_args_t { std::string type; diff --git a/lib/include/srsran/common/mac_pcap_base.h b/lib/include/srsran/common/mac_pcap_base.h index 7771fbfb7..b141009c3 100644 --- a/lib/include/srsran/common/mac_pcap_base.h +++ b/lib/include/srsran/common/mac_pcap_base.h @@ -37,6 +37,12 @@ class mac_pcap_base : protected srsran::thread { public: mac_pcap_base(); + + mac_pcap_base(const mac_pcap_base& other) = delete; + mac_pcap_base& operator=(const mac_pcap_base& other) = delete; + mac_pcap_base(mac_pcap_base&& other) = delete; + mac_pcap_base& operator=(mac_pcap_base&& other) = delete; + ~mac_pcap_base(); void enable(bool enable); virtual uint32_t close() = 0; @@ -45,7 +51,7 @@ public: // EUTRA void - write_ul_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, uint32_t reTX, uint32_t tti, uint8_t cc_idx); + write_ul_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, uint32_t reTX, uint32_t tti, uint8_t cc_idx); void write_dl_crnti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t crnti, bool crc_ok, uint32_t tti, uint8_t cc_idx); void write_dl_ranti(uint8_t* pdu, uint32_t pdu_len_bytes, uint16_t ranti, bool crc_ok, uint32_t tti, uint8_t cc_idx); diff --git a/lib/include/srsran/common/s1ap_pcap.h b/lib/include/srsran/common/s1ap_pcap.h index 78fee92da..016db2455 100644 --- a/lib/include/srsran/common/s1ap_pcap.h +++ b/lib/include/srsran/common/s1ap_pcap.h @@ -30,7 +30,12 @@ namespace srsran { class s1ap_pcap { public: - s1ap_pcap() = default; + s1ap_pcap(); + s1ap_pcap(const s1ap_pcap& other) = delete; + s1ap_pcap& operator=(const s1ap_pcap& other) = delete; + s1ap_pcap(s1ap_pcap&& other) = delete; + s1ap_pcap& operator=(s1ap_pcap&& other) = delete; + void enable(); void open(const char* filename_); void close(); diff --git a/lib/include/srsran/common/signal_handler.h b/lib/include/srsran/common/signal_handler.h deleted file mode 100644 index 0477186ec..000000000 --- a/lib/include/srsran/common/signal_handler.h +++ /dev/null @@ -1,83 +0,0 @@ -/** - * Copyright 2013-2021 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -/** - * @file signal_handler.h - * @brief Common signal handling methods for all srsRAN applications. - */ - -#ifndef SRSRAN_SIGNAL_HANDLER_H -#define SRSRAN_SIGNAL_HANDLER_H - -#include "srsran/srslog/sink.h" -#include "srsran/srslog/srslog.h" -#include -#include - -#ifdef __cplusplus -extern "C" { -#endif // __cplusplus - -#define SRSRAN_TERM_TIMEOUT_S (5) - -// static vars required by signal handling -static srslog::sink* log_sink = nullptr; -static std::atomic running = {true}; - -void srsran_dft_exit(); - -static void srsran_signal_handler(int signal) -{ - switch (signal) { - case SIGALRM: - fprintf(stderr, "Couldn't stop after %ds. Forcing exit.\n", SRSRAN_TERM_TIMEOUT_S); - srslog::flush(); - //:TODO: refactor the sighandler, should not depend on log utilities - if (log_sink) { - log_sink->flush(); - } - srsran_dft_exit(); - raise(SIGKILL); - default: - // all other registered signals try to stop the app gracefully - if (running) { - running = false; - fprintf(stdout, "Stopping ..\n"); - alarm(SRSRAN_TERM_TIMEOUT_S); - } else { - // already waiting for alarm to go off .. - } - break; - } -} - -void srsran_register_signal_handler() -{ - signal(SIGINT, srsran_signal_handler); - signal(SIGTERM, srsran_signal_handler); - signal(SIGHUP, srsran_signal_handler); - signal(SIGALRM, srsran_signal_handler); -} - -#ifdef __cplusplus -} -#endif // __cplusplus -#endif // SRSRAN_SIGNAL_HANDLER_H diff --git a/lib/include/srsran/common/thread_pool.h b/lib/include/srsran/common/thread_pool.h index f962160dd..65df9fc5a 100644 --- a/lib/include/srsran/common/thread_pool.h +++ b/lib/include/srsran/common/thread_pool.h @@ -153,6 +153,40 @@ private: bool running = false; }; +/// Class used to create a single worker with an input task queue with a single reader +class task_worker : public thread +{ + using task_t = srsran::move_callback; + +public: + task_worker(std::string thread_name_, + uint32_t queue_size, + bool start_deferred = false, + int32_t prio_ = -1, + uint32_t mask_ = 255); + task_worker(const task_worker&) = delete; + task_worker(task_worker&&) = delete; + task_worker& operator=(const task_worker&) = delete; + task_worker& operator=(task_worker&&) = delete; + ~task_worker(); + + void stop(); + void start(int32_t prio_ = -1, uint32_t mask_ = 255); + + void push_task(task_t&& task); + uint32_t nof_pending_tasks() const; + +private: + void run_thread() override; + + // args + int32_t prio = -1; + uint32_t mask = 255; + srslog::basic_logger& logger; + + srsran::dyn_blocking_queue pending_tasks; +}; + srsran::task_thread_pool& get_background_workers(); } // namespace srsran diff --git a/lib/include/srsran/common/tsan_options.h b/lib/include/srsran/common/tsan_options.h index f73ad6edc..b64e433f9 100644 --- a/lib/include/srsran/common/tsan_options.h +++ b/lib/include/srsran/common/tsan_options.h @@ -50,8 +50,16 @@ const char* __tsan_default_suppressions() // External uninstrumented libraries "called_from_lib:libzmq.so\n" "called_from_lib:libpgm-5.2.so\n" - // Lock order inversion issue in this function, ignore it as it uses rw locks in read mode - "deadlock:srsenb::mac::rlc_buffer_state\n"; + "called_from_lib:libusb*\n" + "called_from_lib:libuhd*\n" + // Races detected inside uninstrumented libraries. This may hide legit races if any of the libraries appear in the + // backtrace + "race:libusb*\n" + "race:libuhd*\n" + // Lock order inversion issues in these functions, ignore it as it uses rw locks in read mode + "deadlock:srsenb::mac::rlc_buffer_state\n" + "deadlock:srsenb::mac::snr_info\n" + "deadlock:srsenb::mac::ack_info\n"; } #ifdef __cplusplus diff --git a/lib/include/srsran/interfaces/enb_mac_interfaces.h b/lib/include/srsran/interfaces/enb_mac_interfaces.h index 1ea9f6948..773f86a53 100644 --- a/lib/include/srsran/interfaces/enb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/enb_mac_interfaces.h @@ -31,7 +31,6 @@ struct mac_args_t { uint32_t nof_prb; ///< Needed to dimension MAC softbuffers for all cells sched_interface::sched_args_t sched; int lcid_padding; - int nr_tb_size = -1; uint32_t nof_prealloc_ues; ///< Number of UE resources to pre-allocate at eNB startup uint32_t max_nof_kos; int rlf_min_ul_snr_estim; diff --git a/lib/include/srsran/interfaces/enb_x2_interfaces.h b/lib/include/srsran/interfaces/enb_x2_interfaces.h index b2b7c4330..d3396cbef 100644 --- a/lib/include/srsran/interfaces/enb_x2_interfaces.h +++ b/lib/include/srsran/interfaces/enb_x2_interfaces.h @@ -45,10 +45,13 @@ public: }; /// Request addition of NR carrier for UE - virtual int sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) = 0; + virtual void sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) = 0; /// Provide information whether the requested configuration was applied successfully by the UE - virtual int sgnb_reconfiguration_complete(uint16_t eutra_rnti, asn1::dyn_octstring reconfig_response) = 0; + virtual void sgnb_reconfiguration_complete(uint16_t eutra_rnti, const asn1::dyn_octstring& reconfig_response) = 0; + + /// Trigger release for specific UE + virtual void sgnb_release_request(uint16_t nr_rnti) = 0; }; /// X2AP inspired interface for response from NR RRC to EUTRA RRC @@ -91,6 +94,13 @@ public: */ virtual void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) = 0; + /** + * @brief Signal release of all UE resources on the NR cell + * + * @param eutra_rnti The RNTI that the EUTRA RRC used to request the SgNB addition + */ + virtual void sgnb_release_ack(uint16_t eutra_rnti) = 0; + /** * @brief Signal user activity (i.e. DL/UL traffic) for given RNTI * @@ -104,7 +114,10 @@ class x2_interface : public rrc_nr_interface_rrc, public rrc_eutra_interface_rrc_nr, public pdcp_interface_gtpu, // allow GTPU to access PDCP in DL direction public gtpu_interface_pdcp // allow PDCP to access GTPU in UL direction -{}; +{ +public: + virtual ~x2_interface() = default; +}; } // namespace srsenb diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index 945789ea8..15d24a976 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -36,6 +36,8 @@ namespace srsenb { +struct sched_nr_ue_cfg_t; + /***************************** * RLC INTERFACES ****************************/ @@ -143,9 +145,9 @@ public: virtual int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) = 0; /// User management - virtual int add_user(uint16_t rnti) = 0; - virtual int update_user(uint16_t new_rnti, uint16_t old_rnti) = 0; - virtual void set_activity_user(uint16_t rnti) = 0; + virtual int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg) = 0; + virtual int update_user(uint16_t new_rnti, uint16_t old_rnti) = 0; + virtual void set_activity_user(uint16_t rnti) = 0; }; // NR interface is almost identical to EUTRA version @@ -277,7 +279,7 @@ public: srsran::unique_byte_buffer_t pdu = nullptr; // PUSCH signal measurements - // ... + srsran_csi_trs_measurements_t csi; ///< DMRS based signal Channel State Information (CSI) }; struct rach_info_t { diff --git a/lib/include/srsran/interfaces/gnb_mac_interfaces.h b/lib/include/srsran/interfaces/gnb_mac_interfaces.h index 39aea1c9c..9076346c4 100644 --- a/lib/include/srsran/interfaces/gnb_mac_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_mac_interfaces.h @@ -33,7 +33,7 @@ public: virtual int cell_cfg(const std::vector& nr_cells) = 0; /// Allocates a new user/RNTI at MAC. Returns RNTI on success or SRSRAN_INVALID_RNTI otherwise. - virtual uint16_t reserve_rnti(uint32_t enb_cc_idx) = 0; + virtual uint16_t reserve_rnti(uint32_t enb_cc_idx, const sched_nr_interface::ue_cfg_t& uecfg) = 0; virtual int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) = 0; diff --git a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h index 1ee9d85f7..b0262e3db 100644 --- a/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_rrc_nr_interfaces.h @@ -38,6 +38,7 @@ public: virtual int establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid) = 0; virtual int allocate_lcid(uint16_t rnti) = 0; + virtual int release_bearers(uint16_t rnti) = 0; virtual void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) = 0; }; diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index b7eba05ec..4ec9b7ddd 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -155,6 +155,7 @@ public: class mac_interface_rrc_nr { public: + virtual void reset() = 0; // Config calls that return SRSRAN_SUCCESS or SRSRAN_ERROR virtual int setup_lcid(const srsran::logical_channel_config_t& config) = 0; virtual int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) = 0; diff --git a/lib/include/srsran/interfaces/ue_rrc_interfaces.h b/lib/include/srsran/interfaces/ue_rrc_interfaces.h index 87d5f23c5..80ce3a8de 100644 --- a/lib/include/srsran/interfaces/ue_rrc_interfaces.h +++ b/lib/include/srsran/interfaces/ue_rrc_interfaces.h @@ -119,6 +119,7 @@ public: uint32_t sk_counter_r15, bool nr_radio_bearer_cfg1_r15_present, asn1::dyn_octstring nr_radio_bearer_cfg1_r15) = 0; + virtual void rrc_release() = 0; virtual bool is_config_pending() = 0; }; diff --git a/lib/include/srsran/mac/mac_sch_pdu_nr.h b/lib/include/srsran/mac/mac_sch_pdu_nr.h index d63bfb4c4..f9348ca7a 100644 --- a/lib/include/srsran/mac/mac_sch_pdu_nr.h +++ b/lib/include/srsran/mac/mac_sch_pdu_nr.h @@ -68,7 +68,7 @@ public: nr_lcid_sch_t get_type(); bool is_sdu(); bool is_valid_lcid(); - bool is_var_len_ce(); + bool is_var_len_ce(uint32_t lcid); bool is_ul_ccch(); int32_t read_subheader(const uint8_t* ptr); @@ -87,9 +87,13 @@ public: uint8_t lcg_id; uint8_t buffer_size; }; - lcg_bsr_t get_sbsr(); - static const uint8_t max_num_lcg_lbsr = 8; - std::array get_lbsr(); + lcg_bsr_t get_sbsr(); + static const uint8_t max_num_lcg_lbsr = 8; + struct lbsr_t { + uint8_t bitmap; // the first octet of LBSR and Long Trunc BSR + std::vector list; // one entry for each reported LCG + }; + lbsr_t get_lbsr(); // TA struct ta_t { @@ -116,6 +120,9 @@ public: private: srslog::basic_logger* logger; + // internal helpers + bool has_length_field(); + uint32_t lcid = 0; int header_length = 0; int sdu_length = 0; @@ -207,8 +214,9 @@ public: void to_string(fmt::memory_buffer& buffer); -private: uint32_t size_header_sdu(const uint32_t lcid_, const uint32_t nbytes); + +private: /// Private helper that adds a subPDU to the MAC PDU uint32_t add_sudpdu(mac_sch_subpdu_nr& subpdu); diff --git a/lib/include/srsran/phy/ch_estimation/dmrs_sch.h b/lib/include/srsran/phy/ch_estimation/dmrs_sch.h index ef797d773..31f6c81ad 100644 --- a/lib/include/srsran/phy/ch_estimation/dmrs_sch.h +++ b/lib/include/srsran/phy/ch_estimation/dmrs_sch.h @@ -59,6 +59,8 @@ typedef struct { cf_t* temp; /// Temporal data vector of size SRSRAN_NRE * carrier.nof_prb float* filter; ///< Smoothing filter + + srsran_csi_trs_measurements_t csi; ///< Last estimated channel state information } srsran_dmrs_sch_t; /** diff --git a/lib/include/srsran/phy/channel/channel.h b/lib/include/srsran/phy/channel/channel.h index a58b206f9..75d61fcbb 100644 --- a/lib/include/srsran/phy/channel/channel.h +++ b/lib/include/srsran/phy/channel/channel.h @@ -37,7 +37,7 @@ namespace srsran { class channel { public: - typedef struct { + struct args_t { // General bool enable = false; @@ -67,7 +67,7 @@ public: bool rlf_enable = false; uint32_t rlf_t_on_ms = 10000; uint32_t rlf_t_off_ms = 2000; - } args_t; + }; channel(const args_t& channel_args, uint32_t _nof_channels, srslog::basic_logger& logger); ~channel(); diff --git a/lib/include/srsran/phy/common/phy_common_nr.h b/lib/include/srsran/phy/common/phy_common_nr.h index f6e5dda36..683ba2c81 100644 --- a/lib/include/srsran/phy/common/phy_common_nr.h +++ b/lib/include/srsran/phy/common/phy_common_nr.h @@ -654,7 +654,7 @@ SRSRAN_API bool srsran_duplex_nr_is_ul(const srsran_duplex_config_nr_t* cfg, uin SRSRAN_API int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran_cell_t* cell); /** - * @brief Writes Channel State Information measurement into a string + * @brief Writes detailed Channel State Information measurement into a string * @param meas Provides the measurement * @param str Provides string * @param str_len Maximum string length @@ -662,6 +662,15 @@ SRSRAN_API int srsran_carrier_to_cell(const srsran_carrier_nr_t* carrier, srsran */ SRSRAN_API uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len); +/** + * @brief Writes short Channel State Information measurement into a string + * @param meas Provides the measurement + * @param str Provides string + * @param str_len Maximum string length + * @return The number of writen characters + */ +SRSRAN_API uint32_t srsran_csi_meas_info_short(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len); + /** * @brief Converts a given string into a subcarrier spacing * @param str Provides the string @@ -704,6 +713,20 @@ SRSRAN_API int srsran_coreset_zero(uint32_t n_cell_id, uint32_t idx, srsran_coreset_t* coreset); +/** + * @brief Convert SSB pattern to string + * @param pattern + * @return a string describing the SSB pattern + */ +SRSRAN_API const char* srsran_ssb_pattern_to_str(srsran_ssb_patern_t pattern); + +/** + * @brief Convert string to SSB pattern + * @param str String to convert + * @return The pattern, SRSRAN_SSB_PATTERN_INVALID if string is invalid + */ +SRSRAN_API srsran_ssb_patern_t srsran_ssb_pattern_fom_str(const char* str); + #ifdef __cplusplus } #endif diff --git a/lib/include/srsran/phy/gnb/gnb_ul.h b/lib/include/srsran/phy/gnb/gnb_ul.h index 0fb188060..ef54c25b0 100644 --- a/lib/include/srsran/phy/gnb/gnb_ul.h +++ b/lib/include/srsran/phy/gnb/gnb_ul.h @@ -30,6 +30,7 @@ typedef struct SRSRAN_API { srsran_pusch_nr_args_t pusch; srsran_pucch_nr_args_t pucch; + float pusch_min_snr_dB; ///< Minimum SNR threshold to decode PUSCH, set to 0 for default value uint32_t nof_max_prb; } srsran_gnb_ul_args_t; @@ -45,6 +46,7 @@ typedef struct SRSRAN_API { srsran_dmrs_sch_t dmrs; srsran_chest_dl_res_t chest_pusch; srsran_chest_ul_res_t chest_pucch; + float pusch_min_snr_dB; ///< Minimum measured DMRS SNR, below this threshold PUSCH is not decoded } srsran_gnb_ul_t; SRSRAN_API int srsran_gnb_ul_init(srsran_gnb_ul_t* q, cf_t* input, const srsran_gnb_ul_args_t* args); @@ -69,11 +71,12 @@ SRSRAN_API int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q, srsran_uci_value_nr_t* uci_value, srsran_csi_trs_measurements_t* meas); -SRSRAN_API uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, - const srsran_pucch_nr_resource_t* resource, - const srsran_uci_data_nr_t* uci_data, - char* str, - uint32_t str_len); +SRSRAN_API uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, + const srsran_pucch_nr_resource_t* resource, + const srsran_uci_data_nr_t* uci_data, + const srsran_csi_trs_measurements_t* csi, + char* str, + uint32_t str_len); SRSRAN_API uint32_t srsran_gnb_ul_pusch_info(srsran_gnb_ul_t* q, const srsran_sch_cfg_nr_t* cfg, diff --git a/lib/include/srsran/phy/phch/csi.h b/lib/include/srsran/phy/phch/csi.h index 6492a356f..c0b42f8e1 100644 --- a/lib/include/srsran/phy/phch/csi.h +++ b/lib/include/srsran/phy/phch/csi.h @@ -40,18 +40,26 @@ srsran_csi_new_nzp_csi_rs_measurement(const srsran_csi_hl_resource_cfg_t csi_res uint32_t nzp_csi_rs_id); /** - * @brief Generates CSI report configuration and values from the higher layer configuration and a list of measurements + * @brief Generates CSI report configuration from the higher layer configuration for a given slot * @param cfg Higher layer report configuration - * @param slot_idx Slot index within the radio frame - * @param measurements Filtered CSI measurements - * @param[out] report_cfg Report configuration re + * @param slot_cfg Current slot configuration + * @param[out] report_cfg Report configuration for the given slot + * @return The number CSI reports for transmission if the provided data is valid, SRSRAN_ERROR code otherwise + */ +SRSRAN_API int srsran_csi_reports_generate(const srsran_csi_hl_cfg_t* cfg, + const srsran_slot_cfg_t* slot_cfg, + srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT]); + +/** + * @brief Quantifies a given set of CSI reports from the given set of measurements + * @param reports Set of report configuration + * @param measurements Set of measurements to quantify + * @param report_value Set of report values * @return The number CSI reports for transmission if the provided data is valid, SRSRAN_ERROR code otherwise */ SRSRAN_API int -srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, - uint32_t slot_idx, +srsran_csi_reports_quantify(const srsran_csi_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT], const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], - srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]); /** diff --git a/lib/include/srsran/phy/phch/csi_cfg.h b/lib/include/srsran/phy/phch/csi_cfg.h index b1a4a51df..3901c1548 100644 --- a/lib/include/srsran/phy/phch/csi_cfg.h +++ b/lib/include/srsran/phy/phch/csi_cfg.h @@ -171,12 +171,10 @@ typedef struct SRSRAN_API { /** * @brief CSI report configuration + * @note An unset report is marked with `cfg.type = SRSRAN_CSI_REPORT_TYPE_NONE` */ typedef struct SRSRAN_API { - srsran_csi_report_type_t type; ///< CSI report type (none, periodic, semiPersistentOnPUCCH, ...) - srsran_csi_report_quantity_t quantity; ///< Report quantity - srsran_pucch_nr_resource_t pucch_resource; ///< PUCCH resource to use for periodic reporting - srsran_csi_report_freq_t freq_cfg; ///< Determine whether it is wideband or subband + srsran_csi_hl_report_cfg_t cfg; ///< Higher layer CSI report configuration // Resource set context uint32_t nof_ports; ///< Number of antenna ports @@ -202,7 +200,6 @@ typedef struct SRSRAN_API { void* none; srsran_csi_report_wideband_cri_ri_pmi_cqi_t wideband_cri_ri_pmi_cqi; }; - bool valid; ///< Used by receiver only } srsran_csi_report_value_t; /** diff --git a/lib/include/srsran/phy/phch/sch_cfg_nr.h b/lib/include/srsran/phy/phch/sch_cfg_nr.h index b0761b163..56c582437 100644 --- a/lib/include/srsran/phy/phch/sch_cfg_nr.h +++ b/lib/include/srsran/phy/phch/sch_cfg_nr.h @@ -28,8 +28,11 @@ typedef struct SRSRAN_API { srsran_mcs_table_t mcs_table; ///< @brief Indicates the MCS table the UE shall use for PDSCH and/or PUSCH without ///< transform precoding - srsran_xoverhead_t xoverhead; ///< Accounts for overhead from CSI-RS, CORESET, etc. If the field is absent, the UE - ///< applies value xOh0 (see TS 38.214 [19], clause 5.1.3.2). + srsran_xoverhead_t xoverhead; ///< @brief Accounts for overhead from CSI-RS, CORESET, etc. If the field is absent, the + ///< UE applies value xOh0 (see TS 38.214 [19], clause 5.1.3.2). + + bool limited_buffer_rm; ///< @brief Enables LBRM (Limited buffer rate-matching). Given by rateMatching parameter in + ///< PUSCH-ServingCellConfig or PDSCH-ServingCellConfig ASN1 sequences } srsran_sch_cfg_t; typedef struct SRSRAN_API { diff --git a/lib/include/srsran/phy/ue/ue_dl_nr.h b/lib/include/srsran/phy/ue/ue_dl_nr.h index 24fac034a..a4dead029 100644 --- a/lib/include/srsran/phy/ue/ue_dl_nr.h +++ b/lib/include/srsran/phy/ue/ue_dl_nr.h @@ -121,11 +121,11 @@ SRSRAN_API int srsran_ue_dl_nr_decode_pdsch(srsran_ue_dl_nr_t* q, const srsran_sch_cfg_nr_t* cfg, srsran_pdsch_res_nr_t* res); -SRSRAN_API int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, - const srsran_sch_cfg_nr_t* cfg, - const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS], - char* str, - uint32_t str_len); +SRSRAN_API uint32_t srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, + const srsran_sch_cfg_nr_t* cfg, + const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS], + char* str, + uint32_t str_len); SRSRAN_API int srsran_ue_dl_nr_csi_measure_trs(const srsran_ue_dl_nr_t* q, diff --git a/lib/include/srsran/radio/radio.h b/lib/include/srsran/radio/radio.h index befb33c1d..effce1f4c 100644 --- a/lib/include/srsran/radio/radio.h +++ b/lib/include/srsran/radio/radio.h @@ -100,9 +100,9 @@ private: std::vector rx_offset_n = {}; rf_metrics_t rf_metrics = {}; std::mutex metrics_mutex; - srslog::basic_logger& logger = srslog::fetch_basic_logger("RF", false); - phy_interface_radio* phy = nullptr; - cf_t* zeros = nullptr; + srslog::basic_logger& logger = srslog::fetch_basic_logger("RF", false); + phy_interface_radio* phy = nullptr; + cf_t* zeros = nullptr; std::array dummy_buffers; std::mutex tx_mutex; std::mutex rx_mutex; @@ -112,23 +112,23 @@ private: std::array decimators = {}; bool decimator_busy = false; ///< Indicates the decimator is changing the rate - rf_timestamp_t end_of_burst_time = {}; - bool is_start_of_burst = false; - uint32_t tx_adv_nsamples = 0; - double tx_adv_sec = 0.0; // Transmission time advance to compensate for antenna->timestamp delay - bool tx_adv_auto = false; - bool tx_adv_negative = false; - bool is_initialized = false; - bool radio_is_streaming = false; - bool continuous_tx = false; - double freq_offset = 0.0; - double cur_tx_srate = 0.0; - double cur_rx_srate = 0.0; - double fix_srate_hz = 0.0; - uint32_t nof_antennas = 0; - uint32_t nof_channels = 0; - uint32_t nof_channels_x_dev = 0; - uint32_t nof_carriers = 0; + rf_timestamp_t end_of_burst_time = {}; + std::atomic is_start_of_burst{false}; + uint32_t tx_adv_nsamples = 0; + double tx_adv_sec = 0.0; // Transmission time advance to compensate for antenna->timestamp delay + bool tx_adv_auto = false; + bool tx_adv_negative = false; + bool is_initialized = false; + bool radio_is_streaming = false; + bool continuous_tx = false; + double freq_offset = 0.0; + double cur_tx_srate = 0.0; + double cur_rx_srate = 0.0; + double fix_srate_hz = 0.0; + uint32_t nof_antennas = 0; + uint32_t nof_channels = 0; + uint32_t nof_channels_x_dev = 0; + uint32_t nof_carriers = 0; std::vector cur_tx_freqs = {}; std::vector cur_rx_freqs = {}; diff --git a/lib/include/srsran/rrc/rrc_common.h b/lib/include/srsran/rrc/rrc_common.h index 1bbeff415..d406636c5 100644 --- a/lib/include/srsran/rrc/rrc_common.h +++ b/lib/include/srsran/rrc/rrc_common.h @@ -22,10 +22,10 @@ #ifndef SRSUE_RRC_COMMON_H #define SRSUE_RRC_COMMON_H -namespace srsran { - #include +namespace srsran { + enum quant_s { quant_rsrp, quant_rsrq }; uint8_t rrc_value_to_range(quant_s quant, const float value); diff --git a/lib/include/srsran/support/emergency_handlers.h b/lib/include/srsran/support/emergency_handlers.h new file mode 100644 index 000000000..818f1a211 --- /dev/null +++ b/lib/include/srsran/support/emergency_handlers.h @@ -0,0 +1,25 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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. + * + */ + +#ifndef SRSRAN_EMERGENCY_HANDLERS_H +#define SRSRAN_EMERGENCY_HANDLERS_H + +using emergency_cleanup_callback = void (*)(void*); + +// Add a cleanup function to be called when a kill signal is about to be delivered to the process. The handler may +// optionally pass a pointer to identify what instance of the handler is being called. +void add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data); + +// Executes all registered emergency cleanup handlers. +void execute_emergency_cleanup_handlers(); + +#endif // SRSRAN_EMERGENCY_HANDLERS_H diff --git a/lib/include/srsran/support/signal_handler.h b/lib/include/srsran/support/signal_handler.h new file mode 100644 index 000000000..e21440bf9 --- /dev/null +++ b/lib/include/srsran/support/signal_handler.h @@ -0,0 +1,27 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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 signal_handler.h + * @brief Common signal handling methods for all srsRAN applications. + */ + +#ifndef SRSRAN_SIGNAL_HANDLER_H +#define SRSRAN_SIGNAL_HANDLER_H + +using srsran_signal_hanlder = void (*)(); + +/// Registers the specified function to be called when the user interrupts the program execution (eg: via Ctrl+C). +/// Passing a null function pointer disables the current installed handler. +void srsran_register_signal_handler(srsran_signal_hanlder handler); + +#endif // SRSRAN_SIGNAL_HANDLER_H diff --git a/lib/src/CMakeLists.txt b/lib/src/CMakeLists.txt index 463186157..aa9940faf 100644 --- a/lib/src/CMakeLists.txt +++ b/lib/src/CMakeLists.txt @@ -27,4 +27,5 @@ add_subdirectory(rlc) add_subdirectory(pdcp) add_subdirectory(gtpu) add_subdirectory(srslog) +add_subdirectory(support) add_subdirectory(system) diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index 0b5f8a53f..9b89880a8 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -387,15 +387,20 @@ bool make_phy_search_space_cfg(const search_space_s& search_space, srsran_search } srsran_search_space.coreset_id = search_space.ctrl_res_set_id; + srsran_search_space.duration = 1; + if (search_space.dur_present) { + srsran_search_space.duration = search_space.dur; + } + if (not search_space.nrof_candidates_present) { asn1::log_warning("nrof_candidates_present option not present"); return false; } - srsran_search_space.nof_candidates[0] = search_space.nrof_candidates.aggregation_level1.value; - srsran_search_space.nof_candidates[1] = search_space.nrof_candidates.aggregation_level2.value; - srsran_search_space.nof_candidates[2] = search_space.nrof_candidates.aggregation_level4.value; - srsran_search_space.nof_candidates[3] = search_space.nrof_candidates.aggregation_level8.value; - srsran_search_space.nof_candidates[4] = search_space.nrof_candidates.aggregation_level16.value; + srsran_search_space.nof_candidates[0] = search_space.nrof_candidates.aggregation_level1.to_number(); + srsran_search_space.nof_candidates[1] = search_space.nrof_candidates.aggregation_level2.to_number(); + srsran_search_space.nof_candidates[2] = search_space.nrof_candidates.aggregation_level4.to_number(); + srsran_search_space.nof_candidates[3] = search_space.nrof_candidates.aggregation_level8.to_number(); + srsran_search_space.nof_candidates[4] = search_space.nrof_candidates.aggregation_level16.to_number(); if (not search_space.search_space_type_present) { asn1::log_warning("nrof_candidates option not present"); @@ -1484,19 +1489,22 @@ bool make_phy_ssb_cfg(const srsran_carrier_nr_t& carrier, return true; } -bool make_pdsch_cfg_from_serv_cell(asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_sch_hl_cfg_nr_t* sch_hl) +bool make_pdsch_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_sch_hl_cfg_nr_t* sch_hl) { if (serv_cell.csi_meas_cfg_present and serv_cell.csi_meas_cfg.type().value == setup_release_c< ::asn1::rrc_nr::csi_meas_cfg_s>::types_opts::options::setup) { auto& setup = serv_cell.csi_meas_cfg.setup(); + + // Configure NZP-CSI if (setup.nzp_csi_rs_res_set_to_add_mod_list_present) { for (auto& nzp_set : setup.nzp_csi_rs_res_set_to_add_mod_list) { auto& uecfg_set = sch_hl->nzp_csi_rs_sets[nzp_set.nzp_csi_res_set_id]; uecfg_set.trs_info = nzp_set.trs_info_present; uecfg_set.count = nzp_set.nzp_csi_rs_res.size(); + uint32_t count = 0; for (uint8_t nzp_rs_idx : nzp_set.nzp_csi_rs_res) { - auto& res = uecfg_set.data[nzp_rs_idx]; + auto& res = uecfg_set.data[count++]; if (not srsran::make_phy_nzp_csi_rs_resource(setup.nzp_csi_rs_res_to_add_mod_list[nzp_rs_idx], &res)) { return false; } @@ -1524,6 +1532,77 @@ bool make_pdsch_cfg_from_serv_cell(asn1::rrc_nr::serving_cell_cfg_s& serv_cell, return true; } +bool make_csi_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_csi_hl_cfg_t* csi_hl) +{ + if (serv_cell.csi_meas_cfg_present and + serv_cell.csi_meas_cfg.type().value == + setup_release_c< ::asn1::rrc_nr::csi_meas_cfg_s>::types_opts::options::setup) { + auto& setup = serv_cell.csi_meas_cfg.setup(); + + // Configure CSI-Report + if (setup.csi_report_cfg_to_add_mod_list_present) { + for (uint32_t i = 0; i < setup.csi_report_cfg_to_add_mod_list.size(); ++i) { + const auto& csi_rep = setup.csi_report_cfg_to_add_mod_list[i]; + if (not make_phy_csi_report(csi_rep, &csi_hl->reports[i])) { + return false; + } + } + } + } + + return true; +} + +bool make_duplex_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell, + srsran_duplex_config_nr_t* duplex_cfg) +{ + duplex_cfg->mode = serv_cell.tdd_ul_dl_cfg_common_present ? SRSRAN_DUPLEX_MODE_TDD : SRSRAN_DUPLEX_MODE_FDD; + if (serv_cell.tdd_ul_dl_cfg_common_present) { + if (not make_phy_tdd_cfg(serv_cell.tdd_ul_dl_cfg_common, duplex_cfg)) { + return false; + } + } + return true; +} + +bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch) +{ + if (pdcch_cfg.ctrl_res_set_to_add_mod_list_present) { + for (const ctrl_res_set_s& coreset : pdcch_cfg.ctrl_res_set_to_add_mod_list) { + pdcch->coreset_present[coreset.ctrl_res_set_id] = true; + make_phy_coreset_cfg(coreset, &pdcch->coreset[coreset.ctrl_res_set_id]); + } + } + + if (pdcch_cfg.search_spaces_to_add_mod_list_present) { + for (const search_space_s& ss : pdcch_cfg.search_spaces_to_add_mod_list) { + pdcch->search_space_present[ss.search_space_id] = true; + make_phy_search_space_cfg(ss, &pdcch->search_space[ss.search_space_id]); + } + } + return true; +} + +bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch) +{ + if (pdcch_cfg.common_ctrl_res_set_present) { + pdcch->coreset_present[pdcch_cfg.common_ctrl_res_set.ctrl_res_set_id] = true; + make_phy_coreset_cfg(pdcch_cfg.common_ctrl_res_set, &pdcch->coreset[pdcch_cfg.common_ctrl_res_set.ctrl_res_set_id]); + } + if (pdcch_cfg.common_search_space_list_present) { + for (const search_space_s& ss : pdcch_cfg.common_search_space_list) { + pdcch->search_space_present[ss.search_space_id] = true; + make_phy_search_space_cfg(ss, &pdcch->search_space[ss.search_space_id]); + if (pdcch_cfg.ra_search_space_present and pdcch_cfg.ra_search_space == ss.search_space_id) { + pdcch->ra_search_space_present = true; + pdcch->ra_search_space = pdcch->search_space[ss.search_space_id]; + } + } + } + + return true; +} + } // namespace srsran namespace srsenb { diff --git a/lib/src/common/CMakeLists.txt b/lib/src/common/CMakeLists.txt index 866d8f4ae..e6c0b1282 100644 --- a/lib/src/common/CMakeLists.txt +++ b/lib/src/common/CMakeLists.txt @@ -59,7 +59,7 @@ add_dependencies(srsran_common gen_build_info) add_executable(arch_select arch_select.cc) target_include_directories(srsran_common PUBLIC ${SEC_INCLUDE_DIRS} ${CMAKE_SOURCE_DIR} ${BACKWARD_INCLUDE_DIRS}) -target_link_libraries(srsran_common srsran_phy srslog ${SEC_LIBRARIES} ${BACKWARD_LIBRARIES} ${SCTP_LIBRARIES}) +target_link_libraries(srsran_common srsran_phy support srslog ${SEC_LIBRARIES} ${BACKWARD_LIBRARIES} ${SCTP_LIBRARIES}) target_compile_definitions(srsran_common PRIVATE ${BACKWARD_DEFINITIONS}) INSTALL(TARGETS srsran_common DESTINATION ${LIBRARY_DIR}) diff --git a/lib/src/common/bearer_manager.cc b/lib/src/common/bearer_manager.cc index 268845bfc..8949346df 100644 --- a/lib/src/common/bearer_manager.cc +++ b/lib/src/common/bearer_manager.cc @@ -67,7 +67,7 @@ ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_radio_bearer( return it != bearers.end() ? it->second : invalid_rb; } -ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_lcid_bearer(uint32_t lcid) +ue_bearer_manager_impl::radio_bearer_t ue_bearer_manager_impl::get_eps_bearer_id_for_lcid(uint32_t lcid) { auto lcid_it = lcid_to_eps_bearer_id.find(lcid); return lcid_it != lcid_to_eps_bearer_id.end() ? bearers.at(lcid_it->second) : invalid_rb; @@ -188,7 +188,7 @@ enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_lcid_bearer(uint16_t if (user_it == users_map.end()) { return srsran::detail::ue_bearer_manager_impl::invalid_rb; } - return user_it->second.get_lcid_bearer(lcid); + return user_it->second.get_eps_bearer_id_for_lcid(lcid); } enb_bearer_manager::radio_bearer_t enb_bearer_manager::get_radio_bearer(uint16_t rnti, uint32_t eps_bearer_id) diff --git a/lib/src/common/mac_pcap_base.cc b/lib/src/common/mac_pcap_base.cc index 744716290..8c1e49a2e 100644 --- a/lib/src/common/mac_pcap_base.cc +++ b/lib/src/common/mac_pcap_base.cc @@ -22,11 +22,21 @@ #include "srsran/common/mac_pcap_base.h" #include "srsran/config.h" #include "srsran/phy/common/phy_common.h" +#include "srsran/support/emergency_handlers.h" #include namespace srsran { -mac_pcap_base::mac_pcap_base() : logger(srslog::fetch_basic_logger("MAC")), thread("PCAP_WRITER_MAC") {} +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} + +mac_pcap_base::mac_pcap_base() : logger(srslog::fetch_basic_logger("MAC")), thread("PCAP_WRITER_MAC") +{ + add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} mac_pcap_base::~mac_pcap_base() {} @@ -54,7 +64,7 @@ void mac_pcap_base::run_thread() } // write remainder of queue - pcap_pdu_t pdu = {}; + pcap_pdu_t pdu = {}; while (queue.try_pop(pdu)) { std::lock_guard lock(mutex); write_pdu(pdu); diff --git a/lib/src/common/phy_cfg_nr.cc b/lib/src/common/phy_cfg_nr.cc index c9b913ee8..df0a61e45 100644 --- a/lib/src/common/phy_cfg_nr.cc +++ b/lib/src/common/phy_cfg_nr.cc @@ -316,7 +316,10 @@ bool phy_cfg_nr_t::get_uci_cfg(const srsran_slot_cfg_t& slot_cfg, } // Generate configuration for CSI reports - // ... + n = srsran_csi_reports_generate(&csi, &slot_cfg, uci_cfg.csi); + if (n > SRSRAN_SUCCESS) { + uci_cfg.nof_csi = (uint32_t)n; + } return true; } diff --git a/lib/src/common/phy_cfg_nr_default.cc b/lib/src/common/phy_cfg_nr_default.cc index eff6c892f..cde1d01e9 100644 --- a/lib/src/common/phy_cfg_nr_default.cc +++ b/lib/src/common/phy_cfg_nr_default.cc @@ -393,10 +393,13 @@ void phy_cfg_nr_default_t::make_harq_auto(srsran_harq_ack_cfg_hl_t& harq, void phy_cfg_nr_default_t::make_prach_default_lte(srsran_prach_cfg_t& prach) { - prach.config_idx = 0; - prach.freq_offset = 4; - prach.root_seq_idx = 0; - prach.is_nr = true; + prach.is_nr = true; + prach.config_idx = 0; + prach.root_seq_idx = 0; + prach.zero_corr_zone = 0; + prach.freq_offset = 4; + prach.num_ra_preambles = 64; + prach.hs_flag = false; } phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) @@ -428,9 +431,11 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) if (duplex.mode == SRSRAN_DUPLEX_MODE_TDD) { carrier.dl_center_frequency_hz = 3513.6e6; + carrier.ul_center_frequency_hz = 3513.6e6; ssb.scs = srsran_subcarrier_spacing_30kHz; } else { carrier.dl_center_frequency_hz = 881.5e6; + carrier.ul_center_frequency_hz = 836.6e6; ssb.scs = srsran_subcarrier_spacing_15kHz; } carrier.ssb_center_freq_hz = carrier.dl_center_frequency_hz; @@ -479,6 +484,24 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) } prach.tdd_config.configured = (duplex.mode == SRSRAN_DUPLEX_MODE_TDD); + + // Make default CSI report configuration always + csi.reports[0].channel_meas_id = 0; + csi.reports[0].type = SRSRAN_CSI_REPORT_TYPE_PERIODIC; + csi.reports[0].periodic.period = 20; + csi.reports[0].periodic.offset = 9; + csi.reports[0].periodic.resource.format = SRSRAN_PUCCH_NR_FORMAT_2; + csi.reports[0].periodic.resource.starting_prb = 51; + csi.reports[0].periodic.resource.format = SRSRAN_PUCCH_NR_FORMAT_2; + csi.reports[0].periodic.resource.nof_prb = 1; + csi.reports[0].periodic.resource.nof_symbols = 2; + csi.reports[0].periodic.resource.start_symbol_idx = 10; + csi.reports[0].quantity = SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI; + csi.reports[0].cqi_table = SRSRAN_CSI_CQI_TABLE_1; + csi.reports[0].freq_cfg = SRSRAN_CSI_REPORT_FREQ_WIDEBAND; + csi.csi_resources[0].type = srsran_csi_hl_resource_cfg_t::SRSRAN_CSI_HL_RESOURCE_CFG_TYPE_NZP_CSI_RS_SSB; + csi.csi_resources[0].nzp_csi_rs_ssb.nzp_csi_rs_resource_set_id_list[0] = 0; + csi.csi_resources[0].nzp_csi_rs_ssb.nzp_csi_rs_resource_set_id_list_count = 1; } } // namespace srsran diff --git a/lib/src/common/s1ap_pcap.cc b/lib/src/common/s1ap_pcap.cc index 385858308..adeb47a1e 100644 --- a/lib/src/common/s1ap_pcap.cc +++ b/lib/src/common/s1ap_pcap.cc @@ -22,10 +22,22 @@ #include "srsran/common/s1ap_pcap.h" #include "srsran/common/pcap.h" #include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" #include namespace srsran { +/// Try to flush the contents of the pcap class before the application is killed. +static void emergency_cleanup_handler(void* data) +{ + reinterpret_cast(data)->close(); +} + +s1ap_pcap::s1ap_pcap() +{ + add_emergency_cleanup_handler(emergency_cleanup_handler, this); +} + void s1ap_pcap::enable() { enable_write = true; @@ -38,6 +50,9 @@ void s1ap_pcap::open(const char* filename_) } void s1ap_pcap::close() { + if (!enable_write) { + return; + } fprintf(stdout, "Saving S1AP PCAP file (DLT=%d) to %s\n", S1AP_LTE_DLT, filename.c_str()); DLT_PCAP_Close(pcap_file); } diff --git a/lib/src/common/test/band_helper_test.cc b/lib/src/common/test/band_helper_test.cc index a57185779..a89b4c80d 100644 --- a/lib/src/common/test/band_helper_test.cc +++ b/lib/src/common/test/band_helper_test.cc @@ -42,27 +42,31 @@ int bands_test_nr() // n32 n75 TESTASSERT(bands.nr_arfcn_to_freq(290400) == 1452.0e6); TESTASSERT(bands.nr_arfcn_to_freq(294400) == 1472.0e6); - // n5 - TESTASSERT(bands.get_duplex_mode(5) == SRSRAN_DUPLEX_MODE_FDD); - TESTASSERT(bands.nr_arfcn_to_freq(176300) == 881.5e6); - TESTASSERT(bands.freq_to_nr_arfcn(881.5e6) == 176300); - TESTASSERT(bands.get_ul_arfcn_from_dl_arfcn(176300) == 167300); - TESTASSERT(bands.nr_arfcn_to_freq(167300) == 836.5e6); // check actual freqs for FDD carrier (example values are for 52 PRB) TESTASSERT(bands.get_center_freq_from_abs_freq_point_a(52, 175364) == 881.5e6); TESTASSERT(bands.get_center_freq_from_abs_freq_point_a(52, 166364) == 836.5e6); - - // n3 - TESTASSERT(bands.nr_arfcn_to_freq(342000) == 1710.0e6); - TESTASSERT(bands.nr_arfcn_to_freq(348000) == 1740.0e6); - TESTASSERT(bands.nr_arfcn_to_freq(361000) == 1805.0e6); - TESTASSERT(bands.nr_arfcn_to_freq(376000) == 1880.0e6); // n1 TESTASSERT(bands.nr_arfcn_to_freq(384000) == 1920.0e6); TESTASSERT(bands.nr_arfcn_to_freq(388030) == 1940.15e6); TESTASSERT(bands.nr_arfcn_to_freq(391830) == 1959.15e6); TESTASSERT(bands.nr_arfcn_to_freq(434000) == 2170.0e6); + // n3 + TESTASSERT(bands.get_duplex_mode(3) == SRSRAN_DUPLEX_MODE_FDD); + TESTASSERT(bands.nr_arfcn_to_freq(342000) == 1710.0e6); + TESTASSERT(bands.nr_arfcn_to_freq(348000) == 1740.0e6); + TESTASSERT(bands.nr_arfcn_to_freq(361000) == 1805.0e6); + TESTASSERT(bands.nr_arfcn_to_freq(376000) == 1880.0e6); + TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 368500) == 367564); + TESTASSERT(bands.get_abs_freq_ssb_arfcn(3, srsran_subcarrier_spacing_15kHz, 367564) > 367924); + // n5 + TESTASSERT(bands.get_duplex_mode(5) == SRSRAN_DUPLEX_MODE_FDD); + TESTASSERT(bands.nr_arfcn_to_freq(176300) == 881.5e6); + TESTASSERT(bands.freq_to_nr_arfcn(881.5e6) == 176300); + TESTASSERT(bands.get_ul_arfcn_from_dl_arfcn(176300) == 167300); + TESTASSERT(bands.nr_arfcn_to_freq(167300) == 836.5e6); + TESTASSERT(bands.get_abs_freq_point_a_arfcn(52, 176300) == 175364); + TESTASSERT(bands.get_abs_freq_ssb_arfcn(5, srsran_subcarrier_spacing_15kHz, 175364) > 175724); // n7 n38 TESTASSERT(bands.get_duplex_mode(7) == SRSRAN_DUPLEX_MODE_FDD); TESTASSERT(bands.nr_arfcn_to_freq(500000) == 2500.0e6); diff --git a/lib/src/common/thread_pool.cc b/lib/src/common/thread_pool.cc index 11659a58b..181958ce5 100644 --- a/lib/src/common/thread_pool.cc +++ b/lib/src/common/thread_pool.cc @@ -401,6 +401,75 @@ void task_thread_pool::worker_t::run_thread() running = false; } +task_worker::task_worker(std::string thread_name_, + uint32_t queue_size, + bool start_deferred, + int32_t prio_, + uint32_t mask_) : + thread(std::move(thread_name_)), + prio(prio_), + mask(mask_), + pending_tasks(queue_size), + logger(srslog::fetch_basic_logger("POOL")) +{ + if (not start_deferred) { + start(prio_, mask_); + } +} + +task_worker::~task_worker() +{ + stop(); +} + +void task_worker::stop() +{ + if (not pending_tasks.is_stopped()) { + pending_tasks.stop(); + wait_thread_finish(); + } +} + +void task_worker::start(int32_t prio_, uint32_t mask_) +{ + prio = prio_; + mask = mask_; + + if (mask == 255) { + thread::start(prio); + } else { + thread::start_cpu_mask(prio, mask); + } +} + +void task_worker::push_task(task_t&& task) +{ + auto ret = pending_tasks.try_push(std::move(task)); + if (ret.is_error()) { + logger.error("Cannot push anymore tasks into the worker queue. maximum size is %u", + uint32_t(pending_tasks.max_size())); + return; + } +} + +uint32_t task_worker::nof_pending_tasks() const +{ + return pending_tasks.size(); +} + +void task_worker::run_thread() +{ + while (true) { + bool success; + task_t task = pending_tasks.pop_blocking(&success); + if (not success) { + break; + } + task(); + } + logger.info("Task worker %s finished.", thread::get_name().c_str()); +} + // Global thread pool for long, low-priority tasks task_thread_pool& get_background_workers() { diff --git a/lib/src/mac/mac_rar_pdu_nr.cc b/lib/src/mac/mac_rar_pdu_nr.cc index 6f53fad4d..7b5665c15 100644 --- a/lib/src/mac/mac_rar_pdu_nr.cc +++ b/lib/src/mac/mac_rar_pdu_nr.cc @@ -223,7 +223,8 @@ int mac_rar_pdu_nr::init_tx(byte_buffer_t* buffer_, uint32_t pdu_len_) logger.error("Invalid buffer"); return SRSRAN_ERROR; } - buffer = buffer_; + buffer = buffer_; + buffer->N_bytes = 0; subpdus.clear(); pdu_len = pdu_len_; remaining_len = pdu_len_; diff --git a/lib/src/mac/mac_sch_pdu_nr.cc b/lib/src/mac/mac_sch_pdu_nr.cc index f802eefc7..d06d7b0b3 100644 --- a/lib/src/mac/mac_sch_pdu_nr.cc +++ b/lib/src/mac/mac_sch_pdu_nr.cc @@ -34,8 +34,18 @@ mac_sch_subpdu_nr::nr_lcid_sch_t mac_sch_subpdu_nr::get_type() bool mac_sch_subpdu_nr::is_sdu() { - // for UL-SCH LCID 52 is also valid for carrying SDUs - return (lcid <= 32 || (parent->is_ulsch() && lcid == 52)); + return (lcid <= 32); +} + +bool mac_sch_subpdu_nr::has_length_field() +{ + // CCCH (both versions) don't have a length field in the UL + if (parent->is_ulsch()) { + if (lcid == CCCH_SIZE_48 || lcid == CCCH_SIZE_64) { + return false; + } + } + return (is_sdu() || is_var_len_ce(lcid)); } // returns false for all reserved values in Table 6.2.1-1 and 6.2.1-2 @@ -44,9 +54,15 @@ bool mac_sch_subpdu_nr::is_valid_lcid() return (lcid <= 63 && ((parent->is_ulsch() && (lcid <= 32 || lcid >= 52)) || (lcid <= 32 || lcid >= 47))); } -bool mac_sch_subpdu_nr::is_var_len_ce() +bool mac_sch_subpdu_nr::is_var_len_ce(uint32_t lcid) { - return false; + switch (lcid) { + case LONG_TRUNC_BSR: + case LONG_BSR: + return true; + default: + return false; + } } // return length of PDU (or SRSRAN_ERROR otherwise) @@ -59,7 +75,7 @@ int32_t mac_sch_subpdu_nr::read_subheader(const uint8_t* ptr) header_length = 1; if (is_valid_lcid()) { - if ((is_sdu() || is_var_len_ce()) && not is_ul_ccch()) { + if (has_length_field()) { // Read first length byte sdu_length = (uint32_t)*ptr; ptr++; @@ -245,7 +261,7 @@ mac_sch_subpdu_nr::ta_t mac_sch_subpdu_nr::get_ta() mac_sch_subpdu_nr::lcg_bsr_t mac_sch_subpdu_nr::get_sbsr() { lcg_bsr_t sbsr = {}; - if (parent->is_ulsch() && lcid == SHORT_BSR) { + if (parent->is_ulsch() && (lcid == SHORT_BSR || lcid == SHORT_TRUNC_BSR)) { uint8_t* ptr = sdu.ptr(); sbsr.lcg_id = (ptr[0] & 0xe0) >> 5; sbsr.buffer_size = ptr[0] & 0x1f; @@ -253,6 +269,44 @@ mac_sch_subpdu_nr::lcg_bsr_t mac_sch_subpdu_nr::get_sbsr() return sbsr; } +mac_sch_subpdu_nr::lbsr_t mac_sch_subpdu_nr::get_lbsr() +{ + lbsr_t lbsr = {}; + lbsr.list.reserve(mac_sch_subpdu_nr::max_num_lcg_lbsr); + + if (parent->is_ulsch() && (lcid == LONG_BSR || lcid == LONG_TRUNC_BSR)) { + uint8_t* ptr = sdu.ptr(); + lbsr.bitmap = *ptr; // read LCG bitmap + ptr++; // skip LCG bitmap + + // early stop if LBSR is empty + if (lbsr.bitmap == 0) { + return lbsr; + } + + int bsr_cnt = 0; + for (int i = 0; i < mac_sch_subpdu_nr::max_num_lcg_lbsr; i++) { + // If LCGi bit is enabled, it means the next 8-bit BSR value corresponds to it + if (lbsr.bitmap & (0x1 << i)) { + lcg_bsr_t bsr = {}; + bsr.lcg_id = i; + // For the Long truncated, some BSR words can be not present, assume BSR > 0 in that case + if (1 + bsr_cnt < sdu_length) { + bsr.buffer_size = ptr[bsr_cnt]; + bsr_cnt++; + } else if (lcid == LONG_TRUNC_BSR) { + bsr.buffer_size = 63; // just assume it has 526 bytes to transmit + } else { + fprintf(stderr, "Error parsing LongBSR CE: sdu_length=%d but there are %d active bsr\n", sdu_length, bsr_cnt); + } + lbsr.list.push_back(bsr); + } + } + } + + return lbsr; +} + uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul) { if (is_ul) { @@ -263,12 +317,14 @@ uint32_t mac_sch_subpdu_nr::sizeof_ce(uint32_t lcid, bool is_ul) return 8; case CRNTI: return 2; - case SHORT_TRUNC_BSR: - return 1; case SHORT_BSR: + case SHORT_TRUNC_BSR: return 1; case SE_PHR: return 2; + case LONG_BSR: + case LONG_TRUNC_BSR: + return 1; // minimum size, could be more than that case PADDING: return 0; } @@ -314,9 +370,13 @@ void mac_sch_subpdu_nr::to_string(fmt::memory_buffer& buffer) lcg_bsr_t sbsr = get_sbsr(); fmt::format_to(buffer, " SBSR: lcg={} bs={}", sbsr.lcg_id, sbsr.buffer_size); } break; - case mac_sch_subpdu_nr::LONG_BSR: - fmt::format_to(buffer, " LBSR: len={}", get_total_length()); - break; + case mac_sch_subpdu_nr::LONG_BSR: { + mac_sch_subpdu_nr::lbsr_t lbsr = get_lbsr(); + fmt::format_to(buffer, " LBSR: bitmap={:#02x}", lbsr.bitmap); + for (const auto& lcg : lbsr.list) { + fmt::format_to(buffer, " lcg={} bs={}", lcg.lcg_id, lcg.buffer_size); + } + } break; case mac_sch_subpdu_nr::SE_PHR: fmt::format_to(buffer, " SE_PHR: ph={} pc={}", get_phr(), get_pcmax()); break; @@ -430,13 +490,8 @@ uint32_t mac_sch_pdu_nr::size_header_sdu(const uint32_t lcid, const uint32_t nby { if (ulsch && (lcid == mac_sch_subpdu_nr::CCCH_SIZE_48 || lcid == mac_sch_subpdu_nr::CCCH_SIZE_64)) { return 1; - } else { - if (nbytes < 256) { - return 2; - } else { - return 3; - } } + return nbytes < 256 ? 2 : 3; } uint32_t mac_sch_pdu_nr::get_remaing_len() diff --git a/lib/src/pdcp/pdcp_entity_lte.cc b/lib/src/pdcp/pdcp_entity_lte.cc index 73edecca1..4c25e5063 100644 --- a/lib/src/pdcp/pdcp_entity_lte.cc +++ b/lib/src/pdcp/pdcp_entity_lte.cc @@ -481,10 +481,14 @@ void pdcp_entity_lte::update_rx_counts_queue(uint32_t rx_count) rx_counts_info.pop_back(); fmc++; } - logger.debug("Queue too large. Updating. New FMC=%d, new back=%d, new queue_size=%zu", - fmc, - rx_counts_info.back(), - rx_counts_info.size()); + if (not rx_counts_info.empty()) { + logger.debug("Queue too large. Updating. New FMC=%d, new back=%d, new queue_size=%zu", + fmc, + rx_counts_info.back(), + rx_counts_info.size()); + } else { + logger.debug("Queue too large. Updating. New FMC=%d, new queue_size=%zu", fmc, rx_counts_info.size()); + } } if (rx_counts_info.empty()) { diff --git a/lib/src/phy/ch_estimation/dmrs_pucch.c b/lib/src/phy/ch_estimation/dmrs_pucch.c index 3e646af05..47251e588 100644 --- a/lib/src/phy/ch_estimation/dmrs_pucch.c +++ b/lib/src/phy/ch_estimation/dmrs_pucch.c @@ -23,6 +23,7 @@ #include "srsran/phy/common/sequence.h" #include "srsran/phy/utils/debug.h" #include "srsran/phy/utils/vector.h" +#include // Implements TS 38.211 table 6.4.1.3.1.1-1: Number of DM-RS symbols and the corresponding N_PUCCH... static uint32_t dmrs_pucch_format1_n_pucch(const srsran_pucch_nr_resource_t* resource, uint32_t m_prime) @@ -226,12 +227,13 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, } // Perform measurements - float rsrp = 0.0f; - float epre = 0.0f; - float ta_err = 0.0f; + float rsrp = 0.0f; + float epre = 0.0f; + float ta_err = 0.0f; + cf_t corr[SRSRAN_PUCCH_NR_FORMAT1_N_MAX] = {}; for (uint32_t m = 0; m < n_pucch; m++) { - cf_t corr = srsran_vec_acc_cc(ce[m], SRSRAN_NRE) / SRSRAN_NRE; - rsrp += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + corr[m] = srsran_vec_acc_cc(ce[m], SRSRAN_NRE) / SRSRAN_NRE; + rsrp += SRSRAN_CSQABS(corr[m]); epre += srsran_vec_avg_power_cf(ce[m], SRSRAN_NRE); ta_err += srsran_vec_estimate_frequency(ce[m], SRSRAN_NRE); } @@ -263,7 +265,22 @@ int srsran_dmrs_pucch_format1_estimate(const srsran_pucch_nr_t* q, } // Measure CFO - res->cfo_hz = NAN; // Not implemented + if (n_pucch > 1) { + float cfo_avg_hz = 0.0f; + for (uint32_t m = 0; m < n_pucch - 1; m++) { + uint32_t l0 = resource->start_symbol_idx + m * 2; + uint32_t l1 = resource->start_symbol_idx + (m + 1) * 2; + float time_diff = srsran_symbol_distance_s(l0, l1, q->carrier.scs); + float phase_diff = cargf(corr[m + 1] * conjf(corr[m])); + + if (isnormal(time_diff)) { + cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (n_pucch - 1)); + } + } + res->cfo_hz = cfo_avg_hz; + } else { + res->cfo_hz = NAN; // Not implemented + } // Do averaging here // ... Not implemented @@ -388,12 +405,13 @@ int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q, } // Perform measurements - float epre = 0.0f; - float rsrp = 0.0f; - float ta_err = 0.0f; + float epre = 0.0f; + float rsrp = 0.0f; + float ta_err = 0.0f; + cf_t corr[SRSRAN_PUCCH_NR_FORMAT2_MAX_NSYMB] = {}; for (uint32_t i = 0; i < resource->nof_symbols; i++) { - cf_t corr = srsran_vec_acc_cc(ce[i], nof_ref) / nof_ref; - rsrp += __real__ corr * __real__ corr + __imag__ corr * __imag__ corr; + corr[i] = srsran_vec_acc_cc(ce[i], nof_ref) / nof_ref; + rsrp += SRSRAN_CSQABS(corr[i]); epre += srsran_vec_avg_power_cf(ce[i], nof_ref); ta_err += srsran_vec_estimate_frequency(ce[i], nof_ref); } @@ -422,6 +440,24 @@ int srsran_dmrs_pucch_format2_estimate(const srsran_pucch_nr_t* q, res->ta_us = 0.0f; } + // Measure CFO + if (resource->nof_symbols > 1) { + float cfo_avg_hz = 0.0f; + for (uint32_t l = 0; l < resource->nof_symbols - 1; l++) { + uint32_t l0 = resource->start_symbol_idx + l; + uint32_t l1 = resource->start_symbol_idx + l + 1; + float time_diff = srsran_symbol_distance_s(l0, l1, q->carrier.scs); + float phase_diff = cargf(corr[l + 1] * conjf(corr[l])); + + if (isnormal(time_diff)) { + cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (resource->nof_symbols - 1)); + } + } + res->cfo_hz = cfo_avg_hz; + } else { + res->cfo_hz = NAN; // Not implemented + } + // Perform averaging // ... diff --git a/lib/src/phy/ch_estimation/dmrs_sch.c b/lib/src/phy/ch_estimation/dmrs_sch.c index 5e9b9c791..70603ad07 100644 --- a/lib/src/phy/ch_estimation/dmrs_sch.c +++ b/lib/src/phy/ch_estimation/dmrs_sch.c @@ -817,7 +817,7 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, sync_err += srsran_vec_estimate_frequency(&q->pilot_estimates[nof_pilots_x_symbol * i], nof_pilots_x_symbol); } sync_err /= (float)nof_symbols; - chest_res->sync_error = sync_err / (dmrs_stride * SRSRAN_SUBC_SPACING_NR(q->carrier.scs)); + float delay_us = sync_err / (dmrs_stride * SRSRAN_SUBC_SPACING_NR(q->carrier.scs)); #if DMRS_SCH_SYNC_PRECOMPENSATE // Pre-compensate synchronization error @@ -845,36 +845,52 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, epre /= nof_symbols; rsrp = SRSRAN_MIN(rsrp, epre - epre * 1e-7); - chest_res->rsrp = rsrp; - chest_res->rsrp_dbm = srsran_convert_power_to_dB(chest_res->rsrp); - - chest_res->noise_estimate = epre - rsrp; - chest_res->noise_estimate_dbm = srsran_convert_power_to_dB(chest_res->noise_estimate); - - chest_res->snr_db = chest_res->rsrp_dbm - chest_res->noise_estimate_dbm; - // Measure CFO if more than one symbol is used - float cfo_avg = 0.0; + float cfo_avg_hz = 0.0; + float cfo_hz_max = INFINITY; for (uint32_t i = 0; i < nof_symbols - 1; i++) { float time_diff = srsran_symbol_distance_s(symbols[i], symbols[i + 1], q->carrier.scs); float phase_diff = cargf(corr[i + 1] * conjf(corr[i])); if (isnormal(time_diff)) { - cfo_avg += phase_diff / (2.0f * M_PI * time_diff * (nof_symbols - 1)); + cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (nof_symbols - 1)); + + // The maximum measured CFO depends on the symbol time difference + cfo_hz_max = SRSRAN_MIN(cfo_hz_max, 1 / time_diff); } } - chest_res->cfo = cfo_avg; + + // Store internal CSI + q->csi.rsrp = rsrp; + q->csi.rsrp_dB = srsran_convert_power_to_dB(rsrp); + q->csi.epre = epre; + q->csi.epre_dB = srsran_convert_power_to_dB(epre); + q->csi.n0 = epre - rsrp; + q->csi.n0_dB = srsran_convert_power_to_dB(q->csi.n0); + q->csi.snr_dB = q->csi.rsrp_dB - q->csi.n0_dB; + q->csi.cfo_hz = cfo_avg_hz; + q->csi.cfo_hz_max = cfo_hz_max; + q->csi.delay_us = delay_us; + + // Write CSI in estimated channel result + chest_res->rsrp = q->csi.rsrp; + chest_res->rsrp_dbm = q->csi.rsrp_dB; + chest_res->noise_estimate = q->csi.n0; + chest_res->noise_estimate_dbm = q->csi.n0_dB; + chest_res->snr_db = q->csi.snr_dB; + chest_res->cfo = q->csi.cfo_hz; + chest_res->sync_error = q->csi.delay_us; #if DMRS_SCH_CFO_PRECOMPENSATE // Pre-compensate CFO cf_t cfo_correction[SRSRAN_NSYMB_PER_SLOT_NR] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - if (isnormal(cfo_avg)) { + if (isnormal(cfo_avg_hz)) { // Calculate phase of the first OFDM symbol (l = 0) - float arg0 = cargf(corr[0]) - 2.0f * M_PI * srsran_symbol_distance_s(0, symbols[0], q->carrier.scs) * cfo_avg; + float arg0 = cargf(corr[0]) - 2.0f * M_PI * srsran_symbol_distance_s(0, symbols[0], q->carrier.scs) * cfo_avg_hz; // Calculate CFO corrections for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) { - float arg = arg0 + 2.0f * M_PI * cfo_avg * srsran_symbol_distance_s(0, l, q->carrier.scs); + float arg = arg0 + 2.0f * M_PI * cfo_avg_hz * srsran_symbol_distance_s(0, l, q->carrier.scs); cfo_correction[l] = cexpf(I * arg); } @@ -892,7 +908,7 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, INFO("PDSCH-DMRS: RSRP=%+.2fdB EPRE=%+.2fdB CFO=%+.0fHz Sync=%.3fus", chest_res->rsrp_dbm, srsran_convert_power_to_dB(epre), - cfo_avg, + cfo_avg_hz, chest_res->sync_error * 1e6); // Average over time, only if more than one DMRS symbol diff --git a/lib/src/phy/common/phy_common_nr.c b/lib/src/phy/common/phy_common_nr.c index 8230daf72..094baf67a 100644 --- a/lib/src/phy/common/phy_common_nr.c +++ b/lib/src/phy/common/phy_common_nr.c @@ -443,6 +443,22 @@ uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* s meas->delay_us); } +uint32_t srsran_csi_meas_info_short(const srsran_csi_trs_measurements_t* meas, char* str, uint32_t str_len) +{ + if (meas == NULL || str == NULL || str_len == 0) { + return 0; + } + + return srsran_print_check(str, + str_len, + 0, + "epre=%+.1f snr=%+.1f cfo=%+.1f delay=%+.1f ", + meas->epre_dB, + meas->snr_dB, + meas->cfo_hz, + meas->delay_us); +} + srsran_subcarrier_spacing_t srsran_subcarrier_spacing_from_str(const char* str) { if (str == NULL) { @@ -674,3 +690,52 @@ int srsran_coreset_zero(uint32_t n_cell_id, return SRSRAN_SUCCESS; } + +const char* srsran_ssb_pattern_to_str(srsran_ssb_patern_t pattern) +{ + switch (pattern) { + case SRSRAN_SSB_PATTERN_A: + return "A"; + case SRSRAN_SSB_PATTERN_B: + return "B"; + case SRSRAN_SSB_PATTERN_C: + return "C"; + case SRSRAN_SSB_PATTERN_D: + return "D"; + case SRSRAN_SSB_PATTERN_E: + return "E"; + case SRSRAN_SSB_PATTERN_INVALID: + default: + break; + } + return "Invalid"; +} + +srsran_ssb_patern_t srsran_ssb_pattern_fom_str(const char* str) +{ + if (str == NULL) { + return SRSRAN_SSB_PATTERN_INVALID; + } + + if (strcasecmp(str, "A") == 0) { + return SRSRAN_SSB_PATTERN_A; + } + + if (strcasecmp(str, "B") == 0) { + return SRSRAN_SSB_PATTERN_B; + } + + if (strcasecmp(str, "C") == 0) { + return SRSRAN_SSB_PATTERN_C; + } + + if (strcasecmp(str, "D") == 0) { + return SRSRAN_SSB_PATTERN_D; + } + + if (strcasecmp(str, "E") == 0) { + return SRSRAN_SSB_PATTERN_E; + } + + return SRSRAN_SSB_PATTERN_INVALID; +} diff --git a/lib/src/phy/gnb/gnb_ul.c b/lib/src/phy/gnb/gnb_ul.c index 4109a7ee6..3d12fbe12 100644 --- a/lib/src/phy/gnb/gnb_ul.c +++ b/lib/src/phy/gnb/gnb_ul.c @@ -29,6 +29,11 @@ */ #define GNB_UL_NR_FFT_WINDOW_OFFSET 0.5f +/** + * @brief Minimum PUSCH DMRS measured SINR default value + */ +#define GNB_UL_PUSCH_MIN_SNR_DEFAULT -10.0f + static int gnb_ul_alloc_prb(srsran_gnb_ul_t* q, uint32_t new_nof_prb) { if (q->max_prb < new_nof_prb) { @@ -92,6 +97,12 @@ int srsran_gnb_ul_init(srsran_gnb_ul_t* q, cf_t* input, const srsran_gnb_ul_args return SRSRAN_ERROR; } + // Set PUSCH minimum SNR, use default value if the given is NAN, INF or zero + q->pusch_min_snr_dB = GNB_UL_PUSCH_MIN_SNR_DEFAULT; + if (isnormal(args->pusch_min_snr_dB)) { + q->pusch_min_snr_dB = args->pusch_min_snr_dB; + } + return SRSRAN_SUCCESS; } @@ -144,7 +155,7 @@ int srsran_gnb_ul_set_carrier(srsran_gnb_ul_t* q, const srsran_carrier_nr_t* car ofdm_cfg.rx_window_offset = GNB_UL_NR_FFT_WINDOW_OFFSET; ofdm_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); ofdm_cfg.keep_dc = true; - ofdm_cfg.phase_compensation_hz = carrier->dl_center_frequency_hz; + ofdm_cfg.phase_compensation_hz = carrier->ul_center_frequency_hz; if (srsran_ofdm_rx_init_cfg(&q->fft, &ofdm_cfg) < SRSRAN_SUCCESS) { return SRSRAN_ERROR; @@ -178,6 +189,15 @@ int srsran_gnb_ul_get_pusch(srsran_gnb_ul_t* q, return SRSRAN_ERROR; } + // Check PUSCH DMRS minimum SNR and abort PUSCH decoding if it is below the threshold + if (q->dmrs.csi.snr_dB < q->pusch_min_snr_dB) { + // Set PUSCH data as not decoded + data->tb[0].crc = false; + data->tb[0].avg_iter = NAN; + data->uci.valid = false; + return SRSRAN_SUCCESS; + } + if (srsran_pusch_nr_decode(&q->pusch, cfg, grant, &q->chest_pusch, q->sf_symbols, data) < SRSRAN_SUCCESS) { return SRSRAN_ERROR; } @@ -308,11 +328,12 @@ int srsran_gnb_ul_get_pucch(srsran_gnb_ul_t* q, return SRSRAN_SUCCESS; } -uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, - const srsran_pucch_nr_resource_t* resource, - const srsran_uci_data_nr_t* uci_data, - char* str, - uint32_t str_len) +uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, + const srsran_pucch_nr_resource_t* resource, + const srsran_uci_data_nr_t* uci_data, + const srsran_csi_trs_measurements_t* csi, + char* str, + uint32_t str_len) { if (q == NULL || uci_data == NULL) { return 0; @@ -320,10 +341,11 @@ uint32_t srsran_gnb_ul_pucch_info(srsran_gnb_ul_t* q, uint32_t len = 0; - len += srsran_pucch_nr_info(resource, uci_data, str, str_len - len); + len += srsran_pucch_nr_info(resource, uci_data, &str[len], str_len - len); + + len += srsran_csi_meas_info_short(csi, &str[len], str_len - len); - len = srsran_print_check( - str, str_len, len, "snr=%+.1f valid=%c", q->chest_pucch.snr_db, uci_data->value.valid ? 'y' : 'n'); + len = srsran_print_check(str, str_len, len, "valid=%c ", uci_data->value.valid ? 'y' : 'n'); return len; } @@ -342,7 +364,8 @@ uint32_t srsran_gnb_ul_pusch_info(srsran_gnb_ul_t* q, len += srsran_pusch_nr_rx_info(&q->pusch, cfg, &cfg->grant, res, str, str_len - len); - len = srsran_print_check(str, str_len, len, "snr=%+.1f", q->chest_pusch.snr_db); + // Append channel estimator info + len += srsran_csi_meas_info_short(&q->dmrs.csi, &str[len], str_len - len); return len; } \ No newline at end of file diff --git a/lib/src/phy/phch/csi.c b/lib/src/phy/phch/csi.c index ca77d3322..566b38611 100644 --- a/lib/src/phy/phch/csi.c +++ b/lib/src/phy/phch/csi.c @@ -48,7 +48,6 @@ static bool csi_report_trigger(const srsran_csi_hl_report_cfg_t* cfg, uint32_t s static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_t* cfg, const srsran_csi_channel_measurements_t* channel_meas, const srsran_csi_channel_measurements_t* interf_meas, - srsran_csi_report_cfg_t* report_cfg, srsran_csi_report_value_t* report_value) { // Take SNR by default @@ -59,18 +58,6 @@ static void csi_wideband_cri_ri_pmi_cqi_quantify(const srsran_csi_hl_report_cfg_ wideband_sinr_db = channel_meas->wideband_rsrp_dBm - interf_meas->wideband_epre_dBm; } - // Fill report configuration - report_cfg->type = cfg->type; - report_cfg->quantity = SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI; - report_cfg->freq_cfg = SRSRAN_CSI_REPORT_FREQ_WIDEBAND; - report_cfg->nof_ports = channel_meas->nof_ports; - report_cfg->K_csi_rs = channel_meas->K_csi_rs; - - // Save PUCCH resource only if periodic type - if (cfg->type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { - report_cfg->pucch_resource = cfg->periodic.resource; - } - // Fill quantified values report_value->wideband_cri_ri_pmi_cqi.cqi = csi_snri_db_to_cqi(cfg->cqi_table, wideband_sinr_db); report_value->wideband_cri_ri_pmi_cqi.ri = 0; @@ -199,48 +186,79 @@ int srsran_csi_new_nzp_csi_rs_measurement( return SRSRAN_SUCCESS; } -int srsran_csi_generate_reports(const srsran_csi_hl_cfg_t* cfg, - uint32_t slot_idx, - const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], - srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT], - srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]) +int srsran_csi_reports_generate(const srsran_csi_hl_cfg_t* cfg, + const srsran_slot_cfg_t* slot_cfg, + srsran_csi_report_cfg_t report_cfg[SRSRAN_CSI_MAX_NOF_REPORT]) { uint32_t count = 0; // Check inputs - if (cfg == NULL || measurements == NULL || report_cfg == NULL || report_value == NULL) { + if (cfg == NULL || report_cfg == NULL) { return SRSRAN_ERROR_INVALID_INPUTS; } + // Make sure report configuration is initialised to zero + SRSRAN_MEM_ZERO(report_cfg, srsran_csi_report_cfg_t, SRSRAN_CSI_MAX_NOF_REPORT); + // Iterate every possible configured CSI report for (uint32_t i = 0; i < SRSRAN_CSI_MAX_NOF_REPORT; i++) { // Skip if report is not configured or triggered - if (!csi_report_trigger(&cfg->reports[i], slot_idx)) { + if (!csi_report_trigger(&cfg->reports[i], slot_cfg->idx)) { continue; } + // Configure report + report_cfg[count].cfg = cfg->reports[i]; + report_cfg[count].nof_ports = 1; + report_cfg[count].K_csi_rs = 1; + report_cfg[count].has_part2 = false; + count++; + } + + return (int)count; +} + +int srsran_csi_reports_quantify(const srsran_csi_report_cfg_t reports[SRSRAN_CSI_MAX_NOF_REPORT], + const srsran_csi_channel_measurements_t measurements[SRSRAN_CSI_MAX_NOF_RESOURCES], + srsran_csi_report_value_t report_value[SRSRAN_CSI_MAX_NOF_REPORT]) +{ + uint32_t count = 0; + + // Check inputs + if (reports == NULL || measurements == NULL || report_value == NULL) { + return SRSRAN_ERROR_INVALID_INPUTS; + } + + // Iterate every possible configured CSI report + for (uint32_t i = 0; i < SRSRAN_CSI_MAX_NOF_REPORT; i++) { + // If the report is the last one, break + if (reports->cfg.type == SRSRAN_CSI_REPORT_TYPE_NONE) { + break; + } + // Select channel measurement - if (cfg->reports->channel_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { - ERROR("Channel measurement ID (%d) is out of range", cfg->reports->channel_meas_id); + uint32_t channel_meas_id = reports[i].cfg.channel_meas_id; + if (channel_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { + ERROR("Channel measurement ID (%d) is out of range", channel_meas_id); return SRSRAN_ERROR; } - const srsran_csi_channel_measurements_t* channel_meas = &measurements[cfg->reports->channel_meas_id]; + const srsran_csi_channel_measurements_t* channel_meas = &measurements[channel_meas_id]; // Select interference measurement const srsran_csi_channel_measurements_t* interf_meas = NULL; - if (cfg->reports->interf_meas_present) { - if (cfg->reports->interf_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { - ERROR("Interference measurement ID (%d) is out of range", cfg->reports->interf_meas_id); + if (reports[i].cfg.interf_meas_present) { + uint32_t interf_meas_id = reports[i].cfg.interf_meas_id; + if (interf_meas_id >= SRSRAN_CSI_MAX_NOF_RESOURCES) { + ERROR("Interference measurement ID (%d) is out of range", interf_meas_id); return SRSRAN_ERROR; } - interf_meas = &measurements[cfg->reports->interf_meas_id]; + interf_meas = &measurements[interf_meas_id]; } // Quantify measurements according to frequency and quantity configuration - if (cfg->reports->freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && - cfg->reports->quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { - csi_wideband_cri_ri_pmi_cqi_quantify( - &cfg->reports[i], channel_meas, interf_meas, &report_cfg[count], &report_value[count]); + if (reports[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && + reports[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + csi_wideband_cri_ri_pmi_cqi_quantify(&reports[i].cfg, channel_meas, interf_meas, &report_value[count]); count++; } else { ; // Ignore other types @@ -262,9 +280,9 @@ int srsran_csi_part1_nof_bits(const srsran_csi_report_cfg_t* report_list, uint32 // Iterate all report configurations for (uint32_t i = 0; i < nof_reports; i++) { const srsran_csi_report_cfg_t* report = &report_list[i]; - if (report->quantity && report->quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + if (report->cfg.quantity && report->cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { count += csi_wideband_cri_ri_pmi_cqi_nof_bits(report); - } else if (report->quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { + } else if (report->cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { count += csi_none_nof_bits(report); } } @@ -317,15 +335,15 @@ int srsran_csi_part1_pack(const srsran_csi_report_cfg_t* report_cfg, } for (uint32_t i = 0; i < nof_reports && count < max_o_csi1; i++) { - if (report_cfg[i].freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && - report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + if (report_cfg[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && + report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { count += csi_wideband_cri_ri_pmi_cqi_pack(&report_cfg[i], &report_value[i], &o_csi1[count]); - } else if (report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { + } else if (report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { count += csi_none_pack(&report_cfg[i], &report_value[i], &o_csi1[count]); } else { ERROR("CSI frequency (%d) and quantity (%d) combination is not implemented", - report_cfg[i].freq_cfg, - report_cfg[i].quantity); + report_cfg[i].cfg.freq_cfg, + report_cfg[i].cfg.quantity); } } @@ -351,15 +369,15 @@ int srsran_csi_part1_unpack(const srsran_csi_report_cfg_t* report_cfg, } for (uint32_t i = 0; i < nof_reports && count < max_o_csi1; i++) { - if (report_cfg[i].freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && - report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + if (report_cfg[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && + report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { count += csi_wideband_cri_ri_pmi_cqi_unpack(&report_cfg[i], &o_csi1[count], &report_value[i]); - } else if (report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { + } else if (report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { count += csi_none_unpack(&report_cfg[i], &o_csi1[count], &report_value[i]); } else { ERROR("CSI frequency (%d) and quantity (%d) combination is not implemented", - report_cfg[i].freq_cfg, - report_cfg[i].quantity); + report_cfg[i].cfg.freq_cfg, + report_cfg[i].cfg.quantity); } } @@ -374,10 +392,10 @@ uint32_t srsran_csi_str(const srsran_csi_report_cfg_t* report_cfg, { uint32_t len = 0; for (uint32_t i = 0; i < nof_reports; i++) { - if (report_cfg[i].freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && - report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { + if (report_cfg[i].cfg.freq_cfg == SRSRAN_CSI_REPORT_FREQ_WIDEBAND && + report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI) { len = srsran_print_check(str, str_len, len, "cqi=%d ", report_value[i].wideband_cri_ri_pmi_cqi.cqi); - } else if (report_cfg[i].quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { + } else if (report_cfg[i].cfg.quantity == SRSRAN_CSI_REPORT_QUANTITY_NONE) { char tmp[20] = {}; srsran_vec_sprint_bin(tmp, sizeof(tmp), report_value[i].none, report_cfg->K_csi_rs); len = srsran_print_check(str, str_len, len, "csi=%s ", tmp); diff --git a/lib/src/phy/phch/dci_nr.c b/lib/src/phy/phch/dci_nr.c index c866ac7ff..66ff897dd 100644 --- a/lib/src/phy/phch/dci_nr.c +++ b/lib/src/phy/phch/dci_nr.c @@ -2081,10 +2081,11 @@ uint32_t srsran_dci_ctx_to_str(const srsran_dci_ctx_t* ctx, char* str, uint32_t len = srsran_print_check(str, str_len, len, - "%s-rnti=%04x dci=%s ", + "%s-rnti=%04x dci=%s ss=%s ", srsran_rnti_type_str_short(ctx->rnti_type), ctx->rnti, - srsran_dci_format_nr_string(ctx->format)); + srsran_dci_format_nr_string(ctx->format), + srsran_ss_type_str(ctx->ss_type)); if (ctx->format != srsran_dci_format_nr_rar) { len = srsran_print_check(str, str_len, len, "L=%d cce=%d ", ctx->location.L, ctx->location.ncce); diff --git a/lib/src/phy/phch/pbch_nr.c b/lib/src/phy/phch/pbch_nr.c index 5725c2a93..85e4e2fda 100644 --- a/lib/src/phy/phch/pbch_nr.c +++ b/lib/src/phy/phch/pbch_nr.c @@ -419,9 +419,11 @@ static void pbch_nr_scramble_tx(const srsran_pbch_nr_cfg_t* cfg, uint32_t M_bit = PBCH_NR_E; // Select value v - uint32_t v = (ssb_idx & 0x7U); + // for L max = 8 or L max = 64 , & is the three least significant bits of the SS/PBCH block index + uint32_t v = (ssb_idx & 0b111U); if (cfg->Lmax == 4) { - v = ssb_idx & 0x3U; + // for L max = 4 , & is the two least significant bits of the SS/PBCH block index + v = ssb_idx & 0b11U; } // Advance sequence @@ -444,8 +446,10 @@ static void pbch_nr_scramble_rx(const srsran_pbch_nr_cfg_t* cfg, uint32_t M_bit = PBCH_NR_E; // Select value v + // for L max = 8 or L max = 64 , & is the three least significant bits of the SS/PBCH block index uint32_t v = (ssb_idx & 0b111U); if (cfg->Lmax == 4) { + // for L max = 4 , & is the two least significant bits of the SS/PBCH block index v = ssb_idx & 0b11U; } diff --git a/lib/src/phy/phch/pdsch_nr.c b/lib/src/phy/phch/pdsch_nr.c index ff0857c39..10af133f7 100644 --- a/lib/src/phy/phch/pdsch_nr.c +++ b/lib/src/phy/phch/pdsch_nr.c @@ -577,6 +577,10 @@ static uint32_t pdsch_nr_grant_info(const srsran_pdsch_nr_t* q, } } + // Append RNTI type and id + len = + srsran_print_check(str, str_len, len, "%s-rnti=0x%x ", srsran_rnti_type_str_short(grant->rnti_type), grant->rnti); + // Append time-domain resource mapping len = srsran_print_check(str, str_len, diff --git a/lib/src/phy/phch/prach.c b/lib/src/phy/phch/prach.c index 7551ec969..956820a64 100644 --- a/lib/src/phy/phch/prach.c +++ b/lib/src/phy/phch/prach.c @@ -624,7 +624,9 @@ int srsran_prach_set_cell_(srsran_prach_t* p, int ret = SRSRAN_ERROR; if (p != NULL && N_ifft_ul < 2049 && cfg->config_idx < 64 && cfg->root_seq_idx < MAX_ROOTS) { if (N_ifft_ul > p->max_N_ifft_ul) { - ERROR("PRACH: Error in set_cell(): N_ifft_ul must be lower or equal max_N_ifft_ul in init()"); + ERROR("PRACH: Error in set_cell(): N_ifft_ul (%d) must be lower or equal max_N_ifft_ul (%d) in init()", + N_ifft_ul, + p->max_N_ifft_ul); return -1; } @@ -752,7 +754,7 @@ int srsran_prach_gen(srsran_prach_t* p, uint32_t seq_index, uint32_t freq_offset uint32_t N_rb_ul = srsran_nof_prb(p->N_ifft_ul); uint32_t k_0 = freq_offset * N_RB_SC - N_rb_ul * N_RB_SC / 2 + p->N_ifft_ul / 2; uint32_t K = DELTA_F / DELTA_F_RA; - uint32_t begin = PHI + (K * k_0) + (p->is_nr ? 1 : (K / 2)); + uint32_t begin = PHI + (K * k_0) + (p->is_nr ? 0 : (K / 2)); if (6 + freq_offset > N_rb_ul) { ERROR("Error no space for PRACH: frequency offset=%d, N_rb_ul=%d", freq_offset, N_rb_ul); @@ -762,11 +764,16 @@ int srsran_prach_gen(srsran_prach_t* p, uint32_t seq_index, uint32_t freq_offset DEBUG( "N_zc: %d, N_cp: %d, N_seq: %d, N_ifft_prach=%d begin: %d", p->N_zc, p->N_cp, p->N_seq, p->N_ifft_prach, begin); + // Fill bottom guard frequency domain with zeros + srsran_vec_cf_zero(p->ifft_in, begin); + // Map dft-precoded sequence to ifft bins - memset(p->ifft_in, 0, begin * sizeof(cf_t)); - memcpy(&p->ifft_in[begin], get_precoded_dft(p, seq_index), p->N_zc * sizeof(cf_t)); - memset(&p->ifft_in[begin + p->N_zc], 0, (p->N_ifft_prach - begin - p->N_zc) * sizeof(cf_t)); + srsran_vec_cf_copy(&p->ifft_in[begin], get_precoded_dft(p, seq_index), p->N_zc); + + // Fill top guard frequency domain with zeros + srsran_vec_cf_zero(&p->ifft_in[begin + p->N_zc], p->N_ifft_prach - begin - p->N_zc); + // Generate frequency domain signal srsran_dft_run(&p->ifft, p->ifft_in, p->ifft_out); // Copy CP into buffer @@ -986,7 +993,7 @@ int srsran_prach_detect_offset(srsran_prach_t* p, uint32_t N_rb_ul = srsran_nof_prb(p->N_ifft_ul); uint32_t k_0 = freq_offset * N_RB_SC - N_rb_ul * N_RB_SC / 2 + p->N_ifft_ul / 2; uint32_t K = DELTA_F / DELTA_F_RA; - uint32_t begin = PHI + (K * k_0) + (K / 2); + uint32_t begin = PHI + (K * k_0) + (p->is_nr ? 0 : (K / 2)); memcpy(p->prach_bins, &p->signal_fft[begin], p->N_zc * sizeof(cf_t)); int loops = (p->successive_cancellation) ? SUCCESSIVE_CANCELLATION_ITS : 1; diff --git a/lib/src/phy/phch/pucch_nr.c b/lib/src/phy/phch/pucch_nr.c index faabd1067..edf681dc2 100644 --- a/lib/src/phy/phch/pucch_nr.c +++ b/lib/src/phy/phch/pucch_nr.c @@ -481,8 +481,9 @@ int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* q, return SRSRAN_ERROR; } - // Received symbol d - cf_t d = 0; + // Accumulates received symbol d and average power + cf_t d = 0; + float pwr_acc = 0.0f; // Get group sequence uint32_t u = 0; @@ -526,22 +527,33 @@ int srsran_pucch_nr_format1_decode(srsran_pucch_nr_t* q, srsran_vec_sc_prod_ccc(r_uv, w_i_m, z, SRSRAN_NRE); // Compute d = sum(x * conj(w(i) * r_uv(n))) = sum(w(i) * d' * r_uv(n) * conj(w(i) * r_uv(n))) = d' - d += srsran_vec_dot_prod_conj_ccc(x, z, SRSRAN_NRE); + d += srsran_vec_dot_prod_conj_ccc(x, z, SRSRAN_NRE) / SRSRAN_NRE; + + // Compute and accumulate average symbol power + pwr_acc += srsran_vec_avg_power_cf(x, SRSRAN_NRE); } // Demodulate d float llr[SRSRAN_PUCCH_NR_FORMAT1_MAX_NOF_BITS]; srsran_demod_soft_demodulate((nof_bits == 1) ? SRSRAN_MOD_BPSK : SRSRAN_MOD_QPSK, &d, llr, 1); - // Hard decision - float corr = 0.0f; + // Hard decision based on the LLRs sign for (uint32_t i = 0; i < nof_bits; i++) { b[i] = llr[i] > 0.0f ? 1 : 0; - corr += fabsf(llr[i]); } - if (norm_corr != NULL && nof_bits > 0) { - *norm_corr = corr / nof_bits; + // Calculate normalised correlation, it uses the absolute value of d and accumulated average power + if (norm_corr != NULL) { + // Get the number of payload symbols. As the one of every 2 symbols carry DMRS, the payload symbols is half of the + // total symbols rounding down + float nsymb = (float)SRSRAN_FLOOR(resource->nof_symbols, 2); + + // Avoid zero, INF or NAN division, set correlation to 0 in this case + if (isnormal(pwr_acc) && isnormal(nsymb)) { + *norm_corr = cabsf(d) / sqrtf(pwr_acc * nsymb); + } else { + *norm_corr = 0.0f; + } } return SRSRAN_SUCCESS; diff --git a/lib/src/phy/phch/pusch_nr.c b/lib/src/phy/phch/pusch_nr.c index bd9af526f..fe1c4c7c9 100644 --- a/lib/src/phy/phch/pusch_nr.c +++ b/lib/src/phy/phch/pusch_nr.c @@ -1019,6 +1019,10 @@ static uint32_t pusch_nr_grant_info(const srsran_pusch_nr_t* q, } } + // Append RNTI type and id + len = + srsran_print_check(str, str_len, len, "%s-rnti=0x%x ", srsran_rnti_type_str_short(grant->rnti_type), grant->rnti); + // Append time-domain resource mapping len = srsran_print_check(str, str_len, diff --git a/lib/src/phy/phch/ra_nr.c b/lib/src/phy/phch/ra_nr.c index 6f620e6b7..35680f3c3 100644 --- a/lib/src/phy/phch/ra_nr.c +++ b/lib/src/phy/phch/ra_nr.c @@ -21,6 +21,7 @@ #include "srsran/phy/phch/ra_nr.h" #include "srsran/phy/ch_estimation/csi_rs.h" +#include "srsran/phy/fec/cbsegm.h" #include "srsran/phy/phch/csi.h" #include "srsran/phy/phch/pdsch_nr.h" #include "srsran/phy/phch/ra_dl_nr.h" @@ -459,6 +460,28 @@ static int ra_nr_assert_csi_rs_dmrs_collision(const srsran_sch_cfg_nr_t* pdsch_c return SRSRAN_SUCCESS; } +uint32_t ra_nr_nof_crc_bits(uint32_t tbs, double R) +{ + srsran_cbsegm_t cbsegm = {}; + srsran_basegraph_t bg = srsran_sch_nr_select_basegraph(tbs, R); + + if (bg == BG1) { + if (srsran_cbsegm_ldpc_bg1(&cbsegm, tbs) != SRSRAN_SUCCESS) { + // This should never fail + ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tbs); + return 0; + } + } else { + if (srsran_cbsegm_ldpc_bg2(&cbsegm, tbs) != SRSRAN_SUCCESS) { + // This should never fail + ERROR("Error: calculating LDPC BG1 code block segmentation for tbs=%d", tbs); + return 0; + } + } + + return cbsegm.C * cbsegm.L_cb + cbsegm.L_tb; +} + int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, const srsran_sch_grant_nr_t* grant, uint32_t mcs_idx, @@ -533,7 +556,7 @@ int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg, // Calculate actual rate tb->R_prime = 0.0; if (tb->nof_re != 0) { - tb->R_prime = (double)tb->tbs / (double)tb->nof_bits; + tb->R_prime = (double)(tb->tbs + ra_nr_nof_crc_bits(tb->tbs, tb->R)) / (double)tb->nof_bits; } return SRSRAN_SUCCESS; @@ -1051,7 +1074,10 @@ int srsran_ra_ul_set_grant_uci_nr(const srsran_carrier_nr_t* carrier, pusch_cfg->grant.tb[i].nof_re * srsran_mod_bits_x_symbol(pusch_cfg->grant.tb[i].mod) - Gack - Gcsi1 - Gcsi2; if (pusch_cfg->grant.tb[i].nof_bits > 0) { - pusch_cfg->grant.tb[i].R_prime = (double)pusch_cfg->grant.tb[i].tbs / (double)pusch_cfg->grant.tb[i].nof_bits; + pusch_cfg->grant.tb[i].R_prime = + (double)(pusch_cfg->grant.tb[i].tbs + + ra_nr_nof_crc_bits(pusch_cfg->grant.tb[i].tbs, pusch_cfg->grant.tb[i].R)) / + (double)pusch_cfg->grant.tb[i].nof_bits; } else { pusch_cfg->grant.tb[i].R_prime = NAN; } diff --git a/lib/src/phy/phch/ra_ul_nr.c b/lib/src/phy/phch/ra_ul_nr.c index a213ad76f..5a76fe24b 100644 --- a/lib/src/phy/phch/ra_ul_nr.c +++ b/lib/src/phy/phch/ra_ul_nr.c @@ -623,8 +623,8 @@ int srsran_ra_ul_nr_pucch_resource(const srsran_pucch_nr_hl_cfg_t* pucch_cfg, // - Irrelevant SR opportunities // - No HARQ-ACK // - Single periodic CSI report - if (uci_cfg->ack.count == 0 && uci_cfg->nof_csi == 1 && uci_cfg->csi[0].type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { - *resource = uci_cfg->csi[0].pucch_resource; + if (uci_cfg->ack.count == 0 && uci_cfg->nof_csi == 1 && uci_cfg->csi[0].cfg.type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { + *resource = uci_cfg->csi[0].cfg.periodic.resource; return SRSRAN_SUCCESS; } diff --git a/lib/src/phy/phch/sch_nr.c b/lib/src/phy/phch/sch_nr.c index be2f5c2c8..9c1043d0d 100644 --- a/lib/src/phy/phch/sch_nr.c +++ b/lib/src/phy/phch/sch_nr.c @@ -96,7 +96,7 @@ static int sch_nr_Nref(uint32_t N_rb, srsran_mcs_table_t mcs_table, uint32_t max uint32_t N_re_lbrm = SRSRAN_MAX_NRE_NR * sch_nr_n_prb_lbrm(N_rb); double TCR_lbrm = 948.0 / 1024.0; uint32_t Qm_lbrm = (mcs_table == srsran_mcs_table_256qam) ? 8 : 6; - uint32_t TBS_LRBM = srsran_ra_nr_tbs(N_re_lbrm, 1.0, TCR_lbrm, Qm_lbrm, max_mimo_layers); + uint32_t TBS_LRBM = srsran_ra_nr_tbs(N_re_lbrm, 1.0, TCR_lbrm, Qm_lbrm, SRSRAN_MIN(4, max_mimo_layers)); double R = 2.0 / 3.0; srsran_basegraph_t bg = srsran_sch_nr_select_basegraph(TBS_LRBM, R); @@ -150,11 +150,15 @@ int srsran_sch_nr_fill_tb_info(const srsran_carrier_nr_t* carrier, cfg->Nl = tb->N_L; // Calculate Nref - int Nref = sch_nr_Nref(carrier->nof_prb, sch_cfg->mcs_table, carrier->max_mimo_layers); - if (Nref < SRSRAN_SUCCESS) { - ERROR("Error computing N_ref"); + if (sch_cfg->limited_buffer_rm) { + int Nref = sch_nr_Nref(carrier->nof_prb, sch_cfg->mcs_table, 4); + if (Nref < SRSRAN_SUCCESS) { + ERROR("Error computing N_ref"); + } + cfg->Nref = (uint32_t)Nref; + } else { + cfg->Nref = SRSRAN_LDPC_MAX_LEN_ENCODED_CB; } - cfg->Nref = (uint32_t)Nref; // Calculate number of code blocks after applying CBGTI... not implemented, activate all CB for (uint32_t r = 0; r < cbsegm.C; r++) { diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 77c9b059b..f6ad6934a 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -585,6 +585,8 @@ add_lte_test(prach_zc0 prach_test -z 0) add_lte_test(prach_zc2 prach_test -z 2) add_lte_test(prach_zc3 prach_test -z 3) +add_nr_test(prach_nr prach_test -n 50 -f 0 -r 0 -z 0 -N 1) + add_executable(prach_test_multi prach_test_multi.c) target_link_libraries(prach_test_multi srsran_phy) diff --git a/lib/src/phy/phch/test/prach_test.c b/lib/src/phy/phch/test/prach_test.c index 133c85ba3..d569c3b7c 100644 --- a/lib/src/phy/phch/test/prach_test.c +++ b/lib/src/phy/phch/test/prach_test.c @@ -32,6 +32,7 @@ #define MAX_LEN 70176 +static bool is_nr = false; static uint32_t nof_prb = 50; static uint32_t config_idx = 3; static uint32_t root_seq_idx = 0; @@ -45,12 +46,13 @@ static void usage(char* prog) printf("\t-f Preamble format [Default 0]\n"); printf("\t-r Root sequence index [Default 0]\n"); printf("\t-z Zero correlation zone config [Default 1]\n"); + printf("\t-N Toggle LTE/NR operation, zero for LTE, non-zero for NR [Default %s]\n", is_nr ? "NR" : "LTE"); } static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "nfrz")) != -1) { + while ((opt = getopt(argc, argv, "nfrzN")) != -1) { switch (opt) { case 'n': nof_prb = (uint32_t)strtol(argv[optind], NULL, 10); @@ -64,6 +66,9 @@ static void parse_args(int argc, char** argv) case 'z': zero_corr_zone = (uint32_t)strtol(argv[optind], NULL, 10); break; + case 'N': + is_nr = (uint32_t)strtol(argv[optind], NULL, 10) > 0; + break; default: usage(argv[0]); exit(-1); @@ -83,6 +88,7 @@ int main(int argc, char** argv) srsran_prach_cfg_t prach_cfg; ZERO_OBJECT(prach_cfg); + prach_cfg.is_nr = is_nr; prach_cfg.config_idx = config_idx; prach_cfg.hs_flag = high_speed_flag; prach_cfg.freq_offset = 0; diff --git a/lib/src/phy/phch/test/pusch_nr_test.c b/lib/src/phy/phch/test/pusch_nr_test.c index 2a8df41f2..e5ed4d0fb 100644 --- a/lib/src/phy/phch/test/pusch_nr_test.c +++ b/lib/src/phy/phch/test/pusch_nr_test.c @@ -234,10 +234,10 @@ int main(int argc, char** argv) uint8_t csi_report_tx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {}; uint8_t csi_report_rx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {}; if (nof_csi_bits > 0) { - pusch_cfg.uci.csi[0].quantity = SRSRAN_CSI_REPORT_QUANTITY_NONE; - pusch_cfg.uci.csi[0].K_csi_rs = nof_csi_bits; - pusch_cfg.uci.nof_csi = 1; - data_tx.uci.csi[0].none = csi_report_tx; + pusch_cfg.uci.csi[0].cfg.quantity = SRSRAN_CSI_REPORT_QUANTITY_NONE; + pusch_cfg.uci.csi[0].K_csi_rs = nof_csi_bits; + pusch_cfg.uci.nof_csi = 1; + data_tx.uci.csi[0].none = csi_report_tx; for (uint32_t i = 0; i < nof_csi_bits; i++) { csi_report_tx[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1); } diff --git a/lib/src/phy/phch/uci_nr.c b/lib/src/phy/phch/uci_nr.c index b7f83bde3..392880d75 100644 --- a/lib/src/phy/phch/uci_nr.c +++ b/lib/src/phy/phch/uci_nr.c @@ -308,8 +308,7 @@ static int uci_nr_A(const srsran_uci_cfg_nr_t* cfg) } // 6.3.1.1.3 HARQ-ACK/SR and CSI - ERROR("HARQ-ACK/SR and CSI encoding are not implemented"); - return SRSRAN_ERROR; + return cfg->ack.count + cfg->o_sr + o_csi; } static int uci_nr_pack_pucch(const srsran_uci_cfg_nr_t* cfg, const srsran_uci_value_nr_t* value, uint8_t* sequence) @@ -341,8 +340,7 @@ static int uci_nr_unpack_pucch(const srsran_uci_cfg_nr_t* cfg, uint8_t* sequence // 6.3.1.1.2 CSI only if (cfg->ack.count == 0 && cfg->o_sr == 0) { - ERROR("CSI only are not implemented"); - return SRSRAN_ERROR; + return srsran_csi_part1_unpack(cfg->csi, cfg->nof_csi, sequence, SRSRAN_UCI_NR_MAX_NOF_BITS, value->csi); } // 6.3.1.1.3 HARQ-ACK/SR and CSI @@ -644,9 +642,11 @@ static int uci_nr_decode_3_11_bit(srsran_uci_nr_t* q, // Compute average LLR power float pwr = srsran_vec_avg_power_bf(llr, E); + + // If the power measurement is invalid (zero, NAN, INF) then consider it cannot be decoded if (!isnormal(pwr)) { - ERROR("Received all zeros"); - return SRSRAN_ERROR; + *decoded_ok = false; + return SRSRAN_SUCCESS; } // Decode diff --git a/lib/src/phy/rf/rf_imp.c b/lib/src/phy/rf/rf_imp.c index 54c17bb76..dc6652831 100644 --- a/lib/src/phy/rf/rf_imp.c +++ b/lib/src/phy/rf/rf_imp.c @@ -190,7 +190,9 @@ int srsran_rf_close(srsran_rf_t* rf) } pthread_mutex_unlock(&rf->mutex); pthread_cond_signal(&rf->cond); - pthread_join(rf->thread_gain, NULL); + if (rf->thread_gain) { + pthread_join(rf->thread_gain, NULL); + } return ((rf_dev_t*)rf->dev)->srsran_rf_close(rf->handler); } diff --git a/lib/src/phy/rf/rf_uhd_imp.cc b/lib/src/phy/rf/rf_uhd_imp.cc index a932ab5db..8a5847884 100644 --- a/lib/src/phy/rf/rf_uhd_imp.cc +++ b/lib/src/phy/rf/rf_uhd_imp.cc @@ -19,6 +19,7 @@ * */ +#include #include #include #include @@ -164,7 +165,7 @@ struct rf_uhd_handler_t { #if HAVE_ASYNC_THREAD // Asynchronous transmission message thread - bool async_thread_running = false; + std::atomic async_thread_running{false}; std::thread async_thread; std::mutex async_mutex; std::condition_variable async_cvar; @@ -235,9 +236,12 @@ static void log_late(rf_uhd_handler_t* h, bool is_rx) #if HAVE_ASYNC_THREAD static void log_underflow(rf_uhd_handler_t* h) { - // Flag underflow - if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { - h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; + { + std::lock_guard tx_lock(h->tx_mutex); + // Flag underflow + if (h->tx_state == RF_UHD_IMP_TX_STATE_BURST) { + h->tx_state = RF_UHD_IMP_TX_STATE_END_OF_BURST; + } } if (h->uhd_error_handler != nullptr) { srsran_rf_error_t error; @@ -298,6 +302,7 @@ static void* async_thread(void* h) } } else if (event_code == uhd::async_metadata_t::EVENT_CODE_BURST_ACK) { // Makes sure next block will be start of burst + std::lock_guard tx_lock(handler->tx_mutex); if (handler->tx_state == RF_UHD_IMP_TX_STATE_WAIT_EOB_ACK) { handler->tx_state = RF_UHD_IMP_TX_STATE_START_BURST; } @@ -1032,11 +1037,12 @@ double rf_uhd_set_rx_srate(void* h, double freq) double rf_uhd_set_tx_srate(void* h, double freq) { - rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; - std::unique_lock lock(handler->tx_mutex); + rf_uhd_handler_t* handler = (rf_uhd_handler_t*)h; + // Locking order should be kept the same with the async worker. #if HAVE_ASYNC_THREAD std::unique_lock lock_async(handler->async_mutex); #endif /* HAVE_ASYNC_THREAD */ + std::unique_lock lock(handler->tx_mutex); // Early return if the current rate matches and Tx stream has been created if (freq == handler->tx_rate and handler->uhd->is_tx_ready()) { @@ -1418,6 +1424,8 @@ int rf_uhd_send_timed_multi(void* h, // Flush receiver only if allowed if (RF_UHD_IMP_PROHIBITED_EOB_FLUSH.count(handler->devname) == 0) { + // Avoid holding the tx lock while in the rf_uhd_flush_buffer function to avoid potential deadlocks. + lock.unlock(); rf_uhd_flush_buffer(h); } diff --git a/lib/src/phy/rf/rf_zmq_imp_rx.c b/lib/src/phy/rf/rf_zmq_imp_rx.c index d676c9b97..40bf4af74 100644 --- a/lib/src/phy/rf/rf_zmq_imp_rx.c +++ b/lib/src/phy/rf/rf_zmq_imp_rx.c @@ -170,7 +170,7 @@ int rf_zmq_rx_open(rf_zmq_rx_t* q, rf_zmq_opts_t opts, void* zmq_ctx, char* sock } if (zmq_setsockopt(q->sock, ZMQ_SNDTIMEO, &timeout, sizeof(timeout)) == -1) { - fprintf(stderr, "Error: setting receive timeout on rx socket\n"); + fprintf(stderr, "Error: setting send timeout on rx socket\n"); goto clean_exit; } diff --git a/lib/src/phy/rf/rf_zmq_imp_tx.c b/lib/src/phy/rf/rf_zmq_imp_tx.c index 2f548be2f..74705a18c 100644 --- a/lib/src/phy/rf/rf_zmq_imp_tx.c +++ b/lib/src/phy/rf/rf_zmq_imp_tx.c @@ -66,7 +66,7 @@ int rf_zmq_tx_open(rf_zmq_tx_t* q, rf_zmq_opts_t opts, void* zmq_ctx, char* sock } if (zmq_setsockopt(q->sock, ZMQ_SNDTIMEO, &timeout, sizeof(timeout)) == -1) { - fprintf(stderr, "Error: setting receive timeout on tx socket\n"); + fprintf(stderr, "Error: setting send timeout on tx socket\n"); goto clean_exit; } diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index 491cd2589..71b823b8b 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -567,10 +567,11 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m // Put PBCH DMRS srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; pbch_dmrs_cfg.N_id = N_id; - pbch_dmrs_cfg.n_hf = msg->hrf ? 0 : 1; + pbch_dmrs_cfg.n_hf = msg->hrf ? 1 : 0; pbch_dmrs_cfg.ssb_idx = msg->ssb_idx; pbch_dmrs_cfg.L_max = q->Lmax; pbch_dmrs_cfg.beta = 0.0f; + pbch_dmrs_cfg.scs = q->cfg.scs; if (srsran_dmrs_pbch_put(&pbch_dmrs_cfg, ssb_grid) < SRSRAN_SUCCESS) { ERROR("Error putting PBCH DMRS"); return SRSRAN_ERROR; @@ -579,7 +580,10 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m // Put PBCH payload srsran_pbch_nr_cfg_t pbch_cfg = {}; pbch_cfg.N_id = N_id; + pbch_cfg.n_hf = msg->hrf; + pbch_cfg.ssb_idx = msg->ssb_idx; pbch_cfg.Lmax = q->Lmax; + pbch_cfg.beta = 0.0f; if (srsran_pbch_nr_encode(&q->pbch, &pbch_cfg, msg, ssb_grid) < SRSRAN_SUCCESS) { ERROR("Error encoding PBCH"); return SRSRAN_ERROR; diff --git a/lib/src/phy/sync/test/CMakeLists.txt b/lib/src/phy/sync/test/CMakeLists.txt index deede8267..331e7b445 100644 --- a/lib/src/phy/sync/test/CMakeLists.txt +++ b/lib/src/phy/sync/test/CMakeLists.txt @@ -145,15 +145,29 @@ target_link_libraries(ssb_measure_test srsran_phy) add_executable(ssb_decode_test ssb_decode_test.c) target_link_libraries(ssb_decode_test srsran_phy) -# For each supported SSB subcarrier spacing -foreach (SSB_SCS 15 30) +# For 1.0 GHz and 3.5 GHz Center frequencies +foreach (CELL_FREQ 1000000 3500000) # For each supported Cell/Carrier subcarrier spacing foreach (CELL_SCS 15 30) - # Test SSB measurements - add_nr_test(ssb_measure_test_${SSB_SCS}_${CELL_SCS} ssb_measure_test -s ${SSB_SCS} -S ${CELL_SCS}) - - # Test SSB PBCH decoding - add_nr_test(ssb_decode_test_${SSB_SCS}_${CELL_SCS} ssb_decode_test -s ${SSB_SCS} -S ${CELL_SCS}) + # For SSB centered at -960, 0 and 960 kHz from the center frequency + foreach (SSB_OFFSET_FREQ -960 +0 +960) + # For each supported SSB subcarrier spacing + foreach (SSB_SCS 15 30) + # For patterns A, B, C + foreach (SSB_PATTERN A B C) + # Calculate Actual SSB center frequency + math(EXPR SSB_FREQ "${CELL_FREQ}${SSB_OFFSET_FREQ}") + + # Test SSB measurements + add_nr_test(ssb_measure_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_measure_test + -F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN}) + + # Test SSB PBCH decoding + add_nr_test(ssb_decode_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_decode_test + -F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN}) + endforeach () + endforeach () + endforeach () endforeach () endforeach () diff --git a/lib/src/phy/sync/test/ssb_decode_test.c b/lib/src/phy/sync/test/ssb_decode_test.c index c924253f0..a445b5673 100644 --- a/lib/src/phy/sync/test/ssb_decode_test.c +++ b/lib/src/phy/sync/test/ssb_decode_test.c @@ -32,7 +32,10 @@ // NR parameters static uint32_t carrier_nof_prb = 52; static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; +static double carrier_freq_hz = 3.5e9 + 960e3; static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; +static double ssb_freq_hz = 3.5e9; +static srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; // Channel parameters static cf_t wideband_gain = 1.0f + 0.5 * I; @@ -51,14 +54,17 @@ static void usage(char* prog) { printf("Usage: %s [v]\n", prog); printf("\t-s SSB subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(ssb_scs)); + printf("\t-f SSB center frequency [default, %.3f MHz]\n", ssb_freq_hz / 1e6); printf("\t-S cell/carrier subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(carrier_scs)); + printf("\t-F cell/carrier center frequency in Hz [default, %.3f MHz]\n", carrier_freq_hz / 1e6); + printf("\t-P SSB pattern [default, %s]\n", srsran_ssb_pattern_to_str(ssb_pattern)); printf("\t-v [set srsran_verbose to debug, default none]\n"); } static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "Ssv")) != -1) { + while ((opt = getopt(argc, argv, "SsFfPv")) != -1) { switch (opt) { case 's': ssb_scs = srsran_subcarrier_spacing_from_str(argv[optind]); @@ -67,6 +73,9 @@ static void parse_args(int argc, char** argv) exit(-1); } break; + case 'f': + ssb_freq_hz = strtod(argv[optind], NULL); + break; case 'S': carrier_scs = srsran_subcarrier_spacing_from_str(argv[optind]); if (carrier_scs == srsran_subcarrier_spacing_invalid) { @@ -74,6 +83,12 @@ static void parse_args(int argc, char** argv) exit(-1); } break; + case 'F': + carrier_freq_hz = strtod(argv[optind], NULL); + break; + case 'P': + ssb_pattern = srsran_ssb_pattern_fom_str(argv[optind]); + break; case 'v': srsran_verbose++; break; @@ -123,10 +138,10 @@ static int test_case_1(srsran_ssb_t* ssb) // SSB configuration srsran_ssb_cfg_t ssb_cfg = {}; ssb_cfg.srate_hz = srate_hz; - ssb_cfg.center_freq_hz = 3.5e9; - ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3; + ssb_cfg.center_freq_hz = carrier_freq_hz; + ssb_cfg.ssb_freq_hz = ssb_freq_hz; ssb_cfg.scs = ssb_scs; - ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C; + ssb_cfg.pattern = ssb_pattern; TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); diff --git a/lib/src/phy/sync/test/ssb_measure_test.c b/lib/src/phy/sync/test/ssb_measure_test.c index 7b91872ed..7c4744e4e 100644 --- a/lib/src/phy/sync/test/ssb_measure_test.c +++ b/lib/src/phy/sync/test/ssb_measure_test.c @@ -30,10 +30,13 @@ // NR parameters static uint32_t carrier_nof_prb = 52; static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; +static double carrier_freq_hz = 3.5e9 + 960e3; static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz; +static double ssb_freq_hz = 3.5e9; +static srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; // Channel parameters -static int32_t delay_n = 1; +static int32_t delay_n = 2; static float cfo_hz = 100.0f; static float n0_dB = -30.0f; @@ -46,8 +49,8 @@ static cf_t* buffer = NULL; // Base-band buffer #define RSRP_MAX_ERROR 1.0f #define EPRE_MAX_ERROR 1.0f -#define N0_MAX_ERROR 2.5f -#define SNR_MAX_ERROR 2.5f +#define N0_MAX_ERROR 3.0f +#define SNR_MAX_ERROR 3.0f #define CFO_MAX_ERROR (cfo_hz * 0.3f) #define DELAY_MAX_ERROR (delay_us * 0.1f) @@ -55,14 +58,18 @@ static void usage(char* prog) { printf("Usage: %s [v]\n", prog); printf("\t-s SSB subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(ssb_scs)); + printf("\t-f SSB center frequency [default, %.3f MHz]\n", ssb_freq_hz / 1e6); printf("\t-S cell/carrier subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(carrier_scs)); + printf("\t-F cell/carrier center frequency in Hz [default, %.3f MHz]\n", carrier_freq_hz / 1e6); + printf("\t-P SSB pattern [default, %s]\n", srsran_ssb_pattern_to_str(ssb_pattern)); printf("\t-v [set srsran_verbose to debug, default none]\n"); } + static void parse_args(int argc, char** argv) { int opt; - while ((opt = getopt(argc, argv, "Ssv")) != -1) { + while ((opt = getopt(argc, argv, "SsFfPv")) != -1) { switch (opt) { case 's': ssb_scs = srsran_subcarrier_spacing_from_str(argv[optind]); @@ -71,6 +78,9 @@ static void parse_args(int argc, char** argv) exit(-1); } break; + case 'f': + ssb_freq_hz = strtod(argv[optind], NULL); + break; case 'S': carrier_scs = srsran_subcarrier_spacing_from_str(argv[optind]); if (carrier_scs == srsran_subcarrier_spacing_invalid) { @@ -78,6 +88,12 @@ static void parse_args(int argc, char** argv) exit(-1); } break; + case 'F': + carrier_freq_hz = strtod(argv[optind], NULL); + break; + case 'P': + ssb_pattern = srsran_ssb_pattern_fom_str(argv[optind]); + break; case 'v': srsran_verbose++; break; @@ -123,10 +139,10 @@ static int test_case_1(srsran_ssb_t* ssb) // SSB configuration srsran_ssb_cfg_t ssb_cfg = {}; ssb_cfg.srate_hz = srate_hz; - ssb_cfg.center_freq_hz = 3.5e9; - ssb_cfg.ssb_freq_hz = 3.5e9 - 960e3; + ssb_cfg.center_freq_hz = carrier_freq_hz; + ssb_cfg.ssb_freq_hz = ssb_freq_hz; ssb_cfg.scs = ssb_scs; - ssb_cfg.pattern = SRSRAN_SSB_PATTERN_C; + ssb_cfg.pattern = ssb_pattern; TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); @@ -228,6 +244,7 @@ int main(int argc, char** argv) if (test_case_1(&ssb) != SRSRAN_SUCCESS) { ERROR("test case failed"); + goto clean_exit; } ret = SRSRAN_SUCCESS; diff --git a/lib/src/phy/ue/ue_dl_nr.c b/lib/src/phy/ue/ue_dl_nr.c index 6658c2db5..4ad3e8ed8 100644 --- a/lib/src/phy/ue/ue_dl_nr.c +++ b/lib/src/phy/ue/ue_dl_nr.c @@ -384,7 +384,7 @@ static int ue_dl_nr_find_dci_ss(srsran_ue_dl_nr_t* q, // Calculate possible PDCCH DCI candidates uint32_t candidates[SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR] = {}; int nof_candidates = srsran_pdcch_nr_locations_coreset( - coreset, search_space, rnti, L, SRSRAN_SLOT_NR_MOD(q->carrier.scs, slot_cfg->idx), candidates); + coreset, search_space, rnti, L, SRSRAN_SLOT_NR_MOD(q->carrier.scs, slot_cfg->idx), candidates); if (nof_candidates < SRSRAN_SUCCESS) { ERROR("Error calculating DCI candidate location"); return SRSRAN_ERROR; @@ -585,19 +585,19 @@ int srsran_ue_dl_nr_decode_pdsch(srsran_ue_dl_nr_t* q, return SRSRAN_SUCCESS; } -int srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, - const srsran_sch_cfg_nr_t* cfg, - const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS], - char* str, - uint32_t str_len) +uint32_t srsran_ue_dl_nr_pdsch_info(const srsran_ue_dl_nr_t* q, + const srsran_sch_cfg_nr_t* cfg, + const srsran_pdsch_res_nr_t res[SRSRAN_MAX_CODEWORDS], + char* str, + uint32_t str_len) { - int len = 0; + uint32_t len = 0; // Append PDSCH info len += srsran_pdsch_nr_rx_info(&q->pdsch, cfg, &cfg->grant, res, &str[len], str_len - len); // Append channel estimator info - len = srsran_print_check(str, str_len, len, "SNR=%+.1f", q->chest.snr_db); + len += srsran_csi_meas_info_short(&q->dmrs_pdsch.csi, &str[len], str_len - len); return len; } diff --git a/lib/src/phy/ue/ue_ul_nr.c b/lib/src/phy/ue/ue_ul_nr.c index ffdc33a28..e49509b96 100644 --- a/lib/src/phy/ue/ue_ul_nr.c +++ b/lib/src/phy/ue/ue_ul_nr.c @@ -81,7 +81,7 @@ int srsran_ue_ul_nr_set_carrier(srsran_ue_ul_nr_t* q, const srsran_carrier_nr_t* fft_cfg.nof_prb = carrier->nof_prb; fft_cfg.symbol_sz = srsran_min_symbol_sz_rb(carrier->nof_prb); fft_cfg.keep_dc = true; - fft_cfg.phase_compensation_hz = carrier->dl_center_frequency_hz; + fft_cfg.phase_compensation_hz = carrier->ul_center_frequency_hz; if (srsran_ofdm_tx_init_cfg(&q->ifft, &fft_cfg) < SRSRAN_SUCCESS) { ERROR("Initiating OFDM"); return SRSRAN_ERROR; diff --git a/lib/src/radio/radio.cc b/lib/src/radio/radio.cc index e18d14c41..995dd97a8 100644 --- a/lib/src/radio/radio.cc +++ b/lib/src/radio/radio.cc @@ -714,7 +714,7 @@ void radio::set_rx_srate(const double& srate) // Assert ratio is integer srsran_assert(((uint32_t)cur_rx_srate % (uint32_t)srate) == 0, - "The sampling rate ratio is not integer (%.2f MHz / %.2 MHz = %.3f)", + "The sampling rate ratio is not integer (%.2f MHz / %.2f MHz = %.3f)", cur_rx_srate / 1e6, srate / 1e6, cur_rx_srate / srate); @@ -967,7 +967,7 @@ void radio::set_tx_srate(const double& srate) // Assert ratio is integer srsran_assert(((uint32_t)cur_tx_srate % (uint32_t)srate) == 0, - "The sampling rate ratio is not integer (%.2f MHz / %.2 MHz = %.3f)", + "The sampling rate ratio is not integer (%.2f MHz / %.2f MHz = %.3f)", cur_rx_srate / 1e6, srate / 1e6, cur_rx_srate / srate); diff --git a/lib/src/support/CMakeLists.txt b/lib/src/support/CMakeLists.txt new file mode 100644 index 000000000..0be443c35 --- /dev/null +++ b/lib/src/support/CMakeLists.txt @@ -0,0 +1,12 @@ +# +# Copyright 2013-2021 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. +# + +set(SOURCES emergency_handlers.cc + signal_handler.cc) + +add_library(support STATIC ${SOURCES}) diff --git a/lib/src/support/emergency_handlers.cc b/lib/src/support/emergency_handlers.cc new file mode 100644 index 000000000..b4e2cdc9a --- /dev/null +++ b/lib/src/support/emergency_handlers.cc @@ -0,0 +1,59 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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/support/emergency_handlers.h" +#include "srsran/support/srsran_assert.h" + +namespace { + +/// Holds the callback function pointer and the associated user provided data pointer. +struct handler_instance { + std::atomic data{}; + std::atomic callback{}; +}; + +} // namespace + +// Handlers are added in a thread safe manner without using locks to avoid possible issues if a signal is emitted while +// modifying the callback array. +static constexpr unsigned max_handlers = 12; +static handler_instance registered_handlers[max_handlers]; +static std::atomic num_handlers; + +void add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data) +{ + // Reserve a slot in the array. + auto pos = num_handlers.fetch_add(1); + + // Check if we have space in the array. + if (pos >= max_handlers) { + srsran_assert(0, "Exceeded the emergency cleanup handler registered limit"); + return; + } + + // Order is important here: write last the callback member as it is used to signal that the handler is valid when + // reading the array. + registered_handlers[pos].data.store(data); + registered_handlers[pos].callback.store(callback); +} + +void execute_emergency_cleanup_handlers() +{ + for (unsigned i = 0, e = num_handlers; i != e; ++i) { + auto callback = registered_handlers[i].callback.load(); + // Test the validity of the callback as it may have not been written yet into the array even if num_callbacks has + // been updated. + if (callback) { + callback(registered_handlers[i].data.load()); + } + } +} diff --git a/lib/src/support/signal_handler.cc b/lib/src/support/signal_handler.cc new file mode 100644 index 000000000..33620d6cc --- /dev/null +++ b/lib/src/support/signal_handler.cc @@ -0,0 +1,54 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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/support/signal_handler.h" +#include "srsran/support/emergency_handlers.h" +#include +#include +#include +#include + +#define SRSRAN_TERM_TIMEOUT_S (5) + +/// Handler called after the user interrupts the program. +static std::atomic user_handler; + +static void srsran_signal_handler(int signal) +{ + switch (signal) { + case SIGALRM: + fprintf(stderr, "Couldn't stop after %ds. Forcing exit.\n", SRSRAN_TERM_TIMEOUT_S); + execute_emergency_cleanup_handlers(); + raise(SIGKILL); + default: + // all other registered signals try to stop the app gracefully + // Call the user handler if present and remove it so that further signals are treated by the default handler. + if (auto handler = user_handler.exchange(nullptr)) { + handler(); + } else { + return; + } + fprintf(stdout, "Stopping ..\n"); + alarm(SRSRAN_TERM_TIMEOUT_S); + break; + } +} + +void srsran_register_signal_handler(srsran_signal_hanlder handler) +{ + user_handler.store(handler); + + signal(SIGINT, srsran_signal_handler); + signal(SIGTERM, srsran_signal_handler); + signal(SIGHUP, srsran_signal_handler); + signal(SIGALRM, srsran_signal_handler); +} diff --git a/lib/test/mac/mac_pdu_nr_test.cc b/lib/test/mac/mac_pdu_nr_test.cc index 2cf9a6024..8442382bf 100644 --- a/lib/test/mac/mac_pdu_nr_test.cc +++ b/lib/test/mac/mac_pdu_nr_test.cc @@ -525,6 +525,90 @@ int mac_ul_sch_pdu_unpack_and_pack_test3() return SRSRAN_SUCCESS; } +int mac_ul_sch_pdu_unpack_and_pack_test4() +{ + // MAC PDU with UL-SCH with C-RNTI, Long BSR (all zeros) and padding + uint8_t mac_ul_sch_pdu_1[] = {0x3a, 0x46, 0x01, 0x3e, 0x01, 0x00, 0x3f, 0x21, 0x21, 0x21, 0x21}; + const uint16_t TV_CRNTI = 0x4601; + + // only LBSR with only one LCG (LCG7 leftmost bit) reporting buffer state + uint8_t mac_ul_sch_pdu_2[] = {0x3e, 0x02, 0x80, 0xab}; + + // only LBSR with only two LCG (LCG7 leftmost bit. LCG0) reporting buffer state + uint8_t mac_ul_sch_pdu_3[] = {0x3e, 0x03, 0x81, 0xab, 0xcd}; + + if (pcap_handle) { + pcap_handle->write_ul_crnti_nr(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1), PCAP_CRNTI, true, PCAP_TTI); + pcap_handle->write_ul_crnti_nr(mac_ul_sch_pdu_2, sizeof(mac_ul_sch_pdu_2), PCAP_CRNTI, true, PCAP_TTI); + pcap_handle->write_ul_crnti_nr(mac_ul_sch_pdu_3, sizeof(mac_ul_sch_pdu_3), PCAP_CRNTI, true, PCAP_TTI); + } + + // pretty print PDU + fmt::memory_buffer buff; + auto& mac_logger = srslog::fetch_basic_logger("MAC"); + + // first TV + srsran::mac_sch_pdu_nr pdu(true); + pdu.unpack(mac_ul_sch_pdu_1, sizeof(mac_ul_sch_pdu_1)); + pdu.to_string(buff); + mac_logger.info("Rx PDU: %s", srsran::to_c_str(buff)); + + TESTASSERT(pdu.get_num_subpdus() == 3); + + // 1st subPDU is C-RNTI CE + mac_sch_subpdu_nr subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_total_length() == 3); + TESTASSERT(subpdu.get_sdu_length() == 2); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::CRNTI); + TESTASSERT(subpdu.get_c_rnti() == TV_CRNTI); + + // 2nd subPDU is LBSR + subpdu = pdu.get_subpdu(1); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::LONG_BSR); + mac_sch_subpdu_nr::lbsr_t lbsr = subpdu.get_lbsr(); + TESTASSERT(lbsr.list.size() == 0); + + // 3rd is padding + subpdu = pdu.get_subpdu(2); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::PADDING); + + // TODO: pack again and test + + // 2nd TV + pdu.init_rx(true); + pdu.unpack(mac_ul_sch_pdu_2, sizeof(mac_ul_sch_pdu_2)); + buff.clear(); + pdu.to_string(buff); + mac_logger.info("Rx PDU: %s", srsran::to_c_str(buff)); + + TESTASSERT(pdu.get_num_subpdus() == 1); + subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::LONG_BSR); + lbsr = subpdu.get_lbsr(); + TESTASSERT(lbsr.list.size() == 1); + TESTASSERT(lbsr.list.at(0).lcg_id == 7); + TESTASSERT(lbsr.list.at(0).buffer_size == 0xab); + + // 3nd TV + pdu.init_rx(true); + pdu.unpack(mac_ul_sch_pdu_3, sizeof(mac_ul_sch_pdu_3)); + buff.clear(); + pdu.to_string(buff); + mac_logger.info("Rx PDU: %s", srsran::to_c_str(buff)); + + TESTASSERT(pdu.get_num_subpdus() == 1); + subpdu = pdu.get_subpdu(0); + TESTASSERT(subpdu.get_lcid() == mac_sch_subpdu_nr::LONG_BSR); + lbsr = subpdu.get_lbsr(); + TESTASSERT(lbsr.list.size() == 2); + TESTASSERT(lbsr.list.at(0).lcg_id == 0); + TESTASSERT(lbsr.list.at(0).buffer_size == 0xab); + TESTASSERT(lbsr.list.at(1).lcg_id == 7); + TESTASSERT(lbsr.list.at(1).buffer_size == 0xcd); + + return SRSRAN_SUCCESS; +} + int mac_ul_sch_pdu_pack_test4() { // MAC PDU with UL-SCH (with normal LCID) subheader for long SDU @@ -780,6 +864,11 @@ int main(int argc, char** argv) return SRSRAN_ERROR; } + if (mac_ul_sch_pdu_unpack_and_pack_test4()) { + fprintf(stderr, "mac_ul_sch_pdu_unpack_and_pack_test4() failed.\n"); + return SRSRAN_ERROR; + } + if (mac_ul_sch_pdu_pack_test4()) { fprintf(stderr, "mac_ul_sch_pdu_pack_test4() failed.\n"); return SRSRAN_ERROR; diff --git a/lib/test/phy/CMakeLists.txt b/lib/test/phy/CMakeLists.txt index 51e330b7c..a07f561ec 100644 --- a/lib/test/phy/CMakeLists.txt +++ b/lib/test/phy/CMakeLists.txt @@ -82,7 +82,7 @@ foreach(rb 25 52 79 106 133 160 216 270) add_nr_test(phy_dl_nr_test_${rb}prb_interleaved phy_dl_nr_test -P ${rb} -p 25 -m 10 -I) # Maximum throughput with 256QAM - add_nr_test(phy_dl_nr_test_${rb}prb_256qam phy_dl_nr_test -P ${rb} -p ${rb} -m 27 -T 256qam -v -d 1 1 -n 10) + add_nr_test(phy_dl_nr_test_${rb}prb_256qam phy_dl_nr_test -P ${rb} -p ${rb} -m 27 -T 256qam -v -d 1 1 1 -n 10) # Maximum throughput with 64QAM and CFO+Delay impairments add_nr_test(phy_dl_nr_test_${rb}prb_cfo_delay phy_dl_nr_test -P ${rb} -p ${rb} -m 27 -C 100.0 -D 4 -n 10) diff --git a/lib/test/phy/phy_dl_nr_test.c b/lib/test/phy/phy_dl_nr_test.c index dffd0efdb..8a9af9ab0 100644 --- a/lib/test/phy/phy_dl_nr_test.c +++ b/lib/test/phy/phy_dl_nr_test.c @@ -28,17 +28,18 @@ #include "srsran/phy/utils/vector.h" #include -static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; -static uint32_t n_prb = 0; // Set to 0 for steering -static uint32_t mcs = 30; // Set to 30 for steering -static srsran_sch_cfg_nr_t pdsch_cfg = {}; -static uint32_t nof_slots = 10; -static uint32_t rv_idx = 0; -static uint32_t delay_n = 0; // Integer delay -static float cfo_hz = 0.0f; // CFO Hz -static srsran_dmrs_sch_type_t dmrs_type = srsran_dmrs_sch_type_1; -static srsran_dmrs_sch_add_pos_t dmrs_add_pos = srsran_dmrs_sch_add_pos_2; -static bool interleaved_pdcch = false; +static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; +static uint32_t n_prb = 0; // Set to 0 for steering +static uint32_t mcs = 30; // Set to 30 for steering +static srsran_sch_cfg_nr_t pdsch_cfg = {}; +static uint32_t nof_slots = 10; +static uint32_t rv_idx = 0; +static uint32_t delay_n = 0; // Integer delay +static float cfo_hz = 0.0f; // CFO Hz +static srsran_dmrs_sch_type_t dmrs_type = srsran_dmrs_sch_type_1; +static srsran_dmrs_sch_add_pos_t dmrs_add_pos = srsran_dmrs_sch_add_pos_2; +static bool interleaved_pdcch = false; +static uint32_t nof_dmrs_cdm_groups_without_data = 1; static void usage(char* prog) { @@ -88,7 +89,7 @@ static int parse_args(int argc, char** argv) dmrs_type = srsran_dmrs_sch_type_2; break; } - switch (strtol(argv[optind], NULL, 10)) { + switch (strtol(argv[optind++], NULL, 10)) { case 0: dmrs_add_pos = srsran_dmrs_sch_add_pos_0; break; @@ -102,6 +103,7 @@ static int parse_args(int argc, char** argv) dmrs_add_pos = srsran_dmrs_sch_add_pos_3; break; } + nof_dmrs_cdm_groups_without_data = (uint32_t)strtol(argv[optind], NULL, 10); break; case 'T': pdsch_cfg.sch_cfg.mcs_table = srsran_mcs_table_from_str(argv[optind]); @@ -366,7 +368,7 @@ int main(int argc, char** argv) pdsch_cfg.grant.L = 13; pdsch_cfg.grant.nof_layers = carrier.max_mimo_layers; pdsch_cfg.grant.dci_format = srsran_dci_format_nr_1_0; - pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; + pdsch_cfg.grant.nof_dmrs_cdm_groups_without_data = nof_dmrs_cdm_groups_without_data; pdsch_cfg.grant.beta_dmrs = srsran_convert_dB_to_amplitude(3); pdsch_cfg.grant.rnti_type = srsran_rnti_type_c; pdsch_cfg.grant.rnti = 0x4601; diff --git a/srsenb/enb.conf.example b/srsenb/enb.conf.example index bbc402425..f54c038ef 100644 --- a/srsenb/enb.conf.example +++ b/srsenb/enb.conf.example @@ -31,12 +31,12 @@ n_prb = 50 #nof_ports = 2 ##################################################################### -# eNB configuration files +# eNB configuration files # -# sib_config: SIB1, SIB2 and SIB3 configuration file +# sib_config: SIB1, SIB2 and SIB3 configuration file # note: when enabling mbms, use the sib.conf.mbsfn configuration file which includes SIB13 -# rr_config: Radio Resources configuration file -# rb_config: SRB/DRB configuration file +# rr_config: Radio Resources configuration file +# rb_config: SRB/DRB configuration file ##################################################################### [enb_files] sib_config = sib.conf @@ -47,7 +47,7 @@ rb_config = rb.conf # RF configuration # # dl_earfcn: EARFCN code for DL (only valid if a single cell is configured in rr.conf) -# tx_gain: Transmit gain (dB). +# tx_gain: Transmit gain (dB). # rx_gain: Optional receive gain (dB). If disabled, AGC if enabled # # Optional parameters: @@ -86,24 +86,24 @@ rx_gain = 40 ##################################################################### # Packet capture configuration # -# MAC-layer packets are captured to file a the compact format decoded -# by the Wireshark. For decoding, use the UDP dissector and the UDP -# heuristic dissection. Edit the preferences (Edit > Preferences > -# Protocols > DLT_USER) for DLT_USER to add an entry for DLT=149 with +# MAC-layer packets are captured to file a the compact format decoded +# by the Wireshark. For decoding, use the UDP dissector and the UDP +# heuristic dissection. Edit the preferences (Edit > Preferences > +# Protocols > DLT_USER) for DLT_USER to add an entry for DLT=149 with # Protocol=udp. Further, enable the heuristic dissection in UDP under: # Analyze > Enabled Protocols > MAC-LTE > mac_lte_udp and MAC-NR > mac_nr_udp # For more information see: https://wiki.wireshark.org/MAC-LTE -# Configuring this Wireshark preferences is needed for decoding the MAC PCAP -# files as well as for the live network capture option. +# Configuring this Wireshark preferences is needed for decoding the MAC PCAP +# files as well as for the live network capture option. # # Please note that this setting will by default only capture MAC # frames on dedicated channels, and not SIB. You have to build with # WRITE_SIB_PCAP enabled in srsenb/src/stack/mac/mac.cc if you want # SIB to be part of the MAC pcap file. # -# S1AP Packets are captured to file in the compact format decoded by -# the Wireshark s1ap dissector and with DLT 150. -# To use the dissector, edit the preferences for DLT_USER to +# S1AP Packets are captured to file in the compact format decoded by +# the Wireshark s1ap dissector and with DLT 150. +# To use the dissector, edit the preferences for DLT_USER to # add an entry with DLT=150, Payload Protocol=s1ap. # # mac_enable: Enable MAC layer packet captures (true/false) @@ -166,10 +166,10 @@ enable = false # max_aggr_level: Optional maximum aggregation level index (l=log2(L) can be 0, 1, 2 or 3) # adaptive_aggr_level: Boolean flag to enable/disable adaptive aggregation level based on target BLER # pdsch_mcs: Optional fixed PDSCH MCS (ignores reported CQIs if specified) -# pdsch_max_mcs: Optional PDSCH MCS limit +# pdsch_max_mcs: Optional PDSCH MCS limit # pusch_mcs: Optional fixed PUSCH MCS (ignores reported CQIs if specified) -# pusch_max_mcs: Optional PUSCH MCS limit -# min_nof_ctrl_symbols: Minimum number of control symbols +# pusch_max_mcs: Optional PUSCH MCS limit +# min_nof_ctrl_symbols: Minimum number of control symbols # max_nof_ctrl_symbols: Maximum number of control symbols # pucch_multiplex_enable: Allow PUCCH HARQ to collide with PUSCH and other PUCCH # pucch_harq_max_rb: Maximum number of RB to be used for PUCCH on the edges of the grid. @@ -186,6 +186,8 @@ enable = false # init_dl_cqi: DL CQI value used before any CQI report is available to the eNB # max_sib_coderate: Upper bound on SIB and RAR grants coderate # pdcch_cqi_offset: CQI offset in derivation of PDCCH aggregation level +# nr_pdsch_mcs: Optional fixed NR PDSCH MCS (ignores reported CQIs if specified) +# nr_pusch_mcs: Optional fixed NR PUSCH MCS (ignores reported CQIs if specified) # ##################################################################### [scheduler] @@ -213,6 +215,8 @@ enable = false #init_dl_cqi=5 #max_sib_coderate=0.3 #pdcch_cqi_offset=0 +#nr_pdsch_mcs=28 +#nr_pusch_mcs=28 ##################################################################### # eMBMS configuration options @@ -324,10 +328,11 @@ enable = false ##################################################################### # Expert configuration options # -# pusch_max_its: Maximum number of turbo decoder iterations (Default 4) +# pusch_max_its: Maximum number of turbo decoder iterations for LTE (Default 4) +# nr_pusch_max_its: Maximum number of LDPC iterations for NR (Default 10) # pusch_8bit_decoder: Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental) # nof_phy_threads: Selects the number of PHY threads (maximum 4, minimum 1, default 3) -# metrics_period_secs: Sets the period at which metrics are requested from the eNB. +# metrics_period_secs: Sets the period at which metrics are requested from the eNB. # metrics_csv_enable: Write eNB metrics to CSV file. # metrics_csv_filename: File path to use for CSV metrics. # report_json_enable: Write eNB report to JSON file (default disabled) @@ -358,6 +363,7 @@ enable = false ##################################################################### [expert] #pusch_max_its = 8 # These are half iterations +#nr_pusch_max_its = 10 #pusch_8bit_decoder = false #nof_phy_threads = 3 #metrics_period_secs = 1 diff --git a/srsenb/hdr/enb.h b/srsenb/hdr/enb.h index e217715f0..479e28089 100644 --- a/srsenb/hdr/enb.h +++ b/srsenb/hdr/enb.h @@ -33,15 +33,14 @@ #include #include "phy/phy.h" -#include "x2_adapter.h" #include "srsran/radio/radio.h" #include "srsenb/hdr/phy/enb_phy_base.h" #include "srsenb/hdr/stack/enb_stack_base.h" -#include "srsenb/hdr/stack/rrc/nr/rrc_config_nr.h" #include "srsenb/hdr/stack/rrc/rrc_config.h" +#include "srsenb/hdr/stack/gnb_stack_nr.h" #include "srsenb/hdr/stack/mac/sched_interface.h" #include "srsran/common/bcd_helpers.h" #include "srsran/common/buffer_pool.h" @@ -51,6 +50,7 @@ #include "srsran/interfaces/enb_command_interface.h" #include "srsran/interfaces/enb_metrics_interface.h" #include "srsran/interfaces/enb_time_interface.h" +#include "srsran/interfaces/enb_x2_interfaces.h" #include "srsran/interfaces/ue_interfaces.h" #include "srsran/srslog/srslog.h" #include "srsran/system/sys_metrics_processor.h" @@ -121,6 +121,7 @@ struct all_args_t { general_args_t general; phy_args_t phy; stack_args_t stack; + gnb_stack_args_t nr_stack; }; struct rrc_cfg_t; @@ -170,7 +171,7 @@ private: rrc_nr_cfg_t rrc_nr_cfg = {}; // eNB components - x2_adapter x2; + std::unique_ptr x2; std::unique_ptr eutra_stack = nullptr; std::unique_ptr nr_stack = nullptr; std::unique_ptr radio = nullptr; diff --git a/srsenb/hdr/phy/nr/slot_worker.h b/srsenb/hdr/phy/nr/slot_worker.h index 86102efb8..4928c8719 100644 --- a/srsenb/hdr/phy/nr/slot_worker.h +++ b/srsenb/hdr/phy/nr/slot_worker.h @@ -59,14 +59,15 @@ public: }; struct args_t { - uint32_t cell_index = 0; - uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR; - uint32_t nof_tx_ports = 1; - uint32_t nof_rx_ports = 1; - uint32_t rf_port = 0; - srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz; - uint32_t pusch_max_nof_iter = 10; - double srate_hz = 0.0; + uint32_t cell_index = 0; + uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR; + uint32_t nof_tx_ports = 1; + uint32_t nof_rx_ports = 1; + uint32_t rf_port = 0; + srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_15kHz; + uint32_t pusch_max_its = 10; + float pusch_min_snr_dB = -10.0f; + double srate_hz = 0.0; }; slot_worker(srsran::phy_common_interface& common_, diff --git a/srsenb/hdr/phy/nr/worker_pool.h b/srsenb/hdr/phy/nr/worker_pool.h index 66dd44251..852199eed 100644 --- a/srsenb/hdr/phy/nr/worker_pool.h +++ b/srsenb/hdr/phy/nr/worker_pool.h @@ -107,12 +107,13 @@ private: public: struct args_t { - double srate_hz = 0.0; - uint32_t nof_phy_threads = 3; - uint32_t nof_prach_workers = 0; - uint32_t prio = 52; - uint32_t pusch_max_nof_iter = 10; - srsran::phy_log_args_t log = {}; + double srate_hz = 0.0; + uint32_t nof_phy_threads = 3; + uint32_t nof_prach_workers = 0; + uint32_t prio = 52; + uint32_t pusch_max_its = 10; + float pusch_min_snr_dB = -10; + srsran::phy_log_args_t log = {}; }; slot_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } diff --git a/srsenb/hdr/phy/phy.h b/srsenb/hdr/phy/phy.h index 9e176381b..b4a8ca618 100644 --- a/srsenb/hdr/phy/phy.h +++ b/srsenb/hdr/phy/phy.h @@ -47,13 +47,13 @@ public: int init(const phy_args_t& args, const phy_cfg_t& cfg, srsran::radio_interface_phy* radio_, - stack_interface_phy_lte* stack_, + stack_interface_phy_lte* stack_lte_, + stack_interface_phy_nr& stack_nr_, enb_time_interface* enb_); int init(const phy_args_t& args, const phy_cfg_t& cfg, srsran::radio_interface_phy* radio_, - stack_interface_phy_lte* stack_lte_, - stack_interface_phy_nr& stack_nr_, + stack_interface_phy_lte* stack_, enb_time_interface* enb_); void stop() override; @@ -81,7 +81,6 @@ public: void srsran_phy_logger(phy_logger_level_t log_level, char* str); - int init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_phy_nr& stack); int set_common_cfg(const common_cfg_t& common_cfg) override; private: @@ -112,6 +111,12 @@ private: common_cfg_t common_cfg = {}; void parse_common_config(const phy_cfg_t& cfg); + int init_lte(const phy_args_t& args, + const phy_cfg_t& cfg, + srsran::radio_interface_phy* radio_, + stack_interface_phy_lte* stack_, + enb_time_interface* enb_); + int init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_phy_nr& stack); }; } // namespace srsenb diff --git a/srsenb/hdr/phy/phy_common.h b/srsenb/hdr/phy/phy_common.h index 24fd3a6b2..3410b9215 100644 --- a/srsenb/hdr/phy/phy_common.h +++ b/srsenb/hdr/phy/phy_common.h @@ -71,9 +71,9 @@ public: // Common objects phy_args_t params = {}; - uint32_t get_nof_carriers_lte() { return static_cast(cell_list_lte.size()); }; - uint32_t get_nof_carriers_nr() { return static_cast(cell_list_nr.size()); }; - uint32_t get_nof_carriers() { return static_cast(cell_list_lte.size() + cell_list_nr.size()); }; + uint32_t get_nof_carriers_lte() { return static_cast(cell_list_lte.size()); } + uint32_t get_nof_carriers_nr() { return static_cast(cell_list_nr.size()); } + uint32_t get_nof_carriers() { return static_cast(cell_list_lte.size() + cell_list_nr.size()); } uint32_t get_nof_prb(uint32_t cc_idx) { uint32_t ret = 0; @@ -93,7 +93,7 @@ public: } } return ret; - }; + } uint32_t get_nof_ports(uint32_t cc_idx) { uint32_t ret = 0; @@ -106,7 +106,7 @@ public: } return ret; - }; + } uint32_t get_nof_rf_channels() { uint32_t count = 0; @@ -135,7 +135,7 @@ public: } return ret; - }; + } double get_dl_freq_hz(uint32_t cc_idx) { double ret = 0.0; @@ -150,7 +150,7 @@ public: } return ret; - }; + } uint32_t get_rf_port(uint32_t cc_idx) { uint32_t ret = 0; @@ -165,7 +165,7 @@ public: } return ret; - }; + } srsran_cell_t get_cell(uint32_t cc_idx) { srsran_cell_t c = {}; @@ -173,7 +173,7 @@ public: c = cell_list_lte[cc_idx].cell; } return c; - }; + } void set_cell_gain(uint32_t cell_id, float gain_db) { @@ -183,6 +183,7 @@ public: // Check if the lte cell was found; if (it_lte != cell_list_lte.end()) { + std::lock_guard lock(cell_gain_mutex); it_lte->gain_db = gain_db; return; } @@ -193,6 +194,7 @@ public: // Check if the nr cell was found; if (it_nr != cell_list_nr.end()) { + std::lock_guard lock(cell_gain_mutex); it_nr->gain_db = gain_db; return; } @@ -202,6 +204,7 @@ public: float get_cell_gain(uint32_t cc_idx) { + std::lock_guard lock(cell_gain_mutex); if (cc_idx < cell_list_lte.size()) { return cell_list_lte.at(cc_idx).gain_db; } @@ -244,6 +247,7 @@ private: phy_cell_cfg_list_t cell_list_lte; phy_cell_cfg_list_nr_t cell_list_nr; + std::mutex cell_gain_mutex; bool have_mtch_stop = false; pthread_mutex_t mtch_mutex = {}; diff --git a/srsenb/hdr/phy/phy_interfaces.h b/srsenb/hdr/phy/phy_interfaces.h index dbb3cbfd3..397448440 100644 --- a/srsenb/hdr/phy/phy_interfaces.h +++ b/srsenb/hdr/phy/phy_interfaces.h @@ -63,7 +63,8 @@ struct phy_args_t { srsran::phy_log_args_t log; float max_prach_offset_us = 10; - int pusch_max_its = 10; + uint32_t pusch_max_its = 10; + uint32_t nr_pusch_max_its = 10; bool pusch_8bit_decoder = false; float tx_amplitude = 1.0f; uint32_t nof_phy_threads = 1; diff --git a/srsenb/hdr/stack/enb_stack_lte.h b/srsenb/hdr/stack/enb_stack_lte.h index bfe9263fe..b62fe95e3 100644 --- a/srsenb/hdr/stack/enb_stack_lte.h +++ b/srsenb/hdr/stack/enb_stack_lte.h @@ -135,6 +135,10 @@ public: // Note: RRC processes activity asynchronously, so there is no need to use x2_task_queue rrc.set_activity_user(eutra_rnti); } + void sgnb_release_ack(uint16_t eutra_rnti) final + { + x2_task_queue.push([this, eutra_rnti]() { rrc.sgnb_release_ack(eutra_rnti); }); + } // gtpu_interface_pdcp void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu); diff --git a/srsenb/hdr/stack/gnb_stack_nr.h b/srsenb/hdr/stack/gnb_stack_nr.h index 790cc00da..aa2fa1c92 100644 --- a/srsenb/hdr/stack/gnb_stack_nr.h +++ b/srsenb/hdr/stack/gnb_stack_nr.h @@ -38,6 +38,11 @@ namespace srsenb { +struct gnb_stack_args_t { + stack_log_args_t log; + mac_nr_args_t mac; +}; + class gnb_stack_nr final : public srsenb::enb_stack_base, public stack_interface_phy_nr, public stack_interface_mac, @@ -50,10 +55,10 @@ public: explicit gnb_stack_nr(srslog::sink& log_sink); ~gnb_stack_nr() final; - int init(const srsenb::stack_args_t& args_, - const rrc_nr_cfg_t& rrc_cfg_, - phy_interface_stack_nr* phy_, - x2_interface* x2_); + int init(const gnb_stack_args_t& args_, + const rrc_nr_cfg_t& rrc_cfg_, + phy_interface_stack_nr* phy_, + x2_interface* x2_); // eNB stack base interface void stop() final; @@ -85,14 +90,19 @@ public: // X2 interface // control plane, i.e. rrc_nr_interface_rrc - int sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) final + void sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) final { - return rrc.sgnb_addition_request(eutra_rnti, params); + x2_task_queue.push([this, eutra_rnti, params]() { rrc.sgnb_addition_request(eutra_rnti, params); }); }; - int sgnb_reconfiguration_complete(uint16_t eutra_rnti, asn1::dyn_octstring reconfig_response) final + void sgnb_reconfiguration_complete(uint16_t eutra_rnti, const asn1::dyn_octstring& reconfig_response) final { - return rrc.sgnb_reconfiguration_complete(eutra_rnti, reconfig_response); + x2_task_queue.push( + [this, eutra_rnti, reconfig_response]() { rrc.sgnb_reconfiguration_complete(eutra_rnti, reconfig_response); }); }; + void sgnb_release_request(uint16_t nr_rnti) final + { + x2_task_queue.push([this, nr_rnti]() { return rrc.sgnb_release_request(nr_rnti); }); + } // X2 data interface void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) final { @@ -110,9 +120,10 @@ public: private: void run_thread() final; void tti_clock_impl(); + void stop_impl(); // args - srsenb::stack_args_t args = {}; + gnb_stack_args_t args = {}; phy_interface_stack_nr* phy = nullptr; srslog::basic_logger& rrc_logger; @@ -124,7 +135,12 @@ private: // task scheduling static const int STACK_MAIN_THREAD_PRIO = 4; srsran::task_scheduler task_sched; - srsran::task_multiqueue::queue_handle sync_task_queue, ue_task_queue, gtpu_task_queue, mac_task_queue; + srsran::task_multiqueue::queue_handle sync_task_queue, gtpu_task_queue, metrics_task_queue, gnb_task_queue, + x2_task_queue; + + // metrics waiting condition + std::mutex metrics_mutex; + std::condition_variable metrics_cvar; // derived srsenb::mac_nr mac; diff --git a/srsenb/hdr/stack/mac/mac.h b/srsenb/hdr/stack/mac/mac.h index 130a1d929..c97b32183 100644 --- a/srsenb/hdr/stack/mac/mac.h +++ b/srsenb/hdr/stack/mac/mac.h @@ -164,12 +164,12 @@ private: const static int NOF_BCCH_DLSCH_MSG = sched_interface::MAX_SIBS; const static int pcch_payload_buffer_len = 1024; - typedef struct { + struct common_buffers_t { uint8_t pcch_payload_buffer[pcch_payload_buffer_len] = {}; srsran_softbuffer_tx_t bcch_softbuffer_tx[NOF_BCCH_DLSCH_MSG] = {}; srsran_softbuffer_tx_t pcch_softbuffer_tx = {}; srsran_softbuffer_tx_t rar_softbuffer_tx = {}; - } common_buffers_t; + }; std::vector common_buffers; diff --git a/srsenb/hdr/stack/mac/nr/mac_nr.h b/srsenb/hdr/stack/mac/nr/mac_nr.h index 198ef8cdb..d927ffb26 100644 --- a/srsenb/hdr/stack/mac/nr/mac_nr.h +++ b/srsenb/hdr/stack/mac/nr/mac_nr.h @@ -37,11 +37,11 @@ namespace srsenb { struct mac_nr_args_t { - srsran::phy_cfg_nr_t phy_base_cfg = {}; - int fixed_dl_mcs = -1; - int fixed_ul_mcs = -1; - sched_nr_interface::sched_cfg_t sched_cfg = {}; - srsenb::pcap_args_t pcap; + srsran::phy_cfg_nr_t phy_base_cfg = {}; + int fixed_dl_mcs = -1; + int fixed_ul_mcs = -1; + sched_nr_interface::sched_args_t sched_cfg = {}; + srsenb::pcap_args_t pcap; }; class mac_nr final : public mac_interface_phy_nr, public mac_interface_rrc_nr, public mac_interface_rlc_nr @@ -57,11 +57,12 @@ public: rrc_interface_mac_nr* rrc_); void stop(); + /// Called from metrics thread. void get_metrics(srsenb::mac_metrics_t& metrics); // MAC interface for RRC int cell_cfg(const std::vector& nr_cells) override; - uint16_t reserve_rnti(uint32_t enb_cc_idx) override; + uint16_t reserve_rnti(uint32_t enb_cc_idx, const sched_nr_interface::ue_cfg_t& uecfg) override; int read_pdu_bcch_bch(uint8_t* payload); int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) override; int remove_ue(uint16_t rnti) override; @@ -91,8 +92,11 @@ private: // PDU processing int handle_pdu(srsran::unique_byte_buffer_t pdu); + // Metrics processing + void get_metrics_nolock(srsenb::mac_metrics_t& metrics); + // Encoding - srsran::byte_buffer_t* assemble_rar(srsran::const_span grants); + srsran::byte_buffer_t* assemble_rar(srsran::const_span grants); srsran::unique_byte_buffer_t rar_pdu_buffer = nullptr; // Interaction with other components @@ -116,7 +120,7 @@ private: std::vector cell_config; // Map of active UEs - pthread_rwlock_t rwlock = {}; + pthread_rwlock_t rwmutex = {}; static const uint16_t FIRST_RNTI = 0x4601; srsran::static_circular_map, SRSENB_MAX_UES> ue_db; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr.h b/srsenb/hdr/stack/mac/nr/sched_nr.h index f349e62cc..cb3da5678 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr.h @@ -39,7 +39,6 @@ class sched_worker_manager; class serv_cell_manager; } // namespace sched_nr_impl -class ue_event_manager; class ul_sched_result_buffer; class sched_nr final : public sched_nr_interface @@ -47,12 +46,12 @@ class sched_nr final : public sched_nr_interface public: explicit sched_nr(); ~sched_nr() override; - int config(const sched_cfg_t& sched_cfg, srsran::const_span cell_list) override; + int config(const sched_args_t& sched_cfg, srsran::const_span cell_list) override; void ue_cfg(uint16_t rnti, const ue_cfg_t& cfg) override; void ue_rem(uint16_t rnti) override; bool ue_exists(uint16_t rnti) override; - int dl_rach_info(uint32_t cc, const dl_sched_rar_info_t& rar_info); + int dl_rach_info(uint32_t cc, const rar_info_t& rar_info); void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) override; void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) override; @@ -60,9 +59,11 @@ public: void ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) override; void dl_buffer_state(uint16_t rnti, uint32_t lcid, uint32_t newtx, uint32_t retx); - int get_dl_sched(slot_point pdsch_tti, uint32_t cc, dl_sched_res_t& result) override; + int run_slot(slot_point pdsch_tti, uint32_t cc, dl_sched_res_t& result) override; int get_ul_sched(slot_point pusch_tti, uint32_t cc, ul_sched_t& result) override; + void get_metrics(mac_metrics_t& metrics); + private: void ue_cfg_impl(uint16_t rnti, const ue_cfg_t& cfg); diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_cell.h b/srsenb/hdr/stack/mac/nr/sched_nr_cell.h index 3bee9770b..cbbb26e54 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_cell.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_cell.h @@ -34,12 +34,12 @@ namespace sched_nr_impl { class si_sched { public: - explicit si_sched(const bwp_params& bwp_cfg_); + explicit si_sched(const bwp_params_t& bwp_cfg_); void run_slot(bwp_slot_allocator& slot_alloc); private: - const bwp_params* bwp_cfg = nullptr; + const bwp_params_t* bwp_cfg = nullptr; srslog::basic_logger& logger; struct sched_si_t { @@ -54,13 +54,13 @@ private: srsran::bounded_vector pending_sis; }; -using dl_sched_rar_info_t = sched_nr_interface::dl_sched_rar_info_t; +using dl_sched_rar_info_t = sched_nr_interface::rar_info_t; /// RAR/Msg3 scheduler class ra_sched { public: - explicit ra_sched(const bwp_params& bwp_cfg_); + explicit ra_sched(const bwp_params_t& bwp_cfg_); /// Addition of detected PRACH into the queue int dl_rach_info(const dl_sched_rar_info_t& rar_info); @@ -82,7 +82,7 @@ private: alloc_result allocate_pending_rar(bwp_slot_allocator& slot_grid, const pending_rar_t& rar, uint32_t& nof_grants_alloc); - const bwp_params* bwp_cfg = nullptr; + const bwp_params_t* bwp_cfg = nullptr; srslog::basic_logger& logger; srsran::deque pending_rars; @@ -91,9 +91,9 @@ private: class bwp_ctxt { public: - explicit bwp_ctxt(const bwp_params& bwp_cfg); + explicit bwp_ctxt(const bwp_params_t& bwp_cfg); - const bwp_params* cfg; + const bwp_params_t* cfg; // channel-specific schedulers ra_sched ra; @@ -108,10 +108,10 @@ class serv_cell_manager public: using feedback_callback_t = srsran::move_callback; - explicit serv_cell_manager(const sched_cell_params& cell_cfg_); + explicit serv_cell_manager(const cell_params_t& cell_cfg_); srsran::bounded_vector bwps; - const sched_cell_params& cfg; + const cell_params_t& cfg; private: srslog::basic_logger& logger; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h b/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h index 6f445fae5..1abe79f63 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_cfg.h @@ -35,27 +35,32 @@ static const size_t MAX_NOF_AGGR_LEVELS = 5; namespace sched_nr_impl { -const static size_t MAX_GRANTS = sched_nr_interface::MAX_GRANTS; - -using pdcch_dl_t = mac_interface_phy_nr::pdcch_dl_t; -using pdcch_ul_t = mac_interface_phy_nr::pdcch_ul_t; -using pdsch_t = mac_interface_phy_nr::pdsch_t; -using pusch_t = mac_interface_phy_nr::pusch_t; -using pucch_t = mac_interface_phy_nr::pucch_t; -using pdcch_dl_list_t = srsran::bounded_vector; -using pdcch_ul_list_t = srsran::bounded_vector; -using pucch_list_t = srsran::bounded_vector; -using pusch_list_t = srsran::bounded_vector; -using nzp_csi_rs_list = srsran::bounded_vector; -using ssb_t = mac_interface_phy_nr::ssb_t; -using ssb_list = srsran::bounded_vector; - -using sched_cfg_t = sched_nr_interface::sched_cfg_t; -using cell_cfg_t = sched_nr_interface::cell_cfg_t; -using bwp_cfg_t = sched_nr_interface::bwp_cfg_t; - +constexpr static size_t MAX_GRANTS = sched_nr_interface::MAX_GRANTS; + +using pdcch_dl_t = mac_interface_phy_nr::pdcch_dl_t; +using pdcch_ul_t = mac_interface_phy_nr::pdcch_ul_t; +using pdsch_t = mac_interface_phy_nr::pdsch_t; +using pusch_t = mac_interface_phy_nr::pusch_t; +using pucch_t = mac_interface_phy_nr::pucch_t; +using pdcch_dl_list_t = srsran::bounded_vector; +using pdcch_ul_list_t = srsran::bounded_vector; +using pucch_list_t = srsran::bounded_vector; +using pusch_list_t = srsran::bounded_vector; +using nzp_csi_rs_list = srsran::bounded_vector; +using ssb_t = mac_interface_phy_nr::ssb_t; +using ssb_list = srsran::bounded_vector; +using sched_args_t = sched_nr_interface::sched_args_t; +using cell_cfg_t = sched_nr_interface::cell_cfg_t; +using bwp_cfg_t = sched_nr_interface::bwp_cfg_t; +using ue_cfg_t = sched_nr_interface::ue_cfg_t; +using ue_cc_cfg_t = sched_nr_interface::ue_cc_cfg_t; using pdcch_cce_pos_list = srsran::bounded_vector; using bwp_cce_pos_list = std::array, SRSRAN_NOF_SF_X_FRAME>; +using dl_sched_t = sched_nr_interface::dl_sched_t; +using ul_sched_t = sched_nr_interface::ul_sched_t; +using dl_sched_res_t = sched_nr_interface::dl_sched_res_t; + +/// Generate list of CCE locations for UE based on coreset and search space configurations void get_dci_locs(const srsran_coreset_t& coreset, const srsran_search_space_t& search_space, uint16_t rnti, @@ -63,18 +68,22 @@ void get_dci_locs(const srsran_coreset_t& coreset, /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -struct bwp_params { - const uint32_t bwp_id; - const uint32_t cc; - const bwp_cfg_t& cfg; - const cell_cfg_t& cell_cfg; - const sched_cfg_t& sched_cfg; +/// Structure that extends the sched_nr_interface::bwp_cfg_t passed by upper layers with other +/// derived BWP-specific params +struct bwp_params_t { + const uint32_t bwp_id; + const uint32_t cc; + const bwp_cfg_t& cfg; + const cell_cfg_t& cell_cfg; + const sched_args_t& sched_cfg; // derived params srslog::basic_logger& logger; uint32_t P; uint32_t N_rbg; + uint32_t nof_prb() const { return cell_cfg.carrier.nof_prb; } + /// Table specifying if a slot has DL or UL enabled struct slot_cfg { bool is_dl; bool is_ul; @@ -91,49 +100,51 @@ struct bwp_params { bwp_cce_pos_list rar_cce_list; - bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc, uint32_t bwp_id); + bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg_, uint32_t cc, uint32_t bwp_id); }; -struct sched_cell_params { - const uint32_t cc; - const cell_cfg_t cell_cfg; - const sched_cfg_t& sched_cfg; - std::vector bwps; +/// Structure packing a single cell config params, and sched args +struct cell_params_t { + const uint32_t cc; + const cell_cfg_t cfg; + const sched_args_t& sched_args; + std::vector bwps; - sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_); + cell_params_t(uint32_t cc_, const cell_cfg_t& cell, const sched_args_t& sched_cfg_); - uint32_t nof_prb() const { return cell_cfg.carrier.nof_prb; } + uint32_t nof_prb() const { return cfg.carrier.nof_prb; } }; +/// Structure packing both the sched args and all gNB NR cell configurations struct sched_params { - sched_cfg_t sched_cfg; - std::vector cells; + sched_args_t sched_cfg; + std::vector cells; sched_params() = default; - explicit sched_params(const sched_cfg_t& sched_cfg_); + explicit sched_params(const sched_args_t& sched_cfg_); }; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -using prb_bitmap = srsran::bounded_bitset; - -using pdcchmask_t = srsran::bounded_bitset; - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -using ue_cfg_t = sched_nr_interface::ue_cfg_t; -using ue_cc_cfg_t = sched_nr_interface::ue_cc_cfg_t; - +/// Configuration of a UE for a given BWP class bwp_ue_cfg { public: bwp_ue_cfg() = default; - explicit bwp_ue_cfg(uint16_t rnti, const bwp_params& bwp_cfg, const ue_cfg_t& uecfg_); + explicit bwp_ue_cfg(uint16_t rnti, const bwp_params_t& bwp_cfg, const ue_cfg_t& uecfg_); - const ue_cfg_t* ue_cfg() const { return cfg_; } - const srsran::phy_cfg_nr_t& phy() const { return cfg_->phy_cfg; } - const bwp_params& active_bwp() const { return *bwp_cfg; } - const bwp_cce_pos_list& cce_pos_list(uint32_t search_id) const + const ue_cfg_t* ue_cfg() const { return cfg_; } + const srsran::phy_cfg_nr_t& phy() const { return cfg_->phy_cfg; } + const bwp_params_t& active_bwp() const { return *bwp_cfg; } + srsran::const_span cce_pos_list(uint32_t search_id, uint32_t slot_idx, uint32_t aggr_idx) const + { + if (cce_positions_list.size() > ss_id_to_cce_idx[search_id]) { + auto& lst = cce_pos_list(search_id); + return lst[slot_idx][aggr_idx]; + } + return srsran::const_span{}; + } + const bwp_cce_pos_list& cce_pos_list(uint32_t search_id) const { return cce_positions_list[ss_id_to_cce_idx[search_id]]; } @@ -144,11 +155,13 @@ public: } return phy().harq_ack.dl_data_to_ul_ack[pdsch_slot.to_uint() % phy().harq_ack.nof_dl_data_to_ul_ack]; } + int fixed_pdsch_mcs() const { return bwp_cfg->sched_cfg.fixed_dl_mcs; } + int fixed_pusch_mcs() const { return bwp_cfg->sched_cfg.fixed_ul_mcs; } private: - uint16_t rnti = SRSRAN_INVALID_RNTI; - const ue_cfg_t* cfg_ = nullptr; - const bwp_params* bwp_cfg = nullptr; + uint16_t rnti = SRSRAN_INVALID_RNTI; + const ue_cfg_t* cfg_ = nullptr; + const bwp_params_t* bwp_cfg = nullptr; std::vector cce_positions_list; std::array ss_id_to_cce_idx; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_grant_allocator.h b/srsenb/hdr/stack/mac/nr/sched_nr_grant_allocator.h index a895f4bde..2724bce74 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_grant_allocator.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_grant_allocator.h @@ -33,7 +33,7 @@ namespace srsenb { namespace sched_nr_impl { // typedefs -using dl_sched_rar_info_t = sched_nr_interface::dl_sched_rar_info_t; +using dl_sched_rar_info_t = sched_nr_interface::rar_info_t; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -53,8 +53,8 @@ using harq_ack_list_t = srsran::bounded_vector; /// save data for scheduler to keep track of previous allocations /// This only contains information about a given slot struct bwp_slot_grid { - uint32_t slot_idx = 0; - const bwp_params* cfg = nullptr; + uint32_t slot_idx = 0; + const bwp_params_t* cfg = nullptr; bwp_rb_bitmap dl_prbs; bwp_rb_bitmap ul_prbs; @@ -72,7 +72,7 @@ struct bwp_slot_grid { srsran::unique_pool_ptr rar_softbuffer; bwp_slot_grid() = default; - explicit bwp_slot_grid(const bwp_params& bwp_params, uint32_t slot_idx_); + explicit bwp_slot_grid(const bwp_params_t& bwp_params, uint32_t slot_idx_); void reset(); bool is_dl() const { return cfg->slots[slot_idx].is_dl; } @@ -80,14 +80,14 @@ struct bwp_slot_grid { }; struct bwp_res_grid { - explicit bwp_res_grid(const bwp_params& bwp_cfg_); + explicit bwp_res_grid(const bwp_params_t& bwp_cfg_); bwp_slot_grid& operator[](slot_point tti) { return slots[tti.to_uint() % slots.capacity()]; }; const bwp_slot_grid& operator[](slot_point tti) const { return slots[tti.to_uint() % slots.capacity()]; }; uint32_t id() const { return cfg->bwp_id; } uint32_t nof_prbs() const { return cfg->cfg.rb_width; } - const bwp_params* cfg = nullptr; + const bwp_params_t* cfg = nullptr; private: // TTIMOD_SZ is the longest allocation in the future @@ -123,7 +123,7 @@ public: slot_point get_tti_rx() const { return pdcch_slot - TX_ENB_DELAY; } const bwp_res_grid& res_grid() const { return bwp_grid; } - const bwp_params& cfg; + const bwp_params_t& cfg; private: alloc_result verify_pdsch_space(bwp_slot_grid& pdsch_grid, bwp_slot_grid& pdcch_grid) const; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_harq.h b/srsenb/hdr/stack/mac/nr/sched_nr_harq.h index c16f392b2..fe181f76d 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_harq.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_harq.h @@ -57,17 +57,13 @@ public: void new_slot(slot_point slot_rx); void reset(); - bool new_tx(slot_point slot_tx, - slot_point slot_ack, - const prb_grant& grant, - uint32_t mcs, - uint32_t tbs, - uint32_t max_retx); + bool new_tx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, uint32_t mcs, uint32_t max_retx); bool new_retx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant); bool new_retx(slot_point slot_tx, slot_point slot_ack); // NOTE: Has to be used before first tx is dispatched bool set_tbs(uint32_t tbs); + bool set_mcs(uint32_t mcs); const uint32_t pid; @@ -96,12 +92,7 @@ public: tx_harq_softbuffer& get_softbuffer() { return *softbuffer; } srsran::unique_byte_buffer_t* get_tx_pdu() { return &pdu; } - bool new_tx(slot_point slot_tx, - slot_point slot_ack, - const prb_grant& grant, - uint32_t mcs, - uint32_t tbs, - uint32_t max_retx); + bool new_tx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, uint32_t mcs, uint32_t max_retx); private: srsran::unique_pool_ptr softbuffer; @@ -119,7 +110,7 @@ public: bool set_tbs(uint32_t tbs) { - softbuffer->reset(tbs * 8u); + softbuffer->reset(tbs); return harq_proc::set_tbs(tbs); } diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h b/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h index 3ddcf9800..5f2274b89 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_helpers.h @@ -34,20 +34,20 @@ struct bwp_res_grid; /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci); +bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci); -bool fill_dci_msg3(const slot_ue& ue, const bwp_params& bwp_cfg, srsran_dci_ul_nr_t& dci); +bool fill_dci_msg3(const slot_ue& ue, const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& dci); /// Generate PDCCH DL DCI fields void fill_dl_dci_ue_fields(const slot_ue& ue, - const bwp_params& bwp_cfg, + const bwp_params_t& bwp_cfg, uint32_t ss_id, srsran_dci_location_t dci_pos, srsran_dci_dl_nr_t& dci); /// Generate PDCCH UL DCI fields void fill_ul_dci_ue_fields(const slot_ue& ue, - const bwp_params& bwp_cfg, + const bwp_params_t& bwp_cfg, uint32_t ss_id, srsran_dci_location_t dci_pos, srsran_dci_ul_nr_t& dci); diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_interface.h b/srsenb/hdr/stack/mac/nr/sched_nr_interface.h index 27eaa9c09..7a04bf32c 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_interface.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_interface.h @@ -37,11 +37,23 @@ const static size_t SCHED_NR_MAX_CARRIERS = 4; const static uint16_t SCHED_NR_INVALID_RNTI = 0; const static size_t SCHED_NR_MAX_NOF_RBGS = 18; const static size_t SCHED_NR_MAX_TB = 1; -const static size_t SCHED_NR_MAX_HARQ = 16; +const static size_t SCHED_NR_MAX_HARQ = SRSRAN_DEFAULT_HARQ_PROC_DL_NR; const static size_t SCHED_NR_MAX_BWP_PER_CELL = 2; const static size_t SCHED_NR_MAX_LCID = 32; const static size_t SCHED_NR_MAX_LC_GROUP = 7; +struct sched_nr_ue_cc_cfg_t { + bool active = false; + uint32_t cc = 0; +}; + +struct sched_nr_ue_cfg_t { + uint32_t maxharq_tx = 4; + srsran::bounded_vector carriers; + std::array ue_bearers = {}; + srsran::phy_cfg_nr_t phy_cfg = {}; +}; + class sched_nr_interface { public: @@ -76,30 +88,21 @@ public: srsran::bounded_vector bwps{1}; // idx0 for BWP-common }; - struct sched_cfg_t { + struct sched_args_t { bool pdsch_enabled = true; bool pusch_enabled = true; bool auto_refill_buffer = false; + int fixed_dl_mcs = 28; + int fixed_ul_mcs = 28; std::string logger_name = "MAC-NR"; }; - struct ue_cc_cfg_t { - bool active = false; - uint32_t cc = 0; - }; + using ue_cc_cfg_t = sched_nr_ue_cc_cfg_t; + using ue_cfg_t = sched_nr_ue_cfg_t; - struct ue_cfg_t { - uint32_t maxharq_tx = 4; - int fixed_dl_mcs = -1; - int fixed_ul_mcs = -1; - srsran::bounded_vector carriers; - std::array ue_bearers = {}; - srsran::phy_cfg_nr_t phy_cfg = {}; - }; + ////// RA procedure ////// - ////// RACH ////// - - struct dl_sched_rar_info_t { + struct rar_info_t { uint32_t preamble_idx; // is this the RAPID? uint32_t ofdm_symbol_idx; uint32_t freq_idx; @@ -108,32 +111,32 @@ public: uint32_t msg3_size = 7; slot_point prach_slot; }; + struct msg3_grant_t { + rar_info_t data; + srsran_dci_ul_nr_t msg3_dci = {}; + }; + struct rar_t { + srsran::bounded_vector grants; + }; ///// Sched Result ///// using dl_sched_t = mac_interface_phy_nr::dl_sched_t; using ul_sched_t = mac_interface_phy_nr::ul_sched_t; - struct sched_rar_grant_t { - dl_sched_rar_info_t data; - srsran_dci_ul_nr_t msg3_dci = {}; - }; - struct sched_rar_t { - srsran::bounded_vector grants; - }; - using sched_rar_list_t = srsran::bounded_vector; + using sched_rar_list_t = srsran::bounded_vector; struct dl_sched_res_t { sched_rar_list_t rar; dl_sched_t dl_sched; }; virtual ~sched_nr_interface() = default; - virtual int config(const sched_cfg_t& sched_cfg, srsran::const_span ue_cfg) = 0; - virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0; - virtual void ue_rem(uint16_t rnti) = 0; - virtual bool ue_exists(uint16_t rnti) = 0; - virtual int get_dl_sched(slot_point slot_rx, uint32_t cc, dl_sched_res_t& result) = 0; - virtual int get_ul_sched(slot_point slot_rx, uint32_t cc, ul_sched_t& result) = 0; + virtual int config(const sched_args_t& sched_cfg, srsran::const_span ue_cfg) = 0; + virtual void ue_cfg(uint16_t rnti, const ue_cfg_t& ue_cfg) = 0; + virtual void ue_rem(uint16_t rnti) = 0; + virtual bool ue_exists(uint16_t rnti) = 0; + virtual int run_slot(slot_point slot_rx, uint32_t cc, dl_sched_res_t& result) = 0; + virtual int get_ul_sched(slot_point slot_rx, uint32_t cc, ul_sched_t& result) = 0; virtual void dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) = 0; virtual void ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) = 0; diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h b/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h index 4c2fc4dfc..5b998443f 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_pdcch.h @@ -43,11 +43,11 @@ using bwp_cfg_t = sched_nr_interface::bwp_cfg_t; class coreset_region { public: - coreset_region(const bwp_params& bwp_cfg_, - uint32_t coreset_id_, - uint32_t slot_idx, - pdcch_dl_list_t& pdcch_dl_list, - pdcch_ul_list_t& pdcch_ul_list); + coreset_region(const bwp_params_t& bwp_cfg_, + uint32_t coreset_id_, + uint32_t slot_idx, + pdcch_dl_list_t& pdcch_dl_list, + pdcch_ul_list_t& pdcch_ul_list); void reset(); /** diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_signalling.h b/srsenb/hdr/stack/mac/nr/sched_nr_signalling.h index 6c4125377..c6b392f8c 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_signalling.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_signalling.h @@ -51,10 +51,10 @@ void sched_nzp_csi_rs(srsran::const_span nzp_csi_rs_set void sched_ssb_basic(const slot_point& sl_point, uint32_t ssb_periodicity, ssb_list& ssb_list); /// For a given BWP and slot, schedule SSB, NZP CSI RS and SIBs -void sched_dl_signalling(const bwp_params& bwp_params, - slot_point sl_pdcch, - ssb_list& ssb_list, - nzp_csi_rs_list& nzp_csi_rs); +void sched_dl_signalling(const bwp_params_t& bwp_params, + slot_point sl_pdcch, + ssb_list& ssb_list, + nzp_csi_rs_list& nzp_csi_rs); } // namespace sched_nr_impl } // namespace srsenb diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_ue.h b/srsenb/hdr/stack/mac/nr/sched_nr_ue.h index dc4e210d7..1948dc6fc 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_ue.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_ue.h @@ -25,6 +25,7 @@ #include "sched_nr_cfg.h" #include "sched_nr_harq.h" #include "sched_nr_interface.h" +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" #include "srsenb/hdr/stack/mac/common/ue_buffer_manager.h" #include "srsran/adt/circular_map.h" #include "srsran/adt/move_callback.h" @@ -70,8 +71,9 @@ public: class ue_carrier { public: - ue_carrier(uint16_t rnti, const ue_cfg_t& cfg, const sched_cell_params& cell_params_); - slot_ue try_reserve(slot_point pdcch_slot, const ue_cfg_t& uecfg_, uint32_t dl_harq_bytes, uint32_t ul_harq_bytes); + ue_carrier(uint16_t rnti, const ue_cfg_t& cfg, const cell_params_t& cell_params_); + void set_cfg(const ue_cfg_t& ue_cfg); + slot_ue try_reserve(slot_point pdcch_slot, uint32_t dl_harq_bytes, uint32_t ul_harq_bytes); const uint16_t rnti; const uint32_t cc; @@ -82,9 +84,13 @@ public: harq_entity harq_ent; + // metrics + mac_ue_metrics_t metrics = {}; + std::mutex metrics_mutex; + private: - bwp_ue_cfg bwp_cfg; - const sched_cell_params& cell_params; + bwp_ue_cfg bwp_cfg; + const cell_params_t& cell_params; }; class ue diff --git a/srsenb/hdr/stack/mac/nr/sched_nr_worker.h b/srsenb/hdr/stack/mac/nr/sched_nr_worker.h index abf632693..f9c9364b1 100644 --- a/srsenb/hdr/stack/mac/nr/sched_nr_worker.h +++ b/srsenb/hdr/stack/mac/nr/sched_nr_worker.h @@ -34,11 +34,10 @@ #include namespace srsenb { -namespace sched_nr_impl { -using dl_sched_t = sched_nr_interface::dl_sched_t; -using ul_sched_t = sched_nr_interface::ul_sched_t; -using dl_sched_res_t = sched_nr_interface::dl_sched_res_t; +struct mac_metrics_t; + +namespace sched_nr_impl { class slot_cc_worker { @@ -63,9 +62,9 @@ private: void alloc_ul_ues(); void postprocess_decisions(); - const sched_cell_params& cfg; - serv_cell_manager& cell; - srslog::basic_logger& logger; + const cell_params_t& cfg; + serv_cell_manager& cell; + srslog::basic_logger& logger; slot_point slot_rx; bwp_slot_allocator bwp_alloc; @@ -103,6 +102,8 @@ public: void run_slot(slot_point slot_tx, uint32_t cc, dl_sched_res_t& dl_res, ul_sched_t& ul_res); + void get_metrics(mac_metrics_t& metrics); + void enqueue_event(uint16_t rnti, srsran::move_callback ev); void enqueue_cc_event(uint32_t cc, srsran::move_callback ev); void enqueue_cc_feedback(uint16_t rnti, uint32_t cc, slot_cc_worker::feedback_callback_t fdbk) @@ -112,7 +113,7 @@ public: private: void update_ue_db(slot_point slot_tx, bool locked_context); - + void get_metrics_nolocking(mac_metrics_t& metrics); bool save_sched_result(slot_point pdcch_slot, uint32_t cc, dl_sched_res_t& dl_res, ul_sched_t& ul_res); const sched_params& cfg; diff --git a/srsenb/hdr/stack/mac/nr/ue_nr.h b/srsenb/hdr/stack/mac/nr/ue_nr.h index b651afadf..7814e43ce 100644 --- a/srsenb/hdr/stack/mac/nr/ue_nr.h +++ b/srsenb/hdr/stack/mac/nr/ue_nr.h @@ -68,9 +68,11 @@ public: void metrics_phr(float phr); void metrics_dl_ri(uint32_t dl_cqi); void metrics_dl_pmi(uint32_t dl_cqi); - void metrics_dl_cqi(uint32_t dl_cqi); + void metrics_dl_cqi(const srsran_uci_cfg_nr_t& cfg_, uint32_t dl_cqi); void metrics_dl_mcs(uint32_t mcs); void metrics_ul_mcs(uint32_t mcs); + void metrics_pucch_sinr(float sinr); + void metrics_pusch_sinr(float sinr); void metrics_cnt(); uint32_t read_pdu(uint32_t lcid, uint8_t* payload, uint32_t requested_bytes) final; @@ -92,11 +94,15 @@ private: std::atomic active_state{true}; - uint32_t phr_counter = 0; - uint32_t dl_cqi_counter = 0; - uint32_t dl_ri_counter = 0; - uint32_t dl_pmi_counter = 0; - mac_ue_metrics_t ue_metrics = {}; + // TODO: some counters are kept as members of class ue_nr, while some others (i.e., mcs) are kept in the ue_metrics + // We should make these counters more uniform + uint32_t phr_counter = 0; + uint32_t dl_cqi_valid_counter = 0; + uint32_t dl_ri_counter = 0; + uint32_t dl_pmi_counter = 0; + uint32_t pucch_sinr_counter = 0; + uint32_t pusch_sinr_counter = 0; + mac_ue_metrics_t ue_metrics = {}; // UE-specific buffer for MAC PDU packing, unpacking and handling srsran::mac_sch_pdu_nr mac_pdu_dl, mac_pdu_ul; @@ -105,6 +111,9 @@ private: ue_rx_pdu_queue; ///< currently only DCH PDUs supported (add BCH, PCH, etc) srsran::unique_byte_buffer_t ue_rlc_buffer; + static constexpr int32_t MIN_RLC_PDU_LEN = + 5; ///< minimum bytes that need to be available in a MAC PDU for attempting to add another RLC SDU + // Mutexes std::mutex mutex; }; diff --git a/srsenb/hdr/stack/ngap/ngap_interfaces.h b/srsenb/hdr/stack/ngap/ngap_interfaces.h index 90ffe1543..f4180017d 100644 --- a/srsenb/hdr/stack/ngap/ngap_interfaces.h +++ b/srsenb/hdr/stack/ngap/ngap_interfaces.h @@ -30,9 +30,10 @@ class ngap_interface_ngap_proc public: virtual bool send_initial_ctxt_setup_response() = 0; virtual bool - send_pdu_session_resource_setup_response(uint16_t pdu_session_id, - uint32_t teid_out, - asn1::bounded_bitstring<1, 160, true, true> transport_layer_address) = 0; + send_pdu_session_resource_setup_response(uint16_t pdu_session_id, + uint32_t teid_out, + asn1::bounded_bitstring<1, 160, true, true> transport_layer_address) = 0; + virtual bool send_ue_ctxt_release_complete() = 0; }; } // namespace srsenb diff --git a/srsenb/hdr/stack/ngap/ngap_ue.h b/srsenb/hdr/stack/ngap/ngap_ue.h index 9e695ee7e..47c115321 100644 --- a/srsenb/hdr/stack/ngap/ngap_ue.h +++ b/srsenb/hdr/stack/ngap/ngap_ue.h @@ -53,6 +53,8 @@ public: bool send_pdu_session_resource_setup_response(uint16_t pdu_session_id, uint32_t teid_in, asn1::bounded_bitstring<1, 160, true, true> addr_in); + // TS 38.413 - Section 9.2.1.2 - UE Context Release Complete + bool send_ue_ctxt_release_complete(); // TS 38.413 - Section 9.2.2.1 - Initial Context Setup Request bool handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_setup_request_s& msg); // TS 38.413 - Section 9.2.2.5 - UE Context Release Command diff --git a/srsenb/hdr/stack/ngap/ngap_ue_bearer_manager.h b/srsenb/hdr/stack/ngap/ngap_ue_bearer_manager.h index a06b59121..a7617ce74 100644 --- a/srsenb/hdr/stack/ngap/ngap_ue_bearer_manager.h +++ b/srsenb/hdr/stack/ngap/ngap_ue_bearer_manager.h @@ -50,7 +50,7 @@ public: std::vector tunnels; }; - ngap_ue_bearer_manager(rrc_interface_ngap_nr* rrc_, gtpu_interface_rrc* gtpu_, srslog::basic_logger& logger_); + ngap_ue_bearer_manager(gtpu_interface_rrc* gtpu_, srslog::basic_logger& logger_); ~ngap_ue_bearer_manager(); int add_pdu_session(uint16_t rnti, @@ -63,14 +63,14 @@ public: uint32_t& teid_in, asn1::ngap_nr::cause_c& cause); + int reset_pdu_sessions(uint16_t rnti); + private: gtpu_interface_rrc* gtpu = nullptr; - rrc_interface_ngap_nr* rrc = nullptr; std::map pdu_session_list; srslog::basic_logger& logger; int add_gtpu_bearer(uint16_t rnti, - uint32_t lcid, uint32_t pdu_session_id, uint32_t teid_out, asn1::bounded_bitstring<1, 160, true, true> address, diff --git a/srsenb/hdr/stack/ngap/ngap_ue_proc.h b/srsenb/hdr/stack/ngap/ngap_ue_proc.h index 179e8079f..ff6308b26 100644 --- a/srsenb/hdr/stack/ngap/ngap_ue_proc.h +++ b/srsenb/hdr/stack/ngap/ngap_ue_proc.h @@ -25,12 +25,12 @@ #include "ngap_interfaces.h" #include "ngap_ue_utils.h" +#include "srsenb/hdr/stack/ngap/ngap_ue_bearer_manager.h" #include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/ngap.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/stack_procedure.h" #include "srsran/interfaces/gnb_rrc_nr_interfaces.h" -#include "srsenb/hdr/stack/ngap/ngap_ue_bearer_manager.h" #include #include @@ -66,6 +66,7 @@ public: explicit ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_, rrc_interface_ngap_nr* rrc_, ngap_ue_ctxt_t* ue_ctxt, + ngap_ue_bearer_manager* bearer_manager, srslog::basic_logger& logger_); srsran::proc_outcome_t init(const asn1::ngap_nr::ue_context_release_cmd_s& msg); srsran::proc_outcome_t step(); @@ -73,8 +74,9 @@ public: private: ngap_ue_ctxt_t* ue_ctxt; - ngap_interface_ngap_proc* parent = nullptr; - rrc_interface_ngap_nr* rrc = nullptr; + ngap_interface_ngap_proc* parent = nullptr; + rrc_interface_ngap_nr* rrc = nullptr; + ngap_ue_bearer_manager* bearer_manager = nullptr; srslog::basic_logger& logger; }; diff --git a/srsenb/hdr/stack/rrc/nr/cell_asn1_config.h b/srsenb/hdr/stack/rrc/nr/cell_asn1_config.h index 0e2d682d9..68a69c5c4 100644 --- a/srsenb/hdr/stack/rrc/nr/cell_asn1_config.h +++ b/srsenb/hdr/stack/rrc/nr/cell_asn1_config.h @@ -27,7 +27,7 @@ namespace srsenb { -int fill_serv_cell_from_enb_cfg(const rrc_nr_cfg_t& cfg, asn1::rrc_nr::serving_cell_cfg_s& serv_cell); +int fill_sp_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::sp_cell_cfg_s& sp_cell); } diff --git a/srsenb/hdr/stack/rrc/nr/rrc_config_nr.h b/srsenb/hdr/stack/rrc/nr/rrc_config_nr.h index 9a0c17c40..a931a8b7f 100644 --- a/srsenb/hdr/stack/rrc/nr/rrc_config_nr.h +++ b/srsenb/hdr/stack/rrc/nr/rrc_config_nr.h @@ -47,6 +47,7 @@ struct rrc_nr_cfg_t { rrc_cfg_cqi_t cqi_cfg; rrc_cell_list_nr_t cell_list; + std::string log_name = "RRC-NR"; std::string log_level; uint32_t log_hex_limit; }; diff --git a/srsenb/hdr/stack/rrc/rrc.h b/srsenb/hdr/stack/rrc/rrc.h index e6506c710..4b4b123bb 100644 --- a/srsenb/hdr/stack/rrc/rrc.h +++ b/srsenb/hdr/stack/rrc/rrc.h @@ -140,6 +140,7 @@ public: void sgnb_addition_ack(uint16_t eutra_rnti, const sgnb_addition_ack_params_t params) override; void sgnb_addition_reject(uint16_t eutra_rnti) override; void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) override; + void sgnb_release_ack(uint16_t eutra_rnti) override; // rrc_interface_pdcp void write_pdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t pdu) override; diff --git a/srsenb/hdr/stack/rrc/rrc_config.h b/srsenb/hdr/stack/rrc/rrc_config.h index 5b52f5853..09f9dd660 100644 --- a/srsenb/hdr/stack/rrc/rrc_config.h +++ b/srsenb/hdr/stack/rrc/rrc_config.h @@ -55,7 +55,7 @@ struct srb_cfg_t { // Parameter required for NR cell measurement handling struct rrc_endc_cfg_t { bool act_from_b1_event; - uint32_t nr_dl_arfcn; + uint32_t abs_frequency_ssb; uint32_t nr_band; using ssb_nr_cfg = asn1::rrc::mtc_ssb_nr_r15_s; using ssb_rs_cfg = asn1::rrc::rs_cfg_ssb_nr_r15_s; diff --git a/srsenb/hdr/stack/rrc/rrc_endc.h b/srsenb/hdr/stack/rrc/rrc_endc.h index b5e9e5e00..f79ba48cf 100644 --- a/srsenb/hdr/stack/rrc/rrc_endc.h +++ b/srsenb/hdr/stack/rrc/rrc_endc.h @@ -45,8 +45,9 @@ public: struct sgnb_add_req_ack_ev { sgnb_addition_ack_params_t params; }; - struct sgnb_add_req_reject_ev {}; + /// Called when Reconf fails + struct rrc_reest_rx_ev {}; /** * @brief Non-standard event sent from NR-RRC to EUTRA when UE has attached to NR cell @@ -57,12 +58,35 @@ public: uint16_t nr_rnti; /// RNTI assigned to UE on NR carrier }; + /** + * @brief Called from EUTRA-RRC when EN-DC (for UE) shall be removed/released + * + */ + struct sgnb_rel_req_ev { + uint16_t nr_rnti; /// RNTI assigned to UE on NR carrier + }; + + /** + * @brief Event sent from NR-RRC to EUTRA when UE has been removed from SgNB + * + * sent in response to SgNB Release Request + */ + struct sgnb_rel_req_ack_ev {}; + + /** + * @brief in case of non supported NR carrier in UE capabilities or the NR reconfiguration failed + */ + struct disable_endc_ev {}; + rrc_endc(srsenb::rrc::ue* outer_ue, const rrc_endc_cfg_t& endc_cfg_); + ~rrc_endc(); bool fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg); void handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_s& eutra_caps); void handle_ue_meas_report(const asn1::rrc::meas_report_s& msg); + void start_sgnb_release(); + bool is_endc_supported(); private: @@ -75,9 +99,14 @@ private: rrc* rrc_enb = nullptr; srslog::basic_logger& logger; + // nr ids to deactivate in second reconfig + uint32_t nr_meas_obj_id = 0; + uint32_t nr_report_cfg_id = 0; + uint32_t nr_meas_id = 0; + // vars - bool endc_supported = false; - rrc_endc_cfg_t endc_cfg = {}; + rrc_endc_cfg_t endc_cfg = {}; + uint16_t nr_rnti = SRSRAN_INVALID_RNTI; // C-RNTI assigned to UE on NR side asn1::rrc::rrc_conn_recfg_complete_s pending_recfg_complete; // fixed ENDC variables @@ -100,37 +129,55 @@ private: private: srslog::basic_logger& logger; }; - struct wait_add_complete_st {}; // user needs to complete RA procedure and send C-RNTI CE - struct endc_activated_st {}; // user has enabled EN-DC successfully and is currently served + struct wait_add_complete_st {}; // user needs to complete RA procedure and send C-RNTI CE + struct endc_activated_st {}; // user has enabled EN-DC successfully and is currently served + struct wait_sgnb_rel_req_resp_st {}; // release EN-DC + struct endc_disabled_st {}; // EN-DC disabled // FSM guards + bool requires_rel_req(const sgnb_rel_req_ev& ev); // FSM transition handlers void handle_sgnb_add_req_ack(wait_sgnb_add_req_resp_st& s, const sgnb_add_req_ack_ev& ev); + void handle_sgnb_rel_req(const sgnb_rel_req_ev& ev); + void handle_rrc_reest(endc_activated_st& s, const rrc_reest_rx_ev& ev); + void handle_endc_disabled(const disable_endc_ev& ev); protected: // states - state_list + state_list states{this, endc_deactivated_st{}, wait_sgnb_add_req_resp_st{}, prepare_recfg_st{this}, wait_add_complete_st{}, - endc_activated_st{}}; + endc_activated_st{}, + wait_sgnb_rel_req_resp_st{}, + endc_disabled_st{}}; // transitions using fsm = rrc_endc; // clang-format off - using transitions = transition_table< + using transitions = transition_table< // Start Target Event Action Guard // +---------------------------+--------------------------+------------------------+------------------------------+-------------------------+ - row< endc_deactivated_st, wait_sgnb_add_req_resp_st, sgnb_add_req_sent_ev, nullptr >, + row< endc_deactivated_st, wait_sgnb_add_req_resp_st, sgnb_add_req_sent_ev, nullptr >, // +---------------------------+--------------------------+------------------------+------------------------------+-------------------------+ - row< wait_sgnb_add_req_resp_st, prepare_recfg_st, sgnb_add_req_ack_ev, &fsm::handle_sgnb_add_req_ack >, - row< wait_sgnb_add_req_resp_st, endc_deactivated_st, sgnb_add_req_reject_ev >, - row< prepare_recfg_st, wait_add_complete_st, rrc_recfg_sent_ev >, - row< wait_add_complete_st, endc_activated_st, sgnb_add_complete_ev > + row< wait_sgnb_add_req_resp_st, prepare_recfg_st, sgnb_add_req_ack_ev, &fsm::handle_sgnb_add_req_ack >, + row< wait_sgnb_add_req_resp_st, endc_deactivated_st, sgnb_add_req_reject_ev >, + row< prepare_recfg_st, wait_add_complete_st, rrc_recfg_sent_ev >, + row< wait_add_complete_st, endc_activated_st, sgnb_add_complete_ev >, + upd< endc_activated_st, rrc_reest_rx_ev, &fsm::handle_rrc_reest >, // +---------------------------+--------------------------+------------------------+------------------------------+-------------------------+ + to_state< wait_sgnb_rel_req_resp_st, sgnb_rel_req_ev, &fsm::handle_sgnb_rel_req, &fsm::requires_rel_req >, + row< wait_sgnb_rel_req_resp_st, endc_deactivated_st, sgnb_rel_req_ack_ev >, + to_state< endc_disabled_st, disable_endc_ev, &fsm::handle_endc_disabled > >; // clang-format on }; diff --git a/srsenb/hdr/stack/rrc/rrc_nr.h b/srsenb/hdr/stack/rrc/rrc_nr.h index a929abfab..c5502078a 100644 --- a/srsenb/hdr/stack/rrc/rrc_nr.h +++ b/srsenb/hdr/stack/rrc/rrc_nr.h @@ -78,7 +78,7 @@ public: int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) final; /// User manegement - int add_user(uint16_t rnti); + int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg); void rem_user(uint16_t rnti); int update_user(uint16_t new_rnti, uint16_t old_rnti); void set_activity_user(uint16_t rnti); @@ -95,8 +95,9 @@ public: void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) final; // Interface for EUTRA RRC - int sgnb_addition_request(uint16_t rnti, const sgnb_addition_req_params_t& params); - int sgnb_reconfiguration_complete(uint16_t rnti, asn1::dyn_octstring reconfig_response); + void sgnb_addition_request(uint16_t rnti, const sgnb_addition_req_params_t& params); + void sgnb_reconfiguration_complete(uint16_t rnti, const asn1::dyn_octstring& reconfig_response) final; + void sgnb_release_request(uint16_t nr_rnti) final; // Interfaces for NGAP int ue_set_security_cfg_key(uint16_t rnti, const asn1::fixed_bitstring<256, false, true>& key); @@ -104,6 +105,7 @@ public: int ue_set_security_cfg_capabilities(uint16_t rnti, const asn1::ngap_nr::ue_security_cap_s& caps); int start_security_mode_procedure(uint16_t rnti); int establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran::const_byte_span nas_pdu, uint32_t lcid); + int release_bearers(uint16_t rnti); void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu); int set_aggregate_max_bitrate(uint16_t rnti, const asn1::ngap_nr::ue_aggregate_maximum_bit_rate_s& rates); int allocate_lcid(uint16_t rnti); @@ -111,7 +113,7 @@ public: class ue { public: - ue(rrc_nr* parent_, uint16_t rnti_); + ue(rrc_nr* parent_, uint16_t rnti_, const sched_nr_ue_cfg_t& uecfg); void send_connection_setup(); void send_dl_ccch(asn1::rrc_nr::dl_ccch_msg_s* dl_dcch_msg); @@ -125,10 +127,11 @@ public: bool is_inactive() { return state == rrc_nr_state_t::RRC_INACTIVE; } bool is_endc() { return endc; } uint16_t get_eutra_rnti() { return eutra_rnti; } - void get_metrics(rrc_ue_metrics_t& ue_metrics) { ue_metrics = {}; /*TODO fill RRC metrics*/ }; + void get_metrics(rrc_ue_metrics_t& ue_metrics) { ue_metrics = {}; /*TODO fill RRC metrics*/ }; // setters - int pack_rrc_reconfiguration(); + int pack_rrc_reconfiguration(); + void deactivate_bearers(); private: rrc_nr* parent = nullptr; @@ -145,8 +148,8 @@ public: int pack_sp_cell_cfg_ded(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_sp_cell_cfg_ded_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); - int pack_sp_cell_cfg_ded_init_dl_bwp_pdcch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_sp_cell_cfg_ded_init_dl_bwp_pdsch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); + int pack_sp_cell_cfg_ded_init_dl_bwp_radio_link_monitoring(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_sp_cell_cfg_ded_ul_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_sp_cell_cfg_ded_ul_cfg_init_ul_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); @@ -155,9 +158,6 @@ public: int pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); - int pack_sp_cell_cfg_ded_csi_meas_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); - int pack_sp_cell_cfg_ded_csi_meas_cfg_csi_report_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); - int pack_recfg_with_sync(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_recfg_with_sync_sp_cell_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); @@ -167,8 +167,6 @@ public: asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); - int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdcch_cfg_common( - asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); int pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common( asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); @@ -182,11 +180,6 @@ public: int pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bwp_pusch_cfg_common( asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); - int pack_recfg_with_sync_sp_cell_cfg_common_ssb_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); - - int - pack_recfg_with_sync_sp_cell_cfg_common_tdd_ul_dl_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack); - int pack_nr_radio_bearer_config(asn1::dyn_octstring& packed_nr_bearer_config); int add_drb(); @@ -226,8 +219,9 @@ private: srsran::task_sched_handle task_sched; // derived - uint32_t slot_dur_ms = 0; - srslog::basic_logger& logger; + uint32_t slot_dur_ms = 0; + srslog::basic_logger& logger; + asn1::rrc_nr::sp_cell_cfg_s base_sp_cell_cfg; // vars std::map > users; diff --git a/srsenb/hdr/x2_adapter.h b/srsenb/hdr/x2_adapter.h index 71c09b352..de16cbdc8 100644 --- a/srsenb/hdr/x2_adapter.h +++ b/srsenb/hdr/x2_adapter.h @@ -48,45 +48,44 @@ namespace srsenb { class x2_adapter final : public x2_interface { public: - x2_adapter() = default; - - // init functions to set handle to stacks - void set_eutra_stack(enb_stack_lte* eutra_stack_) { eutra_stack = eutra_stack_; } - - void set_nr_stack(gnb_stack_nr* nr_stack_) { nr_stack = nr_stack_; } + x2_adapter(enb_stack_lte* eutra_stack_, gnb_stack_nr* nr_stack_) : + nr_stack(nr_stack_), eutra_stack(eutra_stack_), logger(srslog::fetch_basic_logger("X2")) + {} /// rrc_nr_interface_rrc - int sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) + void sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) override { if (nr_stack == nullptr) { - return SRSRAN_ERROR; + logger.error("SgNB Addition Requested for inexistent NR stack"); + return; } - return nr_stack->sgnb_addition_request(eutra_rnti, params); + nr_stack->sgnb_addition_request(eutra_rnti, params); } - int sgnb_reconfiguration_complete(uint16_t eutra_rnti, asn1::dyn_octstring reconfig_response) + void sgnb_reconfiguration_complete(uint16_t eutra_rnti, const asn1::dyn_octstring& reconfig_response) override { if (nr_stack == nullptr) { - return SRSRAN_ERROR; + logger.error("SgNB Addition Requested for inexistent NR stack"); + return; } - return nr_stack->sgnb_reconfiguration_complete(eutra_rnti, reconfig_response); + nr_stack->sgnb_reconfiguration_complete(eutra_rnti, reconfig_response); } /// rrc_eutra_interface_rrc_nr - void sgnb_addition_ack(uint16_t eutra_rnti, sgnb_addition_ack_params_t params) + void sgnb_addition_ack(uint16_t eutra_rnti, sgnb_addition_ack_params_t params) override { if (eutra_stack == nullptr) { return; } eutra_stack->sgnb_addition_ack(eutra_rnti, params); } - void sgnb_addition_reject(uint16_t eutra_rnti) + void sgnb_addition_reject(uint16_t eutra_rnti) override { if (eutra_stack == nullptr) { return; } eutra_stack->sgnb_addition_reject(eutra_rnti); } - void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) + void sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) override { if (eutra_stack == nullptr) { return; @@ -94,7 +93,15 @@ public: eutra_stack->sgnb_addition_complete(eutra_rnti, nr_rnti); } - void set_activity_user(uint16_t eutra_rnti) + void sgnb_release_ack(uint16_t eutra_rnti) override + { + if (eutra_stack == nullptr) { + return; + } + eutra_stack->sgnb_release_ack(eutra_rnti); + } + + void set_activity_user(uint16_t eutra_rnti) override { if (eutra_stack == nullptr) { return; @@ -102,15 +109,24 @@ public: eutra_stack->set_activity_user(eutra_rnti); } + void sgnb_release_request(uint16_t nr_rnti) override + { + if (nr_stack == nullptr) { + logger.error("SgNB Addition Requested for inexistent NR stack"); + return; + } + nr_stack->sgnb_release_request(nr_rnti); + } + // pdcp_interface_gtpu - void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) + void write_sdu(uint16_t rnti, uint32_t lcid, srsran::unique_byte_buffer_t sdu, int pdcp_sn = -1) override { if (nr_stack == nullptr) { return; } nr_stack->write_sdu(rnti, lcid, std::move(sdu), pdcp_sn); } - std::map get_buffered_pdus(uint16_t rnti, uint32_t lcid) + std::map get_buffered_pdus(uint16_t rnti, uint32_t lcid) override { if (nr_stack == nullptr) { return {}; @@ -119,7 +135,7 @@ public: } // gtpu_interface_pdcp - void write_pdu(uint16_t rnti, uint32_t bearer_id, srsran::unique_byte_buffer_t pdu) + void write_pdu(uint16_t rnti, uint32_t bearer_id, srsran::unique_byte_buffer_t pdu) override { if (eutra_stack == nullptr) { return; @@ -128,8 +144,9 @@ public: } private: - enb_stack_lte* eutra_stack = nullptr; - gnb_stack_nr* nr_stack = nullptr; + enb_stack_lte* eutra_stack = nullptr; + gnb_stack_nr* nr_stack = nullptr; + srslog::basic_logger& logger; }; } // namespace srsenb diff --git a/srsenb/src/CMakeLists.txt b/srsenb/src/CMakeLists.txt index 63218daca..72fa2baf6 100644 --- a/srsenb/src/CMakeLists.txt +++ b/srsenb/src/CMakeLists.txt @@ -39,7 +39,7 @@ target_link_libraries(enb_cfg_parser srsran_common ${LIBCONFIGPP_LIBRARIES}) add_executable(srsenb main.cc enb.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) set(SRSENB_SOURCES srsenb_phy srsenb_stack srsenb_common srsenb_s1ap srsenb_upper srsenb_mac srsenb_rrc srslog system) -set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_gtpu srsran_rlc srsran_pdcp srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog system) +set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_gtpu srsran_rlc srsran_pdcp srsran_radio rrc_asn1 s1ap_asn1 enb_cfg_parser srslog support system) set(SRSENB_SOURCES ${SRSENB_SOURCES} srsgnb_stack srsgnb_ngap srsgnb_upper srsgnb_mac srsgnb_rrc) set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) diff --git a/srsenb/src/enb.cc b/srsenb/src/enb.cc index 475b785f5..b711c0fbb 100644 --- a/srsenb/src/enb.cc +++ b/srsenb/src/enb.cc @@ -22,6 +22,7 @@ #include "srsenb/hdr/enb.h" #include "srsenb/hdr/stack/enb_stack_lte.h" #include "srsenb/hdr/stack/gnb_stack_nr.h" +#include "srsenb/hdr/x2_adapter.h" #include "srsenb/src/enb_cfg_parser.h" #include "srsran/build_info.h" #include "srsran/common/enb_events.h" @@ -68,7 +69,6 @@ int enb::init(const all_args_t& args_) srsran::console("Error creating EUTRA stack.\n"); return SRSRAN_ERROR; } - x2.set_eutra_stack(tmp_eutra_stack.get()); } std::unique_ptr tmp_nr_stack; @@ -79,7 +79,11 @@ int enb::init(const all_args_t& args_) srsran::console("Error creating NR stack.\n"); return SRSRAN_ERROR; } - x2.set_nr_stack(tmp_nr_stack.get()); + } + + // If NR and EUTRA stacks were initiated, create an X2 adapter between the two. + if (tmp_nr_stack != nullptr and tmp_eutra_stack != nullptr) { + x2.reset(new x2_adapter(tmp_eutra_stack.get(), tmp_nr_stack.get())); } // Radio and PHY are RAT agnostic @@ -97,14 +101,14 @@ int enb::init(const all_args_t& args_) // initialize layers, if they exist if (tmp_eutra_stack) { - if (tmp_eutra_stack->init(args.stack, rrc_cfg, tmp_phy.get(), &x2) != SRSRAN_SUCCESS) { + if (tmp_eutra_stack->init(args.stack, rrc_cfg, tmp_phy.get(), x2.get()) != SRSRAN_SUCCESS) { srsran::console("Error initializing EUTRA stack.\n"); ret = SRSRAN_ERROR; } } if (tmp_nr_stack) { - if (tmp_nr_stack->init(args.stack, rrc_nr_cfg, tmp_phy.get(), &x2) != SRSRAN_SUCCESS) { + if (tmp_nr_stack->init(args.nr_stack, rrc_nr_cfg, tmp_phy.get(), x2.get()) != SRSRAN_SUCCESS) { srsran::console("Error initializing NR stack.\n"); ret = SRSRAN_ERROR; } @@ -157,14 +161,14 @@ void enb::stop() { if (started) { // tear down in reverse order - if (radio) { - radio->stop(); - } - if (phy) { phy->stop(); } + if (radio) { + radio->stop(); + } + if (eutra_stack) { eutra_stack->stop(); } diff --git a/srsenb/src/enb_cfg_parser.cc b/srsenb/src/enb_cfg_parser.cc index e512de142..f0988bf2f 100644 --- a/srsenb/src/enb_cfg_parser.cc +++ b/srsenb/src/enb_cfg_parser.cc @@ -907,8 +907,8 @@ static int parse_cell_list(all_args_t* args, rrc_cfg_t* rrc_cfg, Setting& root) HANDLEPARSERCODE(parse_required_field(cell_cfg.pci, cellroot, "pci")); cell_cfg.pci = cell_cfg.pci % SRSRAN_NUM_PCI; HANDLEPARSERCODE(parse_required_field(cell_cfg.dl_earfcn, cellroot, "dl_earfcn")); - parse_required_field(cell_cfg.dl_freq_hz, cellroot, "dl_freq"); - parse_required_field(cell_cfg.ul_freq_hz, cellroot, "ul_freq"); + parse_default_field(cell_cfg.dl_freq_hz, cellroot, "dl_freq", 0.0); // will be derived from DL EARFCN If not set + parse_default_field(cell_cfg.ul_freq_hz, cellroot, "ul_freq", 0.0); // will be derived from DL EARFCN If not set parse_default_field(cell_cfg.ul_earfcn, cellroot, "ul_earfcn", 0u); // will be derived from DL EARFCN If not set parse_default_field( cell_cfg.root_seq_idx, cellroot, "root_seq_idx", rrc_cfg->sibs[1].sib2().rr_cfg_common.prach_cfg.root_seq_idx); @@ -1031,6 +1031,17 @@ static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cf } } if (!dl_arfcn_valid) { + if (not bands.empty()) { + std::stringstream ss; + for (uint32_t& band : bands) { + ss << band << " "; + } + ERROR("DL ARFCN (%d) does not belong to band (%d). Recommended bands: %s", + it->dl_arfcn, + it->band, + ss.str().c_str()); + return SRSRAN_ERROR; + } ERROR("DL ARFCN (%d) is not valid for the specified band (%d)", it->dl_arfcn, it->band); return SRSRAN_ERROR; } @@ -1176,8 +1187,8 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr // update EUTRA RRC params for ENDC if (rrc_nr_cfg_->cell_list.size() == 1) { - rrc_cfg_->endc_cfg.nr_dl_arfcn = rrc_nr_cfg_->cell_list.at(0).dl_arfcn; - rrc_cfg_->endc_cfg.nr_band = rrc_nr_cfg_->cell_list.at(0).band; + rrc_cfg_->endc_cfg.abs_frequency_ssb = rrc_nr_cfg_->cell_list.at(0).ssb_absolute_freq_point; + rrc_cfg_->endc_cfg.nr_band = rrc_nr_cfg_->cell_list.at(0).band; rrc_cfg_->endc_cfg.ssb_period_offset.set_sf10_r15(); rrc_cfg_->endc_cfg.ssb_duration = asn1::rrc::mtc_ssb_nr_r15_s::ssb_dur_r15_opts::sf1; rrc_cfg_->endc_cfg.ssb_ssc = asn1::rrc::rs_cfg_ssb_nr_r15_s::subcarrier_spacing_ssb_r15_opts::khz15; @@ -1496,7 +1507,7 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_cfg_, phy_cfg_t* ph cfg.phy_cell.prach.is_nr = true; cfg.phy_cell.prach.config_idx = 0; cfg.phy_cell.prach.root_seq_idx = 0; - cfg.phy_cell.prach.freq_offset = phy_cfg_->prach_cnfg.prach_cfg_info.prach_freq_offset; + cfg.phy_cell.prach.freq_offset = 1; cfg.phy_cell.prach.num_ra_preambles = cfg.phy_cell.num_ra_preambles; cfg.phy_cell.prach.hs_flag = phy_cfg_->prach_cnfg.prach_cfg_info.high_speed_flag; cfg.phy_cell.prach.tdd_config.configured = (cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD); @@ -1541,15 +1552,27 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_cfg_, phy_cfg_t* ph cfg.dl_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(cfg.phy_cell.carrier.nof_prb, cfg.dl_arfcn); cfg.ul_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(cfg.phy_cell.carrier.nof_prb, cfg.ul_arfcn); - // Calculate SSB absolute frequency (in ARFCN notation) - if (band_helper.get_ssb_pattern(cfg.band, srsran_subcarrier_spacing_15kHz) != SRSRAN_SSB_PATTERN_INVALID) { - cfg.ssb_absolute_freq_point = - band_helper.get_abs_freq_ssb_arfcn(cfg.band, srsran_subcarrier_spacing_15kHz, cfg.dl_absolute_freq_point_a); + // Calculate SSB params depending on band/duplex + cfg.ssb_cfg.duplex_mode = band_helper.get_duplex_mode(cfg.band); + cfg.ssb_cfg.pattern = band_helper.get_ssb_pattern(cfg.band, srsran_subcarrier_spacing_15kHz); + if (cfg.ssb_cfg.pattern == SRSRAN_SSB_PATTERN_A) { + // 15kHz SSB SCS + cfg.ssb_cfg.scs = srsran_subcarrier_spacing_15kHz; } else { - cfg.ssb_absolute_freq_point = - band_helper.get_abs_freq_ssb_arfcn(cfg.band, srsran_subcarrier_spacing_30kHz, cfg.dl_absolute_freq_point_a); + // try to optain SSB pattern for same band with 30kHz SCS + cfg.ssb_cfg.pattern = band_helper.get_ssb_pattern(cfg.band, srsran_subcarrier_spacing_30kHz); + if (cfg.ssb_cfg.pattern == SRSRAN_SSB_PATTERN_B || cfg.ssb_cfg.pattern == SRSRAN_SSB_PATTERN_C) { + // SSB SCS is 30 kHz + cfg.ssb_cfg.scs = srsran_subcarrier_spacing_30kHz; + } else { + ERROR("Can't derive SSB pattern for band %d", cfg.band); + return SRSRAN_ERROR; + } } + // fill remaining SSB fields + cfg.ssb_absolute_freq_point = + band_helper.get_abs_freq_ssb_arfcn(cfg.band, cfg.ssb_cfg.scs, cfg.dl_absolute_freq_point_a); if (cfg.ssb_absolute_freq_point == 0) { ERROR("Can't derive SSB freq point for dl_arfcn %d and band %d", cfg.dl_arfcn, cfg.band); return SRSRAN_ERROR; @@ -1558,12 +1581,24 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_cfg_, phy_cfg_t* ph // Convert to frequency for PHY cfg.phy_cell.carrier.ssb_center_freq_hz = band_helper.nr_arfcn_to_freq(cfg.ssb_absolute_freq_point); - // TODO: set SSB config - cfg.ssb_cfg = {}; + cfg.ssb_cfg.center_freq_hz = cfg.phy_cell.carrier.dl_center_frequency_hz; + cfg.ssb_cfg.ssb_freq_hz = cfg.phy_cell.carrier.ssb_center_freq_hz; + cfg.ssb_cfg.periodicity_ms = 10; // TODO: make a param + cfg.ssb_cfg.beta_pss = 0.0; + cfg.ssb_cfg.beta_sss = 0.0; + cfg.ssb_cfg.beta_pbch = 0.0; + cfg.ssb_cfg.beta_pbch_dmrs = 0.0; + // set by PHY layer in worker_pool::set_common_cfg + cfg.ssb_cfg.srate_hz = 0.0; + cfg.ssb_cfg.scaling = 0.0; phy_cfg_->phy_cell_cfg_nr.push_back(cfg.phy_cell); } + // MAC-NR PCAP options + args_->nr_stack.mac.pcap.enable = args_->stack.mac_pcap.enable; + args_->nr_stack.log = args_->stack.log; + return SRSRAN_SUCCESS; } diff --git a/srsenb/src/enb_cfg_parser.h b/srsenb/src/enb_cfg_parser.h index 263e7ef66..2970f7f30 100644 --- a/srsenb/src/enb_cfg_parser.h +++ b/srsenb/src/enb_cfg_parser.h @@ -30,7 +30,6 @@ #include #include -#include "srsenb/hdr/stack/rrc/nr/rrc_config_nr.h" #include "srsenb/hdr/stack/rrc/rrc.h" #include "srsran/asn1/asn1_utils.h" @@ -40,6 +39,7 @@ using namespace libconfig; struct all_args_t; struct phy_cfg_t; +struct rrc_nr_cfg_t; bool sib_is_present(const asn1::rrc::sched_info_list_l& l, asn1::rrc::sib_type_e sib_num); diff --git a/srsenb/src/main.cc b/srsenb/src/main.cc index be16a38a6..72de4ad9d 100644 --- a/srsenb/src/main.cc +++ b/srsenb/src/main.cc @@ -29,10 +29,11 @@ #include "srsran/common/common_helper.h" #include "srsran/common/config_file.h" #include "srsran/common/crash_handler.h" -#include "srsran/common/signal_handler.h" #include "srsran/common/tsan_options.h" #include "srsran/srslog/event_trace.h" #include "srsran/srslog/srslog.h" +#include "srsran/support/emergency_handlers.h" +#include "srsran/support/signal_handler.h" #include #include @@ -54,8 +55,10 @@ namespace bpo = boost::program_options; /********************************************************************** * Program arguments processing ***********************************************************************/ -string config_file; -static bool stdout_ts_enable = false; +string config_file; +static bool stdout_ts_enable = false; +static srslog::sink* log_sink = nullptr; +static std::atomic running = {true}; void parse_args(all_args_t* args, int argc, char* argv[]) { @@ -140,6 +143,7 @@ void parse_args(all_args_t* args, int argc, char* argv[]) /* PCAP */ ("pcap.enable", bpo::value(&args->stack.mac_pcap.enable)->default_value(false), "Enable MAC packet captures for wireshark") ("pcap.filename", bpo::value(&args->stack.mac_pcap.filename)->default_value("enb_mac.pcap"), "MAC layer capture filename") + ("pcap.nr_filename", bpo::value(&args->nr_stack.mac.pcap.filename)->default_value("enb_mac_nr.pcap"), "NR MAC layer capture filename") ("pcap.s1ap_enable", bpo::value(&args->stack.s1ap_pcap.enable)->default_value(false), "Enable S1AP packet captures for wireshark") ("pcap.s1ap_filename", bpo::value(&args->stack.s1ap_pcap.filename)->default_value("enb_s1ap.pcap"), "S1AP layer capture filename") ("pcap.mac_net_enable", bpo::value(&args->stack.mac_pcap_net.enable)->default_value(false), "Enable MAC network captures") @@ -216,41 +220,41 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("channel.ul.hst.init_time_s", bpo::value(&args->phy.ul_channel_args.hst_init_time_s)->default_value(0), "Initial time in seconds") /* Expert section */ - ("expert.metrics_period_secs", bpo::value(&args->general.metrics_period_secs)->default_value(1.0), "Periodicity for metrics in seconds") - ("expert.metrics_csv_enable", bpo::value(&args->general.metrics_csv_enable)->default_value(false), "Write metrics to CSV file") - ("expert.metrics_csv_filename", bpo::value(&args->general.metrics_csv_filename)->default_value("/tmp/enb_metrics.csv"), "Metrics CSV filename") - ("expert.pusch_max_its", bpo::value(&args->phy.pusch_max_its)->default_value(8), "Maximum number of turbo decoder iterations") - ("expert.pusch_8bit_decoder", bpo::value(&args->phy.pusch_8bit_decoder)->default_value(false), "Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental)") - ("expert.pusch_meas_evm", bpo::value(&args->phy.pusch_meas_evm)->default_value(false), "Enable/Disable PUSCH EVM measure") - ("expert.tx_amplitude", bpo::value(&args->phy.tx_amplitude)->default_value(0.6), "Transmit amplitude factor") - ("expert.nof_phy_threads", bpo::value(&args->phy.nof_phy_threads)->default_value(3), "Number of PHY threads") - ("expert.nof_prach_threads", bpo::value(&args->phy.nof_prach_threads)->default_value(1), "Number of PRACH workers per carrier. Only 1 or 0 is supported") - ("expert.max_prach_offset_us", bpo::value(&args->phy.max_prach_offset_us)->default_value(30), "Maximum allowed RACH offset (in us)") - ("expert.equalizer_mode", bpo::value(&args->phy.equalizer_mode)->default_value("mmse"), "Equalizer mode") + ("expert.metrics_period_secs", bpo::value(&args->general.metrics_period_secs)->default_value(1.0), "Periodicity for metrics in seconds.") + ("expert.metrics_csv_enable", bpo::value(&args->general.metrics_csv_enable)->default_value(false), "Write metrics to CSV file.") + ("expert.metrics_csv_filename", bpo::value(&args->general.metrics_csv_filename)->default_value("/tmp/enb_metrics.csv"), "Metrics CSV filename.") + ("expert.pusch_max_its", bpo::value(&args->phy.pusch_max_its)->default_value(8), "Maximum number of turbo decoder iterations for LTE.") + ("expert.pusch_8bit_decoder", bpo::value(&args->phy.pusch_8bit_decoder)->default_value(false), "Use 8-bit for LLR representation and turbo decoder trellis computation (Experimental).") + ("expert.pusch_meas_evm", bpo::value(&args->phy.pusch_meas_evm)->default_value(false), "Enable/Disable PUSCH EVM measure.") + ("expert.tx_amplitude", bpo::value(&args->phy.tx_amplitude)->default_value(0.6), "Transmit amplitude factor.") + ("expert.nof_phy_threads", bpo::value(&args->phy.nof_phy_threads)->default_value(3), "Number of PHY threads.") + ("expert.nof_prach_threads", bpo::value(&args->phy.nof_prach_threads)->default_value(1), "Number of PRACH workers per carrier. Only 1 or 0 is supported.") + ("expert.max_prach_offset_us", bpo::value(&args->phy.max_prach_offset_us)->default_value(30), "Maximum allowed RACH offset (in us).") + ("expert.equalizer_mode", bpo::value(&args->phy.equalizer_mode)->default_value("mmse"), "Equalizer mode.") ("expert.estimator_fil_w", bpo::value(&args->phy.estimator_fil_w)->default_value(0.1), "Chooses the coefficients for the 3-tap channel estimator centered filter.") ("expert.lte_sample_rates", bpo::value(&use_standard_lte_rates)->default_value(false), "Whether to use default LTE sample rates instead of shorter variants.") - ("expert.report_json_enable", bpo::value(&args->general.report_json_enable)->default_value(false), "Write eNB report to JSON file (default disabled)") - ("expert.report_json_filename", bpo::value(&args->general.report_json_filename)->default_value("/tmp/enb_report.json"), "Report JSON filename (default /tmp/enb_report.json)") - ("expert.report_json_asn1_oct", bpo::value(&args->general.report_json_asn1_oct)->default_value(false), "Prints ASN1 messages encoded as an octet string instead of plain text in the JSON report file") - ("expert.alarms_log_enable", bpo::value(&args->general.alarms_log_enable)->default_value(false), "Enable Alarms logging (default diabled)") - ("expert.alarms_filename", bpo::value(&args->general.alarms_filename)->default_value("/tmp/enb_alarms.log"), "Alarms logging filename (default /tmp/alarms.log)") - ("expert.tracing_enable", bpo::value(&args->general.tracing_enable)->default_value(false), "Events tracing") - ("expert.tracing_filename", bpo::value(&args->general.tracing_filename)->default_value("/tmp/enb_tracing.log"), "Tracing events filename") - ("expert.tracing_buffcapacity", bpo::value(&args->general.tracing_buffcapacity)->default_value(1000000), "Tracing buffer capcity") - ("expert.stdout_ts_enable", bpo::value(&stdout_ts_enable)->default_value(false), "Prints once per second the timestamp into stdout") + ("expert.report_json_enable", bpo::value(&args->general.report_json_enable)->default_value(false), "Write eNB report to JSON file (default disabled).") + ("expert.report_json_filename", bpo::value(&args->general.report_json_filename)->default_value("/tmp/enb_report.json"), "Report JSON filename (default /tmp/enb_report.json).") + ("expert.report_json_asn1_oct", bpo::value(&args->general.report_json_asn1_oct)->default_value(false), "Prints ASN1 messages encoded as an octet string instead of plain text in the JSON report file.") + ("expert.alarms_log_enable", bpo::value(&args->general.alarms_log_enable)->default_value(false), "Enable Alarms logging (default diabled).") + ("expert.alarms_filename", bpo::value(&args->general.alarms_filename)->default_value("/tmp/enb_alarms.log"), "Alarms logging filename (default /tmp/alarms.log).") + ("expert.tracing_enable", bpo::value(&args->general.tracing_enable)->default_value(false), "Events tracing.") + ("expert.tracing_filename", bpo::value(&args->general.tracing_filename)->default_value("/tmp/enb_tracing.log"), "Tracing events filename.") + ("expert.tracing_buffcapacity", bpo::value(&args->general.tracing_buffcapacity)->default_value(1000000), "Tracing buffer capcity.") + ("expert.stdout_ts_enable", bpo::value(&stdout_ts_enable)->default_value(false), "Prints once per second the timestamp into stdout.") ("expert.rrc_inactivity_timer", bpo::value(&args->general.rrc_inactivity_timer)->default_value(30000), "Inactivity timer in ms.") - ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds") + ("expert.print_buffer_state", bpo::value(&args->general.print_buffer_state)->default_value(false), "Prints on the console the buffer state every 10 seconds.") ("expert.eea_pref_list", bpo::value(&args->general.eea_pref_list)->default_value("EEA0, EEA2, EEA1"), "Ordered preference list for the selection of encryption algorithm (EEA) (default: EEA0, EEA2, EEA1).") ("expert.eia_pref_list", bpo::value(&args->general.eia_pref_list)->default_value("EIA2, EIA1, EIA0"), "Ordered preference list for the selection of integrity algorithm (EIA) (default: EIA2, EIA1, EIA0).") - ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization") + ("expert.nof_prealloc_ues", bpo::value(&args->stack.mac.nof_prealloc_ues)->default_value(8), "Number of UE resources to preallocate during eNB initialization.") ("expert.lcid_padding", bpo::value(&args->stack.mac.lcid_padding)->default_value(3), "LCID on which to put MAC padding") - ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release (default 100)") - ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release (default 100)") - ("expert.gtpu_tunnel_timeout", bpo::value(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(0), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU. (0 for infinity)") - ("expert.rlf_release_timer_ms", bpo::value(&args->general.rlf_release_timer_ms)->default_value(4000), "Time taken by eNB to release UE context after it detects an RLF") + ("expert.max_mac_dl_kos", bpo::value(&args->general.max_mac_dl_kos)->default_value(100), "Maximum number of consecutive KOs in DL before triggering the UE's release (default 100).") + ("expert.max_mac_ul_kos", bpo::value(&args->general.max_mac_ul_kos)->default_value(100), "Maximum number of consecutive KOs in UL before triggering the UE's release (default 100).") + ("expert.gtpu_tunnel_timeout", bpo::value(&args->stack.gtpu_indirect_tunnel_timeout_msec)->default_value(0), "Maximum time that GTPU takes to release indirect forwarding tunnel since the last received GTPU PDU (0 for infinity).") + ("expert.rlf_release_timer_ms", bpo::value(&args->general.rlf_release_timer_ms)->default_value(4000), "Time taken by eNB to release UE context after it detects an RLF.") ("expert.extended_cp", bpo::value(&args->phy.extended_cp)->default_value(false), "Use extended cyclic prefix") - ("expert.ts1_reloc_prep_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_prep_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocPrep Expiry Timeout value in milliseconds") - ("expert.ts1_reloc_overall_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_overall_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocOverall Expiry Timeout value in milliseconds") + ("expert.ts1_reloc_prep_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_prep_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocPrep Expiry Timeout value in milliseconds.") + ("expert.ts1_reloc_overall_timeout", bpo::value(&args->stack.s1ap.ts1_reloc_overall_timeout)->default_value(10000), "S1AP TS 36.413 TS1RelocOverall Expiry Timeout value in milliseconds.") ("expert.rlf_min_ul_snr_estim", bpo::value(&args->stack.mac.rlf_min_ul_snr_estim)->default_value(-2), "SNR threshold in dB below which the eNB is notified with rlf ko.") // eMBMS section @@ -260,14 +264,16 @@ void parse_args(all_args_t* args, int argc, char* argv[]) ("embms.mcs", bpo::value(&args->stack.embms.mcs)->default_value(20), "Modulation and Coding scheme of MBMS traffic.") // NR section - ("scheduler.tb_len", bpo::value(&args->stack.mac.nr_tb_size)->default_value(1520), "Default TB size") + ("scheduler.nr_pdsch_mcs", bpo::value(&args->nr_stack.mac.sched_cfg.fixed_dl_mcs)->default_value(28), "Fixed NR DL MCS (-1 for dynamic).") + ("scheduler.nr_pusch_mcs", bpo::value(&args->nr_stack.mac.sched_cfg.fixed_ul_mcs)->default_value(28), "Fixed NR UL MCS (-1 for dynamic).") + ("expert.nr_pusch_max_its", bpo::value(&args->phy.nr_pusch_max_its)->default_value(10), "Maximum number of LDPC iterations for NR.") // VNF params - ("vnf.type", bpo::value(&args->phy.vnf_args.type)->default_value("gnb"), "VNF instance type [gnb,ue]") - ("vnf.addr", bpo::value(&args->phy.vnf_args.bind_addr)->default_value("localhost"), "Address to bind VNF interface") - ("vnf.port", bpo::value(&args->phy.vnf_args.bind_port)->default_value(3333), "Bind port") - ("log.vnf_level", bpo::value(&args->phy.vnf_args.log_level), "VNF log level") - ("log.vnf_hex_limit", bpo::value(&args->phy.vnf_args.log_hex_limit), "VNF log hex dump limit") + ("vnf.type", bpo::value(&args->phy.vnf_args.type)->default_value("gnb"), "VNF instance type [gnb,ue].") + ("vnf.addr", bpo::value(&args->phy.vnf_args.bind_addr)->default_value("localhost"), "Address to bind VNF interface.") + ("vnf.port", bpo::value(&args->phy.vnf_args.bind_port)->default_value(3333), "Bind port.") + ("log.vnf_level", bpo::value(&args->phy.vnf_args.log_level), "VNF log level.") + ("log.vnf_hex_limit", bpo::value(&args->phy.vnf_args.log_hex_limit), "VNF log hex dump limit.") ; // Positional options - config file location @@ -540,9 +546,26 @@ static size_t fixup_log_file_maxsize(int x) return (x < 0) ? 0 : size_t(x) * 1024u; } +extern "C" void srsran_dft_exit(); +static void emergency_cleanup_handler(void* data) +{ + srslog::flush(); + if (log_sink) { + log_sink->flush(); + } + srsran_dft_exit(); +} + +static void signal_handler() +{ + running = false; +} + int main(int argc, char* argv[]) { - srsran_register_signal_handler(); + srsran_register_signal_handler(signal_handler); + add_emergency_cleanup_handler(emergency_cleanup_handler, nullptr); + all_args_t args = {}; srsran::metrics_hub metricshub; metrics_stdout metrics_screen; diff --git a/srsenb/src/metrics_stdout.cc b/srsenb/src/metrics_stdout.cc index 87d7cff67..288707383 100644 --- a/srsenb/src/metrics_stdout.cc +++ b/srsenb/src/metrics_stdout.cc @@ -147,10 +147,15 @@ void metrics_stdout::set_metrics_helper(uint32_t num_ue } else { fmt::print(" {:>5.5}", "n/a"); } - int phr = (is_nr) ? mac.ues[i].phr : mac.ues[i].phr; - fmt::print(" {:>3}", int(phr)); + int phr = mac.ues[i].phr; if (not isnan(phr)) { - fmt::print(" {:>2}", int(phr)); + fmt::print(" {:>2}", phr); + } else { + fmt::print(" {:>2}", 0); + } + float ul_mcs = (is_nr) ? mac.ues[i].ul_mcs : phy[i].ul.mcs; + if (not isnan(ul_mcs)) { + fmt::print(" {:>2}", int(ul_mcs)); } else { fmt::print(" {:>2}", 0); } diff --git a/srsenb/src/phy/nr/slot_worker.cc b/srsenb/src/phy/nr/slot_worker.cc index dbd53e7cd..1ba19a27a 100644 --- a/srsenb/src/phy/nr/slot_worker.cc +++ b/srsenb/src/phy/nr/slot_worker.cc @@ -83,9 +83,11 @@ bool slot_worker::init(const args_t& args) // Prepare UL arguments srsran_gnb_ul_args_t ul_args = {}; ul_args.pusch.measure_time = true; + ul_args.pusch.measure_evm = true; ul_args.pusch.max_layers = args.nof_rx_ports; ul_args.pusch.max_prb = args.nof_max_prb; ul_args.nof_max_prb = args.nof_max_prb; + ul_args.pusch_min_snr_dB = args.pusch_min_snr_dB; // Initialise UL if (srsran_gnb_ul_init(&gnb_ul, rx_buffer[0], &ul_args) < SRSRAN_SUCCESS) { @@ -204,6 +206,7 @@ bool slot_worker::work_ul() srsran_gnb_ul_pucch_info(&gnb_ul, &pucch.candidates[0].resource, &pucch_info[best_candidate].uci_data, + &pucch_info[best_candidate].csi, str.data(), (uint32_t)str.size()); @@ -233,6 +236,9 @@ bool slot_worker::work_ul() return false; } + // Extract DMRS information + pusch_info.csi = gnb_ul.dmrs.csi; + // Inform stack if (stack.pusch_info(ul_slot_cfg, pusch_info) < SRSRAN_SUCCESS) { logger.error("Error pushing PUSCH information to stack"); @@ -244,7 +250,13 @@ bool slot_worker::work_ul() std::array str; srsran_gnb_ul_pusch_info(&gnb_ul, &pusch.sch, &pusch_info.pusch_data, str.data(), (uint32_t)str.size()); - logger.info("PUSCH: %s", str.data()); + if (logger.debug.enabled()) { + std::array str_extra = {}; + srsran_sch_cfg_nr_info(&pusch.sch, str_extra.data(), (uint32_t)str_extra.size()); + logger.info("PUSCH: %s\n%s", str.data(), str_extra.data()); + } else { + logger.info("PUSCH: %s", str.data()); + } } } @@ -401,10 +413,12 @@ void slot_worker::work_imp() common.worker_end(context, true, tx_rf_buffer); } + bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_, const srsran_ssb_cfg_t& ssb_cfg_) { + std::lock_guard lock(mutex); // Set gNb DL carrier if (srsran_gnb_dl_set_carrier(&gnb_dl, &carrier) < SRSRAN_SUCCESS) { logger.error("Error setting DL carrier"); diff --git a/srsenb/src/phy/nr/worker_pool.cc b/srsenb/src/phy/nr/worker_pool.cc index 91d84ceea..2ddc4c5cb 100644 --- a/srsenb/src/phy/nr/worker_pool.cc +++ b/srsenb/src/phy/nr/worker_pool.cc @@ -71,7 +71,8 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li w_args.nof_rx_ports = cell_list[cell_index].carrier.max_mimo_layers; w_args.rf_port = cell_list[cell_index].rf_port; w_args.srate_hz = srate_hz; - w_args.pusch_max_nof_iter = args.pusch_max_nof_iter; + w_args.pusch_max_its = args.pusch_max_its; + w_args.pusch_min_snr_dB = args.pusch_min_snr_dB; if (not w->init(w_args)) { return false; diff --git a/srsenb/src/phy/phy.cc b/srsenb/src/phy/phy.cc index a133f1c2f..de61f7f30 100644 --- a/srsenb/src/phy/phy.cc +++ b/srsenb/src/phy/phy.cc @@ -109,7 +109,7 @@ int phy::init(const phy_args_t& args, stack_interface_phy_nr& stack_nr_, enb_time_interface* enb_) { - if (init(args, cfg, radio_, stack_lte_, enb_) != SRSRAN_SUCCESS) { + if (init_lte(args, cfg, radio_, stack_lte_, enb_) != SRSRAN_SUCCESS) { phy_log.error("Couldn't initialize LTE PHY"); return SRSRAN_ERROR; } @@ -119,6 +119,9 @@ int phy::init(const phy_args_t& args, return SRSRAN_ERROR; } + tx_rx.init(enb_, radio, <e_workers, &workers_common, &prach, SF_RECV_THREAD_PRIO); + initialized = true; + return SRSRAN_SUCCESS; } @@ -127,6 +130,23 @@ int phy::init(const phy_args_t& args, srsran::radio_interface_phy* radio_, stack_interface_phy_lte* stack_lte_, enb_time_interface* enb_) +{ + if (init_lte(args, cfg, radio_, stack_lte_, enb_) != SRSRAN_SUCCESS) { + phy_log.error("Couldn't initialize LTE PHY"); + return SRSRAN_ERROR; + } + + tx_rx.init(enb_, radio, <e_workers, &workers_common, &prach, SF_RECV_THREAD_PRIO); + initialized = true; + + return SRSRAN_SUCCESS; +} + +int phy::init_lte(const phy_args_t& args, + const phy_cfg_t& cfg, + srsran::radio_interface_phy* radio_, + stack_interface_phy_lte* stack_lte_, + enb_time_interface* enb_) { if (cfg.phy_cell_cfg.size() > SRSRAN_MAX_CARRIERS) { phy_log.error( @@ -175,11 +195,6 @@ int phy::init(const phy_args_t& args, } prach.set_max_prach_offset_us(args.max_prach_offset_us); - // Warning this must be initialized after all workers have been added to the pool - tx_rx.init(enb_, radio, <e_workers, &workers_common, &prach, SF_RECV_THREAD_PRIO); - - initialized = true; - return SRSRAN_SUCCESS; } @@ -342,6 +357,7 @@ int phy::init_nr(const phy_args_t& args, const phy_cfg_t& cfg, stack_interface_p worker_args.nof_phy_threads = args.nof_phy_threads; worker_args.log.phy_level = args.log.phy_level; worker_args.log.phy_hex_limit = args.log.phy_hex_limit; + worker_args.pusch_max_its = args.nr_pusch_max_its; if (not nr_workers->init(worker_args, cfg.phy_cell_cfg_nr)) { return SRSRAN_ERROR; diff --git a/srsenb/src/stack/gnb_stack_nr.cc b/srsenb/src/stack/gnb_stack_nr.cc index c81c3ccd8..f0d8ce690 100644 --- a/srsenb/src/stack/gnb_stack_nr.cc +++ b/srsenb/src/stack/gnb_stack_nr.cc @@ -39,10 +39,11 @@ gnb_stack_nr::gnb_stack_nr(srslog::sink& log_sink) : pdcp(&task_sched, pdcp_logger), rlc(rlc_logger) { - ue_task_queue = task_sched.make_task_queue(); - sync_task_queue = task_sched.make_task_queue(); - gtpu_task_queue = task_sched.make_task_queue(); - mac_task_queue = task_sched.make_task_queue(); + sync_task_queue = task_sched.make_task_queue(); + gtpu_task_queue = task_sched.make_task_queue(); + metrics_task_queue = task_sched.make_task_queue(); + gnb_task_queue = task_sched.make_task_queue(); + x2_task_queue = task_sched.make_task_queue(); } gnb_stack_nr::~gnb_stack_nr() @@ -55,14 +56,13 @@ std::string gnb_stack_nr::get_type() return "nr"; } -int gnb_stack_nr::init(const srsenb::stack_args_t& args_, - const rrc_nr_cfg_t& rrc_cfg_, - phy_interface_stack_nr* phy_, - x2_interface* x2_) +int gnb_stack_nr::init(const gnb_stack_args_t& args_, + const rrc_nr_cfg_t& rrc_cfg_, + phy_interface_stack_nr* phy_, + x2_interface* x2_) { args = args_; - // rrc_cfg = rrc_cfg_; - phy = phy_; + phy = phy_; // setup logging mac_logger.set_level(srslog::str_to_basic_level(args.log.mac_level)); @@ -78,10 +78,7 @@ int gnb_stack_nr::init(const srsenb::stack_args_t& args_, stack_logger.set_hex_dump_max_size(args.log.stack_hex_limit); // Init all layers - mac_nr_args_t mac_args = {}; - mac_args.pcap = args.mac_pcap; - mac_args.pcap.filename = "/tmp/enb_mac_nr.pcap"; - if (mac.init(mac_args, phy, nullptr, &rlc, &rrc) != SRSRAN_SUCCESS) { + if (mac.init(args.mac, phy, nullptr, &rlc, &rrc) != SRSRAN_SUCCESS) { stack_logger.error("Couldn't initialize MAC-NR"); return SRSRAN_ERROR; } @@ -109,15 +106,23 @@ int gnb_stack_nr::init(const srsenb::stack_args_t& args_, void gnb_stack_nr::stop() { if (running) { - rrc.stop(); - pdcp.stop(); - mac.stop(); - - srsran::get_background_workers().stop(); - running = false; + gnb_task_queue.push([this]() { stop_impl(); }); + wait_thread_finish(); } } +void gnb_stack_nr::stop_impl() +{ + rrc.stop(); + pdcp.stop(); + mac.stop(); + + task_sched.stop(); + srsran::get_background_workers().stop(); + + running = false; +} + bool gnb_stack_nr::switch_on() { // Nothing to be done here @@ -142,9 +147,7 @@ void gnb_stack_nr::tti_clock_impl() task_sched.tic(); } -void gnb_stack_nr::process_pdus() -{ -} +void gnb_stack_nr::process_pdus() {} /******************************************************** * @@ -154,8 +157,27 @@ void gnb_stack_nr::process_pdus() bool gnb_stack_nr::get_metrics(srsenb::stack_metrics_t* metrics) { + bool metrics_ready = false; + + // use stack thread to query RRC metrics + auto ret = metrics_task_queue.try_push([this, metrics, &metrics_ready]() { + rrc.get_metrics(metrics->rrc); + { + std::lock_guard lock(metrics_mutex); + metrics_ready = true; + } + metrics_cvar.notify_one(); + }); + if (not ret.has_value()) { + return false; + } + + // obtain MAC metrics (do not use stack thread) mac.get_metrics(metrics->mac); - rrc.get_metrics(metrics->rrc); + + // wait for RRC result + std::unique_lock lock(metrics_mutex); + metrics_cvar.wait(lock, [&metrics_ready]() { return metrics_ready; }); return true; } diff --git a/srsenb/src/stack/mac/mac.cc b/srsenb/src/stack/mac/mac.cc index 2aae2e63d..e10e352d9 100644 --- a/srsenb/src/stack/mac/mac.cc +++ b/srsenb/src/stack/mac/mac.cc @@ -597,7 +597,7 @@ int mac::get_dl_sched(uint32_t tti_tx_dl, dl_sched_list_t& dl_sched_res_list) return 0; } - trace_threshold_complete_event("mac::get_dl_sched", "total_time", std::chrono::microseconds(100)); + trace_threshold_complete_event("mac::run_slot", "total_time", std::chrono::microseconds(100)); logger.set_context(TTI_SUB(tti_tx_dl, FDD_HARQ_DELAY_UL_MS)); if (do_padding) { add_padding(); @@ -841,9 +841,9 @@ int mac::get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res int requested_bytes = (mcs_data.tbs / 8 > (int)mch.mtch_sched[mtch_index].lcid_buffer_size) ? (mch.mtch_sched[mtch_index].lcid_buffer_size) : ((mcs_data.tbs / 8) - 2); - int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); - mch.pdu[0].lcid = current_lcid; - mch.pdu[0].nbytes = bytes_received; + int bytes_received = ue_db[SRSRAN_MRNTI]->read_pdu(current_lcid, mtch_payload_buffer, requested_bytes); + mch.pdu[0].lcid = current_lcid; + mch.pdu[0].nbytes = bytes_received; mch.mtch_sched[0].mtch_payload = mtch_payload_buffer; dl_sched_res->pdsch[0].dci.rnti = SRSRAN_MRNTI; if (bytes_received) { diff --git a/srsenb/src/stack/mac/nr/mac_nr.cc b/srsenb/src/stack/mac/nr/mac_nr.cc index ccce23618..711471e05 100644 --- a/srsenb/src/stack/mac/nr/mac_nr.cc +++ b/srsenb/src/stack/mac/nr/mac_nr.cc @@ -20,9 +20,9 @@ */ #include "srsenb/hdr/stack/mac/nr/mac_nr.h" -#include "srsenb/test/mac/nr/sched_nr_cfg_generators.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/log_helper.h" +#include "srsran/common/phy_cfg_nr_default.h" #include "srsran/common/rwlock_guard.h" #include "srsran/common/standard_streams.h" #include "srsran/common/string_helpers.h" @@ -85,14 +85,22 @@ void mac_nr::stop() } } +/// Called from metrics thread. +/// Note: This can contend for the same mutexes as the ones used by L1/L2 workers. +/// However, get_metrics is called infrequently enough to cause major halts in the L1/L2 void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics) { - srsran::rwlock_read_guard lock(rwlock); + // TODO: We should comment on the logic we follow to get the metrics. Some of them are retrieved from MAC, some + // others from the scheduler. + get_metrics_nolock(metrics); + sched.get_metrics(metrics); +} + +void mac_nr::get_metrics_nolock(srsenb::mac_metrics_t& metrics) +{ + srsran::rwlock_read_guard lock(rwmutex); metrics.ues.reserve(ue_db.size()); for (auto& u : ue_db) { - if (not sched.ue_exists(u.first)) { - continue; - } metrics.ues.emplace_back(); u.second->metrics_read(&metrics.ues.back()); } @@ -139,16 +147,14 @@ int mac_nr::ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) return SRSRAN_SUCCESS; } -uint16_t mac_nr::reserve_rnti(uint32_t enb_cc_idx) +uint16_t mac_nr::reserve_rnti(uint32_t enb_cc_idx, const sched_nr_ue_cfg_t& uecfg) { uint16_t rnti = alloc_ue(enb_cc_idx); if (rnti == SRSRAN_INVALID_RNTI) { return rnti; } - // Add new user to the scheduler so that it can RX/TX SRB0 - srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1); - sched.ue_cfg(rnti, ue_cfg); + sched.ue_cfg(rnti, uecfg); return rnti; } @@ -162,20 +168,34 @@ void mac_nr::rach_detected(const rach_info_t& rach_info) uint32_t enb_cc_idx = 0; stack_task_queue.push([this, rach_info, enb_cc_idx, rach_tprof_meas]() mutable { rach_tprof_meas.defer_stop(); - uint16_t rnti = reserve_rnti(enb_cc_idx); + + // Add new user to the scheduler so that it can RX/TX SRB0 + sched_nr_ue_cfg_t uecfg = {}; + uecfg.carriers.resize(1); + uecfg.carriers[0].active = true; + uecfg.carriers[0].cc = 0; + uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH; + srsran::phy_cfg_nr_default_t::reference_cfg_t ref_args{}; + ref_args.duplex = cell_config[0].duplex.mode == SRSRAN_DUPLEX_MODE_TDD + ? srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_TDD_CUSTOM_6_4 + : srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD; + uecfg.phy_cfg = srsran::phy_cfg_nr_default_t{ref_args}; + uecfg.phy_cfg.csi = {}; // disable CSI until RA is complete + + uint16_t rnti = reserve_rnti(enb_cc_idx, uecfg); // Log this event. ++detected_rachs[enb_cc_idx]; // Trigger scheduler RACH - srsenb::sched_nr_interface::dl_sched_rar_info_t rar_info = {}; - rar_info.preamble_idx = rach_info.preamble; - rar_info.temp_crnti = rnti; - rar_info.ta_cmd = rach_info.time_adv; - rar_info.prach_slot = slot_point{NUMEROLOGY_IDX, rach_info.slot_index}; + srsenb::sched_nr_interface::rar_info_t rar_info = {}; + rar_info.preamble_idx = rach_info.preamble; + rar_info.temp_crnti = rnti; + rar_info.ta_cmd = rach_info.time_adv; + rar_info.prach_slot = slot_point{NUMEROLOGY_IDX, rach_info.slot_index}; // TODO: fill remaining fields as required sched.dl_rach_info(enb_cc_idx, rar_info); - rrc->add_user(rnti); + rrc->add_user(rnti, uecfg); logger.info("RACH: slot=%d, cc=%d, preamble=%d, offset=%d, temp_crnti=0x%x", rach_info.slot_index, @@ -203,7 +223,7 @@ uint16_t mac_nr::alloc_ue(uint32_t enb_cc_idx) // Pre-check if rnti is valid { - srsran::rwlock_read_guard read_lock(rwlock); + srsran::rwlock_read_guard read_lock(rwmutex); if (not is_rnti_valid_nolock(rnti)) { continue; } @@ -213,7 +233,7 @@ uint16_t mac_nr::alloc_ue(uint32_t enb_cc_idx) std::unique_ptr ue_ptr = std::unique_ptr(new ue_nr(rnti, enb_cc_idx, &sched, rrc, rlc, phy, logger)); // Add UE to rnti map - srsran::rwlock_write_guard rw_lock(rwlock); + srsran::rwlock_write_guard rw_lock(rwmutex); if (not is_rnti_valid_nolock(rnti)) { continue; } @@ -231,7 +251,7 @@ uint16_t mac_nr::alloc_ue(uint32_t enb_cc_idx) // Remove UE from the perspective of L2/L3 int mac_nr::remove_ue(uint16_t rnti) { - srsran::rwlock_write_guard lock(rwlock); + srsran::rwlock_write_guard lock(rwmutex); if (is_rnti_active_nolock(rnti)) { sched.ue_rem(rnti); ue_db.erase(rnti); @@ -282,18 +302,20 @@ int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg) int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) { - logger.set_context(slot_cfg.idx); + logger.set_context(slot_cfg.idx - TX_ENB_DELAY); slot_point pdsch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx}; sched_nr_interface::dl_sched_res_t dl_res; - int ret = sched.get_dl_sched(pdsch_slot, 0, dl_res); + + // Run Scheduler + int ret = sched.run_slot(pdsch_slot, 0, dl_res); if (ret != SRSRAN_SUCCESS) { return ret; } dl_sched = dl_res.dl_sched; uint32_t rar_count = 0; - srsran::rwlock_read_guard rw_lock(rwlock); + srsran::rwlock_read_guard rw_lock(rwmutex); for (pdsch_t& pdsch : dl_sched.pdsch) { if (pdsch.sch.grant.rnti_type == srsran_rnti_type_c) { uint16_t rnti = pdsch.sch.grant.rnti; @@ -313,7 +335,7 @@ int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched } } } else if (pdsch.sch.grant.rnti_type == srsran_rnti_type_ra) { - sched_nr_interface::sched_rar_t& rar = dl_res.rar[rar_count++]; + sched_nr_interface::rar_t& rar = dl_res.rar[rar_count++]; // for RARs we could actually move the byte_buffer to the PHY, as there are no retx pdsch.data[0] = assemble_rar(rar.grants); } @@ -330,6 +352,8 @@ int mac_nr::get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched slot_point pusch_slot = srsran::slot_point{NUMEROLOGY_IDX, slot_cfg.idx}; ret = sched.get_ul_sched(pusch_slot, 0, ul_sched); + + srsran::rwlock_read_guard rw_lock(rwmutex); for (auto& pusch : ul_sched.pusch) { if (ue_db.contains(pusch.sch.grant.rnti)) { ue_db[pusch.sch.grant.rnti]->metrics_ul_mcs(pusch.sch.grant.tb->mcs); @@ -344,6 +368,14 @@ int mac_nr::pucch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_ph logger.error("Error handling UCI data from PUCCH reception"); return SRSRAN_ERROR; } + + // process PUCCH SNR + uint16_t rnti = pucch_info.uci_data.cfg.pucch.rnti; + srsran::rwlock_read_guard rw_lock(rwmutex); + if (ue_db.contains(rnti)) { + ue_db[rnti]->metrics_pucch_sinr(pucch_info.csi.snr_dB); + } + return SRSRAN_SUCCESS; } @@ -354,6 +386,7 @@ bool mac_nr::handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg const srsran_harq_ack_bit_t* ack_bit = &cfg_.ack.bits[i]; bool is_ok = (value.ack[i] == 1) and value.valid; sched.dl_ack_info(rnti, 0, ack_bit->pid, 0, is_ok); + srsran::rwlock_read_guard rw_lock(rwmutex); if (ue_db.contains(rnti)) { ue_db[rnti]->metrics_tx(is_ok, 0 /*TODO get size of packet from scheduler somehow*/); } @@ -363,6 +396,15 @@ bool mac_nr::handle_uci_data(const uint16_t rnti, const srsran_uci_cfg_nr_t& cfg if (value.valid and value.sr > 0) { sched.ul_sr_info(cfg_.pucch.rnti); } + + // Process CQI + { + srsran::rwlock_read_guard rw_lock(rwmutex); + if (ue_db.contains(rnti) && value.valid) { + ue_db[rnti]->metrics_dl_cqi(cfg_, value.csi->wideband_cri_ri_pmi_cqi.cqi); + } + } + return true; } @@ -370,6 +412,7 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr:: { uint16_t rnti = pusch_info.rnti; uint32_t nof_bytes = pusch_info.pdu->N_bytes; + // Handle UCI data if (not handle_uci_data(rnti, pusch_info.uci_cfg, pusch_info.pusch_data.uci)) { logger.error("Error handling UCI data from PUCCH reception"); @@ -386,7 +429,7 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr:: } auto process_pdu_task = [this, rnti](srsran::unique_byte_buffer_t& pdu) { - srsran::rwlock_read_guard lock(rwlock); + srsran::rwlock_read_guard lock(rwmutex); if (is_rnti_active_nolock(rnti)) { ue_db[rnti]->process_pdu(std::move(pdu)); } else { @@ -395,14 +438,15 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, mac_interface_phy_nr:: }; stack_task_queue.try_push(std::bind(process_pdu_task, std::move(pusch_info.pdu))); } + srsran::rwlock_read_guard rw_lock(rwmutex); if (ue_db.contains(rnti)) { ue_db[rnti]->metrics_rx(pusch_info.pusch_data.tb[0].crc, nof_bytes); - ue_db[rnti]->metrics_dl_cqi(15); // TODO extract correct CQI measurments + ue_db[rnti]->metrics_pusch_sinr(pusch_info.csi.snr_dB); } return SRSRAN_SUCCESS; } -srsran::byte_buffer_t* mac_nr::assemble_rar(srsran::const_span grants) +srsran::byte_buffer_t* mac_nr::assemble_rar(srsran::const_span grants) { srsran::mac_rar_pdu_nr rar_pdu; diff --git a/srsenb/src/stack/mac/nr/sched_nr.cc b/srsenb/src/stack/mac/nr/sched_nr.cc index 2ac84b365..765dae8f5 100644 --- a/srsenb/src/stack/mac/nr/sched_nr.cc +++ b/srsenb/src/stack/mac/nr/sched_nr.cc @@ -20,6 +20,7 @@ */ #include "srsenb/hdr/stack/mac/nr/sched_nr.h" +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" #include "srsenb/hdr/stack/mac/nr/harq_softbuffer.h" #include "srsenb/hdr/stack/mac/nr/sched_nr_cell.h" #include "srsenb/hdr/stack/mac/nr/sched_nr_worker.h" @@ -78,7 +79,7 @@ sched_nr::sched_nr() : logger(&srslog::fetch_basic_logger("MAC-NR")) {} sched_nr::~sched_nr() {} -int sched_nr::config(const sched_cfg_t& sched_cfg, srsran::const_span cell_list) +int sched_nr::config(const sched_args_t& sched_cfg, srsran::const_span cell_list) { cfg = sched_params{sched_cfg}; logger = &srslog::fetch_basic_logger(sched_cfg.logger_name); @@ -109,7 +110,14 @@ void sched_nr::ue_cfg(uint16_t rnti, const ue_cfg_t& uecfg) void sched_nr::ue_rem(uint16_t rnti) { - sched_workers->enqueue_event(rnti, [this, rnti]() { ue_db.erase(rnti); }); + sched_workers->enqueue_event(rnti, [this, rnti]() { + auto ue_it = ue_db.find(rnti); + if (ue_it == ue_db.end()) { + logger->warning("SCHED: ue_rem(rnti) called for inexistent rnti=0x%x", rnti); + return; + } + ue_db.erase(rnti); + }); } bool sched_nr::ue_exists(uint16_t rnti) @@ -132,7 +140,7 @@ void sched_nr::ue_cfg_impl(uint16_t rnti, const ue_cfg_t& uecfg) } /// Generate {pdcch_slot,cc} scheduling decision -int sched_nr::get_dl_sched(slot_point slot_dl, uint32_t cc, dl_sched_res_t& result) +int sched_nr::run_slot(slot_point slot_dl, uint32_t cc, dl_sched_res_t& result) { // Copy UL results to intermediate buffer ul_sched_t& ul_res = pending_results->add_ul_result(slot_dl, cc); @@ -148,7 +156,8 @@ int sched_nr::get_ul_sched(slot_point slot_ul, uint32_t cc, ul_sched_t& result) { if (not pending_results->has_ul_result(slot_ul, cc)) { // sched result hasn't been generated - result = {}; + result.pucch.clear(); + result.pusch.clear(); return SRSRAN_SUCCESS; } @@ -156,7 +165,12 @@ int sched_nr::get_ul_sched(slot_point slot_ul, uint32_t cc, ul_sched_t& result) return SRSRAN_SUCCESS; } -int sched_nr::dl_rach_info(uint32_t cc, const dl_sched_rar_info_t& rar_info) +void sched_nr::get_metrics(mac_metrics_t& metrics) +{ + sched_workers->get_metrics(metrics); +} + +int sched_nr::dl_rach_info(uint32_t cc, const rar_info_t& rar_info) { sched_workers->enqueue_cc_event(cc, [this, cc, rar_info]() { cells[cc]->bwps[0].ra.dl_rach_info(rar_info); }); return SRSRAN_SUCCESS; @@ -165,7 +179,16 @@ int sched_nr::dl_rach_info(uint32_t cc, const dl_sched_rar_info_t& rar_info) void sched_nr::dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb_idx, bool ack) { sched_workers->enqueue_cc_feedback(rnti, cc, [this, pid, tb_idx, ack](ue_carrier& ue_cc) { - if (ue_cc.harq_ent.dl_ack_info(pid, tb_idx, ack) != SRSRAN_SUCCESS) { + int tbs = ue_cc.harq_ent.dl_ack_info(pid, tb_idx, ack); + if (tbs >= 0) { + std::lock_guard lock(ue_cc.metrics_mutex); + if (ack) { + ue_cc.metrics.tx_brate += tbs; + } else { + ue_cc.metrics.tx_errors++; + } + ue_cc.metrics.tx_pkts++; + } else { logger->warning("SCHED: rnti=0x%x, received DL HARQ-ACK for empty pid=%d", ue_cc.rnti, pid); } }); @@ -174,7 +197,7 @@ void sched_nr::dl_ack_info(uint16_t rnti, uint32_t cc, uint32_t pid, uint32_t tb void sched_nr::ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) { sched_workers->enqueue_cc_feedback(rnti, cc, [this, pid, crc](ue_carrier& ue_cc) { - if (ue_cc.harq_ent.ul_crc_info(pid, crc) != SRSRAN_SUCCESS) { + if (ue_cc.harq_ent.ul_crc_info(pid, crc) < 0) { logger->warning("SCHED: rnti=0x%x, received CRC for empty pid=%d", ue_cc.rnti, pid); } }); @@ -182,18 +205,35 @@ void sched_nr::ul_crc_info(uint16_t rnti, uint32_t cc, uint32_t pid, bool crc) void sched_nr::ul_sr_info(uint16_t rnti) { - sched_workers->enqueue_event(rnti, [this, rnti]() { ue_db[rnti]->ul_sr_info(); }); + sched_workers->enqueue_event(rnti, [this, rnti]() { + if (ue_db.contains(rnti)) { + ue_db[rnti]->ul_sr_info(); + } else { + logger->warning("Received SR for inexistent rnti=0x%x", rnti); + } + }); } void sched_nr::ul_bsr(uint16_t rnti, uint32_t lcg_id, uint32_t bsr) { - sched_workers->enqueue_event(rnti, [this, rnti, lcg_id, bsr]() { ue_db[rnti]->ul_bsr(lcg_id, bsr); }); + sched_workers->enqueue_event(rnti, [this, rnti, lcg_id, bsr]() { + if (ue_db.contains(rnti)) { + ue_db[rnti]->ul_bsr(lcg_id, bsr); + } else { + logger->warning("Received BSR=%d for inexistent rnti=0x%x", bsr, rnti); + } + }); } void sched_nr::dl_buffer_state(uint16_t rnti, uint32_t lcid, uint32_t newtx, uint32_t retx) { - sched_workers->enqueue_event(rnti, - [this, rnti, lcid, newtx, retx]() { ue_db[rnti]->rlc_buffer_state(lcid, newtx, retx); }); + sched_workers->enqueue_event(rnti, [this, rnti, lcid, newtx, retx]() { + if (ue_db.contains(rnti)) { + ue_db[rnti]->rlc_buffer_state(lcid, newtx, retx); + } else { + logger->warning("Received DL buffer state=%d/%d for inexistent rnti=0x%x", newtx, retx, rnti); + } + }); } #define VERIFY_INPUT(cond, msg, ...) \ diff --git a/srsenb/src/stack/mac/nr/sched_nr_cell.cc b/srsenb/src/stack/mac/nr/sched_nr_cell.cc index 4bbe574de..e2870b5a1 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_cell.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_cell.cc @@ -26,7 +26,7 @@ namespace srsenb { namespace sched_nr_impl { -si_sched::si_sched(const bwp_params& bwp_cfg_) : +si_sched::si_sched(const bwp_params_t& bwp_cfg_) : bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger(bwp_cfg_.sched_cfg.logger_name)) {} @@ -86,7 +86,7 @@ void si_sched::run_slot(bwp_slot_allocator& slot_alloc) /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ra_sched::ra_sched(const bwp_params& bwp_cfg_) : +ra_sched::ra_sched(const bwp_params_t& bwp_cfg_) : bwp_cfg(&bwp_cfg_), logger(srslog::fetch_basic_logger(bwp_cfg_.sched_cfg.logger_name)) {} @@ -229,16 +229,16 @@ int ra_sched::dl_rach_info(const dl_sched_rar_info_t& rar_info) return SRSRAN_SUCCESS; } -bwp_ctxt::bwp_ctxt(const bwp_params& bwp_cfg) : +bwp_ctxt::bwp_ctxt(const bwp_params_t& bwp_cfg) : cfg(&bwp_cfg), ra(bwp_cfg), grid(bwp_cfg), data_sched(new sched_nr_time_rr()) {} ////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -serv_cell_manager::serv_cell_manager(const sched_cell_params& cell_cfg_) : - cfg(cell_cfg_), logger(srslog::fetch_basic_logger(cell_cfg_.sched_cfg.logger_name)) +serv_cell_manager::serv_cell_manager(const cell_params_t& cell_cfg_) : + cfg(cell_cfg_), logger(srslog::fetch_basic_logger(cell_cfg_.sched_args.logger_name)) { - for (uint32_t bwp_id = 0; bwp_id < cfg.cell_cfg.bwps.size(); ++bwp_id) { + for (uint32_t bwp_id = 0; bwp_id < cfg.cfg.bwps.size(); ++bwp_id) { bwps.emplace_back(cell_cfg_.bwps[bwp_id]); } diff --git a/srsenb/src/stack/mac/nr/sched_nr_cfg.cc b/srsenb/src/stack/mac/nr/sched_nr_cfg.cc index e28eb391d..848f77de1 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_cfg.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_cfg.cc @@ -29,7 +29,25 @@ extern "C" { namespace srsenb { namespace sched_nr_impl { -bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, uint32_t cc_, uint32_t bwp_id_) : +void get_dci_locs(const srsran_coreset_t& coreset, + const srsran_search_space_t& search_space, + uint16_t rnti, + bwp_cce_pos_list& cce_locs) +{ + for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { + for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { + pdcch_cce_pos_list pdcch_locs; + cce_locs[sl][agg_idx].resize(pdcch_locs.capacity()); + uint32_t n = + srsran_pdcch_nr_locations_coreset(&coreset, &search_space, rnti, agg_idx, sl, cce_locs[sl][agg_idx].data()); + cce_locs[sl][agg_idx].resize(n); + } + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bwp_params_t::bwp_params_t(const cell_cfg_t& cell, const sched_args_t& sched_cfg_, uint32_t cc_, uint32_t bwp_id_) : cell_cfg(cell), sched_cfg(sched_cfg_), cc(cc_), @@ -37,7 +55,8 @@ bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, ui cfg(cell.bwps[bwp_id_]), logger(srslog::fetch_basic_logger(sched_cfg_.logger_name)) { - srsran_assert(bwp_id != 0 or cfg.pdcch.coreset_present[0], "CORESET#0 has to be active for initial BWP"); + srsran_assert(cfg.pdcch.ra_search_space_present, "BWPs without RA search space not supported"); + const uint32_t ra_coreset_id = cfg.pdcch.ra_search_space.coreset_id; P = get_P(cfg.rb_width, cfg.pdsch.rbg_size_cfg_1); N_rbg = get_nof_rbgs(cfg.rb_width, cfg.start_rb, cfg.pdsch.rbg_size_cfg_1); @@ -52,14 +71,13 @@ bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, ui } pusch_ra_list.resize(cfg.pusch.nof_common_time_ra); - const uint32_t coreset_id = 0; srsran_sch_grant_nr_t grant; for (uint32_t m = 0; m < cfg.pusch.nof_common_time_ra; ++m) { int ret = - srsran_ra_ul_nr_time(&cfg.pusch, srsran_rnti_type_ra, srsran_search_space_type_rar, coreset_id, m, &grant); + srsran_ra_ul_nr_time(&cfg.pusch, srsran_rnti_type_ra, srsran_search_space_type_rar, ra_coreset_id, m, &grant); srsran_assert(ret == SRSRAN_SUCCESS, "Failed to obtain RA config"); pusch_ra_list[m].msg3_delay = grant.k; - ret = srsran_ra_ul_nr_time(&cfg.pusch, srsran_rnti_type_c, srsran_search_space_type_ue, coreset_id, m, &grant); + ret = srsran_ra_ul_nr_time(&cfg.pusch, srsran_rnti_type_c, srsran_search_space_type_ue, ra_coreset_id, m, &grant); pusch_ra_list[m].K = grant.k; pusch_ra_list[m].S = grant.S; pusch_ra_list[m].L = grant.L; @@ -70,7 +88,7 @@ bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, ui for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { rar_cce_list[sl][agg_idx].resize(SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR); - int n = srsran_pdcch_nr_locations_coreset(&cell_cfg.bwps[0].pdcch.coreset[0], + int n = srsran_pdcch_nr_locations_coreset(&cell_cfg.bwps[0].pdcch.coreset[ra_coreset_id], &cell_cfg.bwps[0].pdcch.ra_search_space, 0, agg_idx, @@ -82,39 +100,25 @@ bwp_params::bwp_params(const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_, ui } } -sched_cell_params::sched_cell_params(uint32_t cc_, const cell_cfg_t& cell, const sched_cfg_t& sched_cfg_) : - cc(cc_), cell_cfg(cell), sched_cfg(sched_cfg_) +cell_params_t::cell_params_t(uint32_t cc_, const cell_cfg_t& cell, const sched_args_t& sched_cfg_) : + cc(cc_), cfg(cell), sched_args(sched_cfg_) { bwps.reserve(cell.bwps.size()); - for (uint32_t i = 0; i < cell_cfg.bwps.size(); ++i) { - bwps.emplace_back(cell_cfg, sched_cfg_, cc, i); + for (uint32_t i = 0; i < cfg.bwps.size(); ++i) { + bwps.emplace_back(cfg, sched_cfg_, cc, i); } srsran_assert(not bwps.empty(), "No BWPs were configured"); } -sched_params::sched_params(const sched_cfg_t& sched_cfg_) : sched_cfg(sched_cfg_) {} - -/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - -void get_dci_locs(const srsran_coreset_t& coreset, - const srsran_search_space_t& search_space, - uint16_t rnti, - bwp_cce_pos_list& cce_locs) +sched_params::sched_params(const sched_args_t& sched_cfg_) : sched_cfg(sched_cfg_) { - for (uint32_t sl = 0; sl < SRSRAN_NOF_SF_X_FRAME; ++sl) { - for (uint32_t agg_idx = 0; agg_idx < MAX_NOF_AGGR_LEVELS; ++agg_idx) { - pdcch_cce_pos_list pdcch_locs; - cce_locs[sl][agg_idx].resize(pdcch_locs.capacity()); - uint32_t n = - srsran_pdcch_nr_locations_coreset(&coreset, &search_space, rnti, agg_idx, sl, cce_locs[sl][agg_idx].data()); - cce_locs[sl][agg_idx].resize(n); - } - } + srsran_assert(sched_cfg.fixed_dl_mcs >= 0, "Dynamic DL MCS not supported"); + srsran_assert(sched_cfg.fixed_ul_mcs >= 0, "Dynamic DL MCS not supported"); } /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -bwp_ue_cfg::bwp_ue_cfg(uint16_t rnti_, const bwp_params& bwp_cfg_, const ue_cfg_t& uecfg_) : +bwp_ue_cfg::bwp_ue_cfg(uint16_t rnti_, const bwp_params_t& bwp_cfg_, const ue_cfg_t& uecfg_) : rnti(rnti_), cfg_(&uecfg_), bwp_cfg(&bwp_cfg_) { std::fill(ss_id_to_cce_idx.begin(), ss_id_to_cce_idx.end(), SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE); diff --git a/srsenb/src/stack/mac/nr/sched_nr_grant_allocator.cc b/srsenb/src/stack/mac/nr/sched_nr_grant_allocator.cc index 1911bb330..26486625a 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_grant_allocator.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_grant_allocator.cc @@ -26,7 +26,7 @@ namespace srsenb { namespace sched_nr_impl { -bwp_slot_grid::bwp_slot_grid(const bwp_params& bwp_cfg_, uint32_t slot_idx_) : +bwp_slot_grid::bwp_slot_grid(const bwp_params_t& bwp_cfg_, uint32_t slot_idx_) : dl_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1), ul_prbs(bwp_cfg_.cfg.rb_width, bwp_cfg_.cfg.start_rb, bwp_cfg_.cfg.pdsch.rbg_size_cfg_1), slot_idx(slot_idx_), @@ -58,9 +58,10 @@ void bwp_slot_grid::reset() pucch.clear(); ssb.clear(); nzp_csi_rs.clear(); + rar.clear(); } -bwp_res_grid::bwp_res_grid(const bwp_params& bwp_cfg_) : cfg(&bwp_cfg_) +bwp_res_grid::bwp_res_grid(const bwp_params_t& bwp_cfg_) : cfg(&bwp_cfg_) { for (uint32_t sl = 0; sl < slots.capacity(); ++sl) { slots.emplace_back(*cfg, sl % static_cast(SRSRAN_NSLOTS_PER_FRAME_NR(0u))); @@ -102,9 +103,15 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t static const uint32_t msg3_nof_prbs = 3, m = 0; bwp_slot_grid& bwp_pdcch_slot = bwp_grid[pdcch_slot]; - slot_point msg3_slot = pdcch_slot + cfg.pusch_ra_list[m].msg3_delay; - bwp_slot_grid& bwp_msg3_slot = bwp_grid[msg3_slot]; - alloc_result ret = verify_pusch_space(bwp_msg3_slot, nullptr); + if (not bwp_pdcch_slot.ssb.empty()) { + // TODO: support concurrent PDSCH and SSB + logger.info("SCHED: skipping ra-rnti=0x%x RAR allocation. Cause: concurrent PDSCH and SSB not yet supported", + ra_rnti); + return alloc_result::no_sch_space; + } + slot_point msg3_slot = pdcch_slot + cfg.pusch_ra_list[m].msg3_delay; + bwp_slot_grid& bwp_msg3_slot = bwp_grid[msg3_slot]; + alloc_result ret = verify_pusch_space(bwp_msg3_slot, nullptr); if (ret != alloc_result::success) { return ret; } @@ -163,7 +170,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t const int mcs = 0, max_harq_msg3_retx = 4; slot_cfg.idx = msg3_slot.to_uint(); bwp_pdcch_slot.rar.emplace_back(); - sched_nr_interface::sched_rar_t& rar_out = bwp_pdcch_slot.rar.back(); + sched_nr_interface::rar_t& rar_out = bwp_pdcch_slot.rar.back(); for (const dl_sched_rar_info_t& grant : pending_rars) { slot_ue& ue = (*slot_ues)[grant.temp_crnti]; @@ -174,7 +181,7 @@ alloc_result bwp_slot_allocator::alloc_rar_and_msg3(uint16_t prb_interval msg3_interv{last_msg3, last_msg3 + msg3_nof_prbs}; last_msg3 += msg3_nof_prbs; ue.h_ul = ue.harq_ent->find_empty_ul_harq(); - success = ue.h_ul->new_tx(msg3_slot, msg3_slot, msg3_interv, mcs, 100, max_harq_msg3_retx); + success = ue.h_ul->new_tx(msg3_slot, msg3_slot, msg3_interv, mcs, max_harq_msg3_retx); srsran_assert(success, "Failed to allocate Msg3"); fill_dci_msg3(ue, *bwp_grid.cfg, rar_grant.msg3_dci); @@ -206,7 +213,7 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr } bwp_slot_grid& bwp_pdcch_slot = bwp_grid[ue.pdcch_slot]; bwp_slot_grid& bwp_pdsch_slot = bwp_grid[ue.pdsch_slot]; - bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot]; // UCI : UL control info + bwp_slot_grid& bwp_uci_slot = bwp_grid[ue.uci_slot]; // UCI : UL control info alloc_result result = verify_pdsch_space(bwp_pdsch_slot, bwp_pdcch_slot); if (result != alloc_result::success) { return result; @@ -225,57 +232,80 @@ alloc_result bwp_slot_allocator::alloc_pdsch(slot_ue& ue, const prb_grant& dl_gr // TODO // Find space and allocate PDCCH - const uint32_t aggr_idx = 2, ss_id = 1; - uint32_t coreset_id = ue.cfg->phy().pdcch.search_space[ss_id].coreset_id; + const uint32_t aggr_idx = 2; + // Choose the ss_id the highest number of candidates + uint32_t ss_id = 0, max_nof_candidates = 0; + for (uint32_t i = 0; i < 3; ++i) { + uint32_t nof_candidates = ue.cfg->cce_pos_list(i, pdcch_slot.slot_idx(), aggr_idx).size(); + if (nof_candidates > max_nof_candidates) { + ss_id = i; + max_nof_candidates = nof_candidates; + } + } + uint32_t coreset_id = ue.cfg->phy().pdcch.search_space[ss_id].coreset_id; if (not bwp_pdcch_slot.coresets[coreset_id]->alloc_dci(pdcch_grant_type_t::dl_data, aggr_idx, ss_id, &ue)) { // Could not find space in PDCCH return alloc_result::no_cch_space; } // Allocate HARQ + int mcs = ue.cfg->fixed_pdsch_mcs(); if (ue.h_dl->empty()) { - srsran_assert(ue.cfg->ue_cfg()->fixed_dl_mcs >= 0, "Dynamic MCS not yet supported"); - int mcs = ue.cfg->ue_cfg()->fixed_dl_mcs; - int tbs = 100; - bool ret = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, tbs, 4); + bool ret = ue.h_dl->new_tx(ue.pdsch_slot, ue.uci_slot, dl_grant, mcs, 4); srsran_assert(ret, "Failed to allocate DL HARQ"); } else { bool ret = ue.h_dl->new_retx(ue.pdsch_slot, ue.uci_slot, dl_grant); + mcs = ue.h_dl->mcs(); srsran_assert(ret, "Failed to allocate DL HARQ retx"); } // Allocation Successful - // Generate PDCCH - pdcch_dl_t& pdcch = bwp_pdcch_slot.dl_pdcchs.back(); - fill_dl_dci_ue_fields(ue, *bwp_grid.cfg, ss_id, pdcch.dci.ctx.location, pdcch.dci); - pdcch.dci.pucch_resource = 0; - pdcch.dci.dai = std::count_if(bwp_uci_slot.pending_acks.begin(), - bwp_uci_slot.pending_acks.end(), - [&ue](const harq_ack_t& p) { return p.res.rnti == ue.rnti; }); - pdcch.dci.dai %= 4; - pdcch.dci_cfg = ue.cfg->phy().get_dci_cfg(); - - // Generate PUCCH - bwp_uci_slot.pending_acks.emplace_back(); - bwp_uci_slot.pending_acks.back().phy_cfg = &ue.cfg->phy(); - srsran_assert(ue.cfg->phy().get_pdsch_ack_resource(pdcch.dci, bwp_uci_slot.pending_acks.back().res), - "Error getting ack resource"); - - // Generate PDSCH - bwp_pdsch_slot.dl_prbs |= dl_grant; - bwp_pdsch_slot.pdschs.emplace_back(); - pdsch_t& pdsch = bwp_pdsch_slot.pdschs.back(); - srsran_slot_cfg_t slot_cfg; - slot_cfg.idx = ue.pdsch_slot.to_uint(); - bool ret = ue.cfg->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); - srsran_assert(ret, "Error converting DCI to grant"); - pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); - pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); - if (ue.h_dl->nof_retx() == 0) { - ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // update HARQ with correct TBS - } else { - srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); + const static float max_R = 0.93; + while (true) { + // Generate PDCCH + pdcch_dl_t& pdcch = bwp_pdcch_slot.dl_pdcchs.back(); + fill_dl_dci_ue_fields(ue, *bwp_grid.cfg, ss_id, pdcch.dci.ctx.location, pdcch.dci); + pdcch.dci.pucch_resource = 0; + pdcch.dci.dai = std::count_if(bwp_uci_slot.pending_acks.begin(), + bwp_uci_slot.pending_acks.end(), + [&ue](const harq_ack_t& p) { return p.res.rnti == ue.rnti; }); + pdcch.dci.dai %= 4; + pdcch.dci_cfg = ue.cfg->phy().get_dci_cfg(); + + // Generate PUCCH + bwp_uci_slot.pending_acks.emplace_back(); + bwp_uci_slot.pending_acks.back().phy_cfg = &ue.cfg->phy(); + srsran_assert(ue.cfg->phy().get_pdsch_ack_resource(pdcch.dci, bwp_uci_slot.pending_acks.back().res), + "Error getting ack resource"); + + // Generate PDSCH + bwp_pdsch_slot.dl_prbs |= dl_grant; + bwp_pdsch_slot.pdschs.emplace_back(); + pdsch_t& pdsch = bwp_pdsch_slot.pdschs.back(); + srsran_slot_cfg_t slot_cfg; + slot_cfg.idx = ue.pdsch_slot.to_uint(); + bool ret = ue.cfg->phy().get_pdsch_cfg(slot_cfg, pdcch.dci, pdsch.sch); + srsran_assert(ret, "Error converting DCI to grant"); + + pdsch.sch.grant.tb[0].softbuffer.tx = ue.h_dl->get_softbuffer().get(); + pdsch.data[0] = ue.h_dl->get_tx_pdu()->get(); + if (ue.h_dl->nof_retx() == 0) { + ue.h_dl->set_tbs(pdsch.sch.grant.tb[0].tbs); // update HARQ with correct TBS + } else { + srsran_assert(pdsch.sch.grant.tb[0].tbs == (int)ue.h_dl->tbs(), "The TBS did not remain constant in retx"); + } + if (ue.h_dl->nof_retx() > 0 or bwp_pdsch_slot.pdschs.back().sch.grant.tb[0].R_prime < max_R or mcs <= 0) { + break; + } + // Decrease MCS if first tx and rate is too high + mcs--; + ue.h_dl->set_mcs(mcs); + bwp_pdsch_slot.pdschs.pop_back(); + bwp_uci_slot.pending_acks.pop_back(); + } + if (mcs == 0) { + logger.warning("Couldn't find mcs that leads to R<0.9"); } return alloc_result::success; @@ -298,18 +328,26 @@ alloc_result bwp_slot_allocator::alloc_pusch(slot_ue& ue, const prb_grant& ul_pr if (bwp_pusch_slot.ul_prbs.collides(ul_prbs)) { return alloc_result::sch_collision; } - const uint32_t aggr_idx = 2, ss_id = 1; - uint32_t coreset_id = ue.cfg->phy().pdcch.search_space[ss_id].coreset_id; + const uint32_t aggr_idx = 2; + // Choose the ss_id the highest number of candidates + uint32_t ss_id = 0, max_nof_candidates = 0; + for (uint32_t i = 0; i < 3; ++i) { + uint32_t nof_candidates = ue.cfg->cce_pos_list(i, pdcch_slot.slot_idx(), aggr_idx).size(); + if (nof_candidates > max_nof_candidates) { + ss_id = i; + max_nof_candidates = nof_candidates; + } + } + uint32_t coreset_id = ue.cfg->phy().pdcch.search_space[ss_id].coreset_id; if (not bwp_pdcch_slot.coresets[coreset_id].value().alloc_dci(pdcch_grant_type_t::ul_data, aggr_idx, ss_id, &ue)) { // Could not find space in PDCCH return alloc_result::no_cch_space; } if (ue.h_ul->empty()) { - srsran_assert(ue.cfg->ue_cfg()->fixed_ul_mcs >= 0, "Dynamic MCS not yet supported"); - int mcs = ue.cfg->ue_cfg()->fixed_ul_mcs; + int mcs = ue.cfg->fixed_pusch_mcs(); int tbs = 100; - bool success = ue.h_ul->new_tx(ue.pusch_slot, ue.pusch_slot, ul_prbs, mcs, tbs, ue.cfg->ue_cfg()->maxharq_tx); + bool success = ue.h_ul->new_tx(ue.pusch_slot, ue.pusch_slot, ul_prbs, mcs, ue.cfg->ue_cfg()->maxharq_tx); srsran_assert(success, "Failed to allocate UL HARQ"); } else { bool success = ue.h_ul->new_retx(ue.pusch_slot, ue.pusch_slot, ul_prbs); diff --git a/srsenb/src/stack/mac/nr/sched_nr_harq.cc b/srsenb/src/stack/mac/nr/sched_nr_harq.cc index f8e69d1d8..5438cec9d 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_harq.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_harq.cc @@ -34,7 +34,7 @@ int harq_proc::ack_info(uint32_t tb_idx, bool ack) if (ack) { tb[tb_idx].active = false; } - return SRSRAN_SUCCESS; + return ack ? tb[tb_idx].tbs : 0; } void harq_proc::new_slot(slot_point slot_rx) @@ -57,7 +57,6 @@ bool harq_proc::new_tx(slot_point slot_tx_, slot_point slot_ack_, const prb_grant& grant, uint32_t mcs, - uint32_t tbs, uint32_t max_retx_) { if (not empty()) { @@ -70,7 +69,7 @@ bool harq_proc::new_tx(slot_point slot_tx_, prbs_ = grant; tb[0].ndi = !tb[0].ndi; tb[0].mcs = mcs; - tb[0].tbs = tbs; + tb[0].tbs = 0; tb[0].active = true; return true; } @@ -84,6 +83,15 @@ bool harq_proc::set_tbs(uint32_t tbs) return true; } +bool harq_proc::set_mcs(uint32_t mcs) +{ + if (empty() or nof_retx() > 0) { + return false; + } + tb[0].mcs = mcs; + return true; +} + bool harq_proc::new_retx(slot_point slot_tx_, slot_point slot_ack_, const prb_grant& grant) { if (grant.is_alloc_type0() != prbs_.is_alloc_type0() or @@ -118,10 +126,9 @@ bool dl_harq_proc::new_tx(slot_point slot_tx, slot_point slot_ack, const prb_grant& grant, uint32_t mcs, - uint32_t tbs, uint32_t max_retx) { - if (harq_proc::new_tx(slot_tx, slot_ack, grant, mcs, tbs, max_retx)) { + if (harq_proc::new_tx(slot_tx, slot_ack, grant, mcs, max_retx)) { softbuffer->reset(); pdu->clear(); return true; diff --git a/srsenb/src/stack/mac/nr/sched_nr_helpers.cc b/srsenb/src/stack/mac/nr/sched_nr_helpers.cc index cf62e27cb..0b5a95e56 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_helpers.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_helpers.cc @@ -31,7 +31,7 @@ namespace sched_nr_impl { //////////////////////////////////////////////////////////////////////////////////////////////////////////////////// template -void fill_dci_common(const slot_ue& ue, const bwp_params& bwp_cfg, DciDlOrUl& dci) +void fill_dci_common(const slot_ue& ue, const bwp_params_t& bwp_cfg, DciDlOrUl& dci) { const static uint32_t rv_idx[4] = {0, 2, 3, 1}; @@ -56,7 +56,7 @@ void fill_dci_common(const slot_ue& ue, const bwp_params& bwp_cfg, DciDlOrUl& dc dci.time_domain_assigment = 0; } -bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params& bwp_cfg, srsran_dci_dl_nr_t& dci) +bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params_t& bwp_cfg, srsran_dci_dl_nr_t& dci) { dci.mcs = 5; dci.ctx.format = srsran_dci_format_nr_1_0; @@ -74,7 +74,7 @@ bool fill_dci_rar(prb_interval interv, uint16_t ra_rnti, const bwp_params& bwp_c return true; } -bool fill_dci_msg3(const slot_ue& ue, const bwp_params& bwp_cfg, srsran_dci_ul_nr_t& msg3_dci) +bool fill_dci_msg3(const slot_ue& ue, const bwp_params_t& bwp_cfg, srsran_dci_ul_nr_t& msg3_dci) { fill_dci_common(ue, bwp_cfg, msg3_dci); msg3_dci.ctx.coreset_id = ue.cfg->phy().pdcch.ra_search_space.coreset_id; @@ -91,7 +91,7 @@ bool fill_dci_msg3(const slot_ue& ue, const bwp_params& bwp_cfg, srsran_dci_ul_n } void fill_dl_dci_ue_fields(const slot_ue& ue, - const bwp_params& bwp_cfg, + const bwp_params_t& bwp_cfg, uint32_t ss_id, srsran_dci_location_t dci_pos, srsran_dci_dl_nr_t& dci) @@ -110,7 +110,7 @@ void fill_dl_dci_ue_fields(const slot_ue& ue, } void fill_ul_dci_ue_fields(const slot_ue& ue, - const bwp_params& bwp_cfg, + const bwp_params_t& bwp_cfg, uint32_t ss_id, srsran_dci_location_t dci_pos, srsran_dci_ul_nr_t& dci) @@ -123,7 +123,7 @@ void fill_ul_dci_ue_fields(const slot_ue& ue, void log_sched_slot_ues(srslog::basic_logger& logger, slot_point pdcch_slot, uint32_t cc, const slot_ue_map_t& slot_ues) { - if (not logger.info.enabled() or slot_ues.empty()) { + if (not logger.debug.enabled() or slot_ues.empty()) { return; } @@ -139,7 +139,7 @@ void log_sched_slot_ues(srslog::basic_logger& logger, slot_point pdcch_slot, uin use_comma = ", "; } - logger.info("%s]", srsran::to_c_str(fmtbuf)); + logger.debug("%s]", srsran::to_c_str(fmtbuf)); } void log_sched_bwp_result(srslog::basic_logger& logger, @@ -154,17 +154,19 @@ void log_sched_bwp_result(srslog::basic_logger& logger, if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) { const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti]; fmt::format_to(fmtbuf, - "SCHED: DL {}, cc={}, rnti=0x{:x}, pid={}, f={}, prbs={}, nrtx={}, dai={}, tbs={}, " - "pdsch_slot={}, tti_ack={}", + "SCHED: DL {}, cc={}, rnti=0x{:x}, pid={}, cs={}, f={}, prbs={}, nrtx={}, dai={}, " + "tbs={}, bs={}, pdsch_slot={}, tti_ack={}", ue.h_dl->nof_retx() == 0 ? "tx" : "retx", res_grid.cfg->cc, ue.rnti, pdcch.dci.pid, + pdcch.dci.ctx.coreset_id, srsran_dci_format_nr_string(pdcch.dci.ctx.format), ue.h_dl->prbs(), ue.h_dl->nof_retx(), pdcch.dci.dai, - ue.h_dl->tbs(), + ue.h_dl->tbs() / 8u, + ue.dl_pending_bytes, ue.pdsch_slot, ue.uci_slot); } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_ra) { @@ -173,7 +175,7 @@ void log_sched_bwp_result(srslog::basic_logger& logger, uint32_t start_idx = std::distance(prbs.begin(), std::find(prbs.begin(), prbs.end(), true)); uint32_t end_idx = std::distance(prbs.begin(), std::find(prbs.begin() + start_idx, prbs.end(), false)); fmt::format_to(fmtbuf, - "SCHED: DL RAR, cc={}, ra-rnti=0x{:x}, prbs={}, pdsch_slot={}, msg3_slot={}, nof_grants={}", + "SCHED: RAR, cc={}, ra-rnti=0x{:x}, prbs={}, pdsch_slot={}, msg3_slot={}, nof_grants={}", res_grid.cfg->cc, pdcch.dci.ctx.rnti, srsran::interval{start_idx, end_idx}, @@ -192,15 +194,16 @@ void log_sched_bwp_result(srslog::basic_logger& logger, if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_c) { const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti]; fmt::format_to(fmtbuf, - "SCHED: UL {}, cc={}, rnti=0x{:x}, pid={}, f={}, nrtx={}, pending_bytes={}, tbs={}, tti_pusch={}", + "SCHED: UL {}, cc={}, rnti=0x{:x}, pid={}, cs={}, f={}, nrtx={}, tbs={}, bs={}, tti_pusch={}", ue.h_ul->nof_retx() == 0 ? "tx" : "retx", res_grid.cfg->cc, ue.rnti, pdcch.dci.pid, + pdcch.dci.ctx.coreset_id, srsran_dci_format_nr_string(pdcch.dci.ctx.format), ue.h_ul->nof_retx(), + ue.h_ul->tbs() / 8u, ue.ul_pending_bytes, - ue.h_ul->tbs(), ue.pusch_slot); } else if (pdcch.dci.ctx.rnti_type == srsran_rnti_type_tc) { const slot_ue& ue = slot_ues[pdcch.dci.ctx.rnti]; diff --git a/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc b/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc index 5844213d2..c881f74bb 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_pdcch.cc @@ -25,11 +25,11 @@ namespace srsenb { namespace sched_nr_impl { -coreset_region::coreset_region(const bwp_params& bwp_cfg_, - uint32_t coreset_id_, - uint32_t slot_idx_, - pdcch_dl_list_t& dl_list_, - pdcch_ul_list_t& ul_list_) : +coreset_region::coreset_region(const bwp_params_t& bwp_cfg_, + uint32_t coreset_id_, + uint32_t slot_idx_, + pdcch_dl_list_t& dl_list_, + pdcch_ul_list_t& ul_list_) : coreset_cfg(&bwp_cfg_.cfg.pdcch.coreset[coreset_id_]), coreset_id(coreset_id_), slot_idx(slot_idx_), @@ -188,9 +188,9 @@ srsran::span coreset_region::get_cce_loc_table(const alloc_recor { switch (record.alloc_type) { case pdcch_grant_type_t::dl_data: - return record.ue->cfg->cce_pos_list(record.ss_id)[slot_idx][record.aggr_idx]; + return record.ue->cfg->cce_pos_list(record.ss_id, slot_idx, record.aggr_idx); case pdcch_grant_type_t::ul_data: - return record.ue->cfg->cce_pos_list(record.ss_id)[slot_idx][record.aggr_idx]; + return record.ue->cfg->cce_pos_list(record.ss_id, slot_idx, record.aggr_idx); case pdcch_grant_type_t::rar: return rar_cce_list[slot_idx][record.aggr_idx]; default: @@ -200,4 +200,4 @@ srsran::span coreset_region::get_cce_loc_table(const alloc_recor } } // namespace sched_nr_impl -} // namespace srsenb \ No newline at end of file +} // namespace srsenb diff --git a/srsenb/src/stack/mac/nr/sched_nr_signalling.cc b/srsenb/src/stack/mac/nr/sched_nr_signalling.cc index 2c45d16db..ce0e9d4f5 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_signalling.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_signalling.cc @@ -84,10 +84,10 @@ void sched_ssb_basic(const slot_point& sl_point, uint32_t ssb_periodicity, ssb_l } } -void sched_dl_signalling(const bwp_params& bwp_params, - slot_point sl_pdcch, - ssb_list& ssb_list, - nzp_csi_rs_list& nzp_csi_rs) +void sched_dl_signalling(const bwp_params_t& bwp_params, + slot_point sl_pdcch, + ssb_list& ssb_list, + nzp_csi_rs_list& nzp_csi_rs) { srsran_slot_cfg_t cfg; cfg.idx = sl_pdcch.to_uint(); diff --git a/srsenb/src/stack/mac/nr/sched_nr_ue.cc b/srsenb/src/stack/mac/nr/sched_nr_ue.cc index 19937620f..74d2d25b4 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_ue.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_ue.cc @@ -30,7 +30,7 @@ slot_ue::slot_ue(uint16_t rnti_, slot_point slot_rx_, uint32_t cc_) : rnti(rnti_ /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -ue_carrier::ue_carrier(uint16_t rnti_, const ue_cfg_t& uecfg_, const sched_cell_params& cell_params_) : +ue_carrier::ue_carrier(uint16_t rnti_, const ue_cfg_t& uecfg_, const cell_params_t& cell_params_) : rnti(rnti_), cc(cell_params_.cc), bwp_cfg(rnti_, cell_params_.bwps[0], uecfg_), @@ -38,17 +38,14 @@ ue_carrier::ue_carrier(uint16_t rnti_, const ue_cfg_t& uecfg_, const sched_cell_ harq_ent(cell_params_.nof_prb()) {} -slot_ue ue_carrier::try_reserve(slot_point pdcch_slot, - const ue_cfg_t& uecfg_, - uint32_t dl_pending_bytes, - uint32_t ul_pending_bytes) +void ue_carrier::set_cfg(const ue_cfg_t& ue_cfg) { - slot_point slot_rx = pdcch_slot - TX_ENB_DELAY; + bwp_cfg = bwp_ue_cfg(rnti, cell_params.bwps[0], ue_cfg); +} - // update CC/BWP config if there were changes - if (bwp_cfg.ue_cfg() != &uecfg_) { - bwp_cfg = bwp_ue_cfg(rnti, cell_params.bwps[0], uecfg_); - } +slot_ue ue_carrier::try_reserve(slot_point pdcch_slot, uint32_t dl_pending_bytes, uint32_t ul_pending_bytes) +{ + slot_point slot_rx = pdcch_slot - TX_ENB_DELAY; // copy cc-specific parameters and find available HARQs slot_ue sfu(rnti, slot_rx, cc); @@ -68,7 +65,7 @@ slot_ue ue_carrier::try_reserve(slot_point pdcch_slot, sfu.dl_pending_bytes = dl_pending_bytes; sfu.ul_pending_bytes = ul_pending_bytes; - const srsran_duplex_config_nr_t& tdd_cfg = cell_params.cell_cfg.duplex; + const srsran_duplex_config_nr_t& tdd_cfg = cell_params.cfg.duplex; if (srsran_duplex_nr_is_dl(&tdd_cfg, 0, sfu.pdsch_slot.slot_idx())) { // If DL enabled sfu.h_dl = harq_ent.find_pending_dl_retx(); @@ -99,8 +96,12 @@ void ue::set_cfg(const ue_cfg_t& cfg) { ue_cfg = cfg; for (auto& ue_cc_cfg : cfg.carriers) { - if (ue_cc_cfg.active and carriers[ue_cc_cfg.cc] == nullptr) { - carriers[ue_cc_cfg.cc].reset(new ue_carrier(rnti, cfg, sched_cfg.cells[ue_cc_cfg.cc])); + if (ue_cc_cfg.active) { + if (carriers[ue_cc_cfg.cc] == nullptr) { + carriers[ue_cc_cfg.cc].reset(new ue_carrier(rnti, ue_cfg, sched_cfg.cells[ue_cc_cfg.cc])); + } else { + carriers[ue_cc_cfg.cc]->set_cfg(ue_cfg); + } } } @@ -156,7 +157,7 @@ slot_ue ue::try_reserve(slot_point pdcch_slot, uint32_t cc) return slot_ue(); } - return carriers[cc]->try_reserve(pdcch_slot, cfg(), dl_pending_bytes, ul_pending_bytes); + return carriers[cc]->try_reserve(pdcch_slot, dl_pending_bytes, ul_pending_bytes); } } // namespace sched_nr_impl diff --git a/srsenb/src/stack/mac/nr/sched_nr_worker.cc b/srsenb/src/stack/mac/nr/sched_nr_worker.cc index a54acc0dc..f6147e13e 100644 --- a/srsenb/src/stack/mac/nr/sched_nr_worker.cc +++ b/srsenb/src/stack/mac/nr/sched_nr_worker.cc @@ -20,6 +20,7 @@ */ #include "srsenb/hdr/stack/mac/nr/sched_nr_worker.h" +#include "srsenb/hdr/stack/mac/common/mac_metrics.h" #include "srsenb/hdr/stack/mac/nr/sched_nr_signalling.h" #include "srsran/common/string_helpers.h" @@ -30,7 +31,7 @@ slot_cc_worker::slot_cc_worker(serv_cell_manager& cc_sched) : cell(cc_sched), cfg(cc_sched.cfg), bwp_alloc(cc_sched.bwps[0].grid), - logger(srslog::fetch_basic_logger(cc_sched.cfg.sched_cfg.logger_name)) + logger(srslog::fetch_basic_logger(cc_sched.cfg.sched_args.logger_name)) {} void slot_cc_worker::enqueue_cc_event(srsran::move_callback ev) @@ -65,7 +66,7 @@ void slot_cc_worker::run_feedback(ue_map_t& ue_db) if (ue_db.contains(f.rnti) and ue_db[f.rnti]->carriers[cfg.cc] != nullptr) { f.fdbk(*ue_db[f.rnti]->carriers[cfg.cc]); } else { - logger.warning("SCHED: feedback received for invalid rnti=0x%x, cc=%d", f.rnti, cfg.cc); + logger.info("SCHED: feedback received for rnti=0x%x, cc=%d that has been removed.", f.rnti, cfg.cc); } } tmp_feedback_to_run.clear(); @@ -124,7 +125,7 @@ void slot_cc_worker::run(slot_point pdcch_slot, ue_map_t& ue_db) void slot_cc_worker::alloc_dl_ues() { - if (not cfg.sched_cfg.pdsch_enabled) { + if (not cfg.sched_args.pdsch_enabled) { return; } cell.bwps[0].data_sched->sched_dl_users(slot_ues, bwp_alloc); @@ -132,7 +133,7 @@ void slot_cc_worker::alloc_dl_ues() void slot_cc_worker::alloc_ul_ues() { - if (not cfg.sched_cfg.pusch_enabled) { + if (not cfg.sched_args.pusch_enabled) { return; } cell.bwps[0].data_sched->sched_ul_users(slot_ues, bwp_alloc); @@ -175,6 +176,10 @@ void slot_cc_worker::postprocess_decisions() if (pusch.sch.grant.rnti == ue.rnti) { // Put UCI configuration in PUSCH config has_pusch = true; + + // If has PUSCH, no SR shall be received + uci_cfg.o_sr = 0; + if (not ue.cfg->phy().get_pusch_uci_cfg(slot_cfg, uci_cfg, pusch.sch)) { logger.error("Error setting UCI configuration in PUSCH"); continue; @@ -345,6 +350,12 @@ void sched_worker_manager::run_slot(slot_point slot_tx, uint32_t cc, dl_sched_re save_sched_result(slot_tx, cc, dl_res, ul_res); } +void sched_worker_manager::get_metrics(mac_metrics_t& metrics) +{ + std::unique_lock lock(slot_mutex); + get_metrics_nolocking(metrics); +} + bool sched_worker_manager::save_sched_result(slot_point pdcch_slot, uint32_t cc, dl_sched_res_t& dl_res, @@ -368,5 +379,19 @@ bool sched_worker_manager::save_sched_result(slot_point pdcch_slot, return true; } +void sched_worker_manager::get_metrics_nolocking(mac_metrics_t& metrics) +{ + for (mac_ue_metrics_t& ue_metric : metrics.ues) { + if (ue_db.contains(ue_metric.rnti) and ue_db[ue_metric.rnti]->carriers[0] != nullptr) { + auto& ue_cc = *ue_db[ue_metric.rnti]->carriers[0]; + std::lock_guard lock(ue_cc.metrics_mutex); + ue_metric.tx_brate = ue_cc.metrics.tx_brate; + ue_metric.tx_errors = ue_cc.metrics.tx_errors; + ue_metric.tx_pkts = ue_cc.metrics.tx_pkts; + ue_cc.metrics = {}; + } + } +} + } // namespace sched_nr_impl } // namespace srsenb diff --git a/srsenb/src/stack/mac/nr/ue_nr.cc b/srsenb/src/stack/mac/nr/ue_nr.cc index 312244765..ee48e5490 100644 --- a/srsenb/src/stack/mac/nr/ue_nr.cc +++ b/srsenb/src/stack/mac/nr/ue_nr.cc @@ -51,7 +51,10 @@ ue_nr::~ue_nr() {} void ue_nr::reset() { - ue_metrics = {}; + { + std::lock_guard lock(metrics_mutex); + ue_metrics = {}; + } nof_failures = 0; } @@ -80,7 +83,9 @@ int ue_nr::process_pdu(srsran::unique_byte_buffer_t pdu) logger.info("Rx PDU: rnti=0x%x, %s", rnti, srsran::to_c_str(str_buffer)); } - for (uint32_t i = 0; i < mac_pdu_ul.get_num_subpdus(); ++i) { + // Reverse the order in which MAC subPDUs get processed. + // First, process MAC CEs, then MAC MAC subPDUs with MAC SDUs + for (uint32_t n = mac_pdu_ul.get_num_subpdus(), i = mac_pdu_ul.get_num_subpdus() - 1; n > 0; --n, i = n - 1) { srsran::mac_sch_subpdu_nr subpdu = mac_pdu_ul.get_subpdu(i); logger.debug("Handling subPDU %d/%d: lcid=%d, sdu_len=%d", i, @@ -110,7 +115,7 @@ int ue_nr::process_pdu(srsran::unique_byte_buffer_t pdu) uint32_t buffer_size_bytes = buff_size_field_to_bytes(sbsr.buffer_size, srsran::SHORT_BSR); // Assume all LCGs are 0 if reported SBSR is 0 if (buffer_size_bytes == 0) { - for (uint32_t j = 0; j < SCHED_NR_MAX_LC_GROUP; j++) { + for (uint32_t j = 0; j <= SCHED_NR_MAX_LC_GROUP; j++) { sched->ul_bsr(rnti, j, 0); } } else { @@ -118,11 +123,12 @@ int ue_nr::process_pdu(srsran::unique_byte_buffer_t pdu) } } break; case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR: - logger.info("LONG_BSR CE not implemented."); - break; - case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_TRUNC_BSR: - logger.info("LONG_TRUNC_BSR CE not implemented."); - break; + case srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_TRUNC_BSR: { + srsran::mac_sch_subpdu_nr::lbsr_t lbsr = subpdu.get_lbsr(); + for (auto& lb : lbsr.list) { + sched->ul_bsr(rnti, lb.lcg_id, buff_size_field_to_bytes(lb.buffer_size, srsran::LONG_BSR)); + } + } break; default: if (subpdu.is_sdu()) { rrc->set_activity_user(rnti); @@ -147,29 +153,58 @@ int ue_nr::generate_pdu(srsran::byte_buffer_t* pdu, uint32_t grant_size) return SRSRAN_ERROR; } - // read RLC PDU - ue_rlc_buffer->clear(); - int lcid = 4; - int pdu_len = rlc->read_pdu(rnti, lcid, ue_rlc_buffer->msg, grant_size - 2); + bool drb_activity = false; // inform RRC about user activity if true + int lcid = 4; // only supporting single DRB right now - // Only create PDU if RLC has something to tx - if (pdu_len > 0) { - logger.debug("Adding MAC PDU for RNTI=%d", rnti); - ue_rlc_buffer->N_bytes = pdu_len; - logger.debug(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes); + int32_t remaining_len = mac_pdu_dl.get_remaing_len(); - // add to MAC PDU and pack - mac_pdu_dl.add_sdu(lcid, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes); + logger.debug("Adding MAC PDU for RNTI=%d (max %d B)", rnti, remaining_len); + while (remaining_len >= MIN_RLC_PDU_LEN) { + // clear read buffer + ue_rlc_buffer->clear(); - // Indicate DRB activity in DL to RRC - if (lcid > 3) { - rrc->set_activity_user(rnti); - logger.debug("DL activity rnti=0x%x, n_bytes=%d", rnti, ue_rlc_buffer->N_bytes); + // Determine space for RLC + remaining_len -= remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2; + + // read RLC PDU + int pdu_len = rlc->read_pdu(rnti, lcid, ue_rlc_buffer->msg, remaining_len); + + if (pdu_len > remaining_len) { + logger.error("Can't add SDU of %d B. Available space %d B", pdu_len, remaining_len); + break; + } else { + // Add SDU if RLC has something to tx + if (pdu_len > 0) { + ue_rlc_buffer->N_bytes = pdu_len; + logger.debug(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes); + + // add to MAC PDU and pack + if (mac_pdu_dl.add_sdu(lcid, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes) != SRSRAN_SUCCESS) { + logger.error("Error packing MAC PDU"); + break; + } + + // set DRB activity flag but only notify RRC once + if (lcid > 3) { + drb_activity = true; + } + } else { + break; + } + + remaining_len -= pdu_len; + logger.debug("%d B remaining PDU", remaining_len); } } mac_pdu_dl.pack(); + if (drb_activity) { + // Indicate DRB activity in DL to RRC + rrc->set_activity_user(rnti); + logger.debug("DL activity rnti=0x%x", rnti); + } + if (logger.info.enabled()) { fmt::memory_buffer str_buffer; mac_pdu_dl.to_string(str_buffer); @@ -194,17 +229,30 @@ void ue_nr::metrics_read(mac_ue_metrics_t* metrics_) auto it = std::find(cc_list.begin(), cc_list.end(), 0); ue_metrics.cc_idx = std::distance(cc_list.begin(), it); - *metrics_ = ue_metrics; - phr_counter = 0; - dl_cqi_counter = 0; - ue_metrics = {}; + *metrics_ = ue_metrics; + phr_counter = 0; + dl_cqi_valid_counter = 0; + pucch_sinr_counter = 0; + pusch_sinr_counter = 0; + ue_metrics = {}; } -void ue_nr::metrics_dl_cqi(uint32_t dl_cqi) +void ue_nr::metrics_dl_cqi(const srsran_uci_cfg_nr_t& cfg_, uint32_t dl_cqi) { std::lock_guard lock(metrics_mutex); - ue_metrics.dl_cqi = SRSRAN_VEC_CMA((float)dl_cqi, ue_metrics.dl_cqi, dl_cqi_counter); - dl_cqi_counter++; + + // Process CQI + for (uint32_t i = 0; i < cfg_.nof_csi; i++) { + // Skip if invalid or not supported CSI report + if (cfg_.csi[i].cfg.quantity != SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI or + cfg_.csi[i].cfg.freq_cfg != SRSRAN_CSI_REPORT_FREQ_WIDEBAND) { + continue; + } + + // Add statistics + ue_metrics.dl_cqi = SRSRAN_VEC_SAFE_CMA(dl_cqi, ue_metrics.dl_cqi, dl_cqi_valid_counter); + dl_cqi_valid_counter++; + } } void ue_nr::metrics_rx(bool crc, uint32_t tbs) @@ -231,12 +279,14 @@ void ue_nr::metrics_tx(bool crc, uint32_t tbs) void ue_nr::metrics_dl_mcs(uint32_t mcs) { + std::lock_guard lock(metrics_mutex); ue_metrics.dl_mcs = SRSRAN_VEC_CMA((float)mcs, ue_metrics.dl_mcs, ue_metrics.dl_mcs_samples); ue_metrics.dl_mcs_samples++; } void ue_nr::metrics_ul_mcs(uint32_t mcs) { + std::lock_guard lock(metrics_mutex); ue_metrics.ul_mcs = SRSRAN_VEC_CMA((float)mcs, ue_metrics.ul_mcs, ue_metrics.ul_mcs_samples); ue_metrics.ul_mcs_samples++; } @@ -247,6 +297,26 @@ void ue_nr::metrics_cnt() ue_metrics.nof_tti++; } +void ue_nr::metrics_pucch_sinr(float sinr) +{ + std::lock_guard lock(metrics_mutex); + // discard nan or inf values for average SINR + if (!std::isinf(sinr) && !std::isnan(sinr)) { + ue_metrics.pucch_sinr = SRSRAN_VEC_SAFE_CMA((float)sinr, ue_metrics.pucch_sinr, pucch_sinr_counter); + pucch_sinr_counter++; + } +} + +void ue_nr::metrics_pusch_sinr(float sinr) +{ + std::lock_guard lock(metrics_mutex); + // discard nan or inf values for average SINR + if (!std::isinf(sinr) && !std::isnan(sinr)) { + ue_metrics.pusch_sinr = SRSRAN_VEC_SAFE_CMA((float)sinr, ue_metrics.pusch_sinr, pusch_sinr_counter); + pusch_sinr_counter++; + } +} + /** Converts the buffer size field of a BSR (5 or 8-bit Buffer Size field) into Bytes * @param buff_size_field The buffer size field contained in the MAC PDU * @param format The BSR format that determines the buffer size field length diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc index a59dd224a..c0f005615 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_harq.cc @@ -333,7 +333,7 @@ void ul_harq_proc::reset_pending_data() uint32_t ul_harq_proc::get_pending_data() const { - return (uint32_t)pending_data; + return is_empty() ? 0 : (uint32_t)pending_data; } /******************** @@ -360,6 +360,9 @@ void harq_entity::reset() for (auto& h : ul_harqs) { for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) { h.reset(tb); + // The reset_pending_data() is called after reset(), when generating PHICH. However, in the case of full HARQ + // reset (e.g. during handover) no PHICH is going to be generated. + h.reset_pending_data(); } } } diff --git a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc index 40cb799c1..6f843426c 100644 --- a/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc +++ b/srsenb/src/stack/mac/sched_ue_ctrl/sched_ue_cell.cc @@ -107,6 +107,7 @@ void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) if (ue_cc_idx != prev_ue_cc_idx) { clear_feedback(); harq_ent.reset(); + logger.info("SCHED: Resetting rnti=0x%x, cc=%d HARQs and feedback state", rnti, cell_cfg->enb_cc_idx); } // Update carrier state @@ -120,8 +121,12 @@ void sched_ue_cell::set_ue_cfg(const sched_interface::ue_cfg_t& ue_cfg_) case cc_st::active: if (ue_cc_idx < 0 or not ue_cfg->supported_cc_list[ue_cc_idx].active) { cc_state_ = cc_st::deactivating; - logger.info( - "SCHED: Deactivating SCell, rnti=0x%x, cc=%d, SCellIndex=%d...", rnti, cell_cfg->enb_cc_idx, ue_cc_idx); + if (ue_cc_idx > 0) { + logger.info( + "SCHED: Deactivating SCell, rnti=0x%x, cc=%d, SCellIndex=%d...", rnti, cell_cfg->enb_cc_idx, ue_cc_idx); + } else { + logger.info("SCHED: Deactivating previous PCell, rnti=0x%x, cc=%d...", rnti, cell_cfg->enb_cc_idx); + } } break; case cc_st::deactivating: diff --git a/srsenb/src/stack/mac/ue.cc b/srsenb/src/stack/mac/ue.cc index a731bec30..d9fd41082 100644 --- a/srsenb/src/stack/mac/ue.cc +++ b/srsenb/src/stack/mac/ue.cc @@ -117,7 +117,7 @@ uint8_t* cc_used_buffers_map::request_pdu(tti_point tti, uint32_t len) void cc_used_buffers_map::clear_old_pdus(tti_point current_tti) { std::unique_lock lock(mutex); - static const uint32_t old_tti_threshold = SRSRAN_FDD_NOF_HARQ + 4; + static const uint32_t old_tti_threshold = SRSRAN_FDD_NOF_HARQ + 4; tti_point max_tti{current_tti - old_tti_threshold}; for (auto& pdu_pair : pdu_map) { @@ -214,7 +214,10 @@ ue::~ue() {} void ue::reset() { - ue_metrics = {}; + { + std::lock_guard lock(metrics_mutex); + ue_metrics = {}; + } nof_failures = 0; for (auto& cc : cc_buffers) { diff --git a/srsenb/src/stack/ngap/ngap.cc b/srsenb/src/stack/ngap/ngap.cc index 86f8b92d1..b2ce1dae5 100644 --- a/srsenb/src/stack/ngap/ngap.cc +++ b/srsenb/src/stack/ngap/ngap.cc @@ -121,7 +121,7 @@ ngap::ngap(srsran::task_sched_handle task_sched_, ngap::~ngap() {} -int ngap::init(const ngap_args_t& args_, rrc_interface_ngap_nr* rrc_, gtpu_interface_rrc * gtpu_) +int ngap::init(const ngap_args_t& args_, rrc_interface_ngap_nr* rrc_, gtpu_interface_rrc* gtpu_) { rrc = rrc_; args = args_; @@ -546,14 +546,21 @@ bool ngap::handle_initial_ctxt_setup_request(const asn1::ngap_nr::init_context_s bool ngap::handle_ue_ctxt_release_cmd(const asn1::ngap_nr::ue_context_release_cmd_s& msg) { - // TODO: UE Context Release Command contains a list of ue_ngap_ids + const asn1::ngap_nr::ue_ngap_id_pair_s& ue_ngap_id_pair = msg.protocol_ies.ue_ngap_ids.value.ue_ngap_id_pair(); + + ue* u = handle_ngapmsg_ue_id(ue_ngap_id_pair.ran_ue_ngap_id, ue_ngap_id_pair.amf_ue_ngap_id); + if (u == nullptr) { + logger.warning("Can not find UE"); + return false; + } + + u->handle_ue_ctxt_release_cmd(msg); return true; } bool ngap::handle_ue_pdu_session_res_setup_request(const asn1::ngap_nr::pdu_session_res_setup_request_s& msg) { - ue* u = handle_ngapmsg_ue_id(msg.protocol_ies.ran_ue_ngap_id.value.value, msg.protocol_ies.amf_ue_ngap_id.value.value); if (u == nullptr) { diff --git a/srsenb/src/stack/ngap/ngap_ue.cc b/srsenb/src/stack/ngap/ngap_ue.cc index a4e53bf18..5e6664b99 100644 --- a/srsenb/src/stack/ngap/ngap_ue.cc +++ b/srsenb/src/stack/ngap/ngap_ue.cc @@ -38,9 +38,9 @@ ngap::ue::ue(ngap* ngap_ptr_, srslog::basic_logger& logger_) : logger(logger_), ngap_ptr(ngap_ptr_), - bearer_manager(rrc_ptr_, gtpu_ptr_, logger_), + bearer_manager(gtpu_ptr_, logger_), initial_context_setup_proc(this, rrc_ptr_, &ctxt, logger_), - ue_context_release_proc(this, rrc_ptr_, &ctxt, logger_), + ue_context_release_proc(this, rrc_ptr_, &ctxt, &bearer_manager, logger_), ue_pdu_session_res_setup_proc(this, rrc_ptr_, &ctxt, &bearer_manager, logger_) { ctxt.ran_ue_ngap_id = ngap_ptr->next_gnb_ue_ngap_id++; @@ -232,6 +232,30 @@ bool ngap::ue::send_pdu_session_resource_setup_response(uint16_t return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "PDUSessionResourceSetupResponse"); } +bool ngap::ue::send_ue_ctxt_release_complete() +{ + if (not ngap_ptr->amf_connected) { + logger.warning("AMF not connected"); + return false; + } + + ngap_pdu_c tx_pdu; + tx_pdu.set_successful_outcome().load_info_obj(ASN1_NGAP_NR_ID_UE_CONTEXT_RELEASE); + ue_context_release_complete_s& container = tx_pdu.successful_outcome().value.ue_context_release_complete(); + + // userLocationInformationNR + container.protocol_ies.user_location_info.value.set_user_location_info_nr(); + container.protocol_ies.user_location_info.value.user_location_info_nr().nr_cgi.nrcell_id = ngap_ptr->nr_cgi.nrcell_id; + container.protocol_ies.user_location_info.value.user_location_info_nr().nr_cgi.plmn_id = ngap_ptr->nr_cgi.plmn_id; + container.protocol_ies.user_location_info.value.user_location_info_nr().tai.plmn_id = ngap_ptr->tai.plmn_id; + container.protocol_ies.user_location_info.value.user_location_info_nr().tai.tac = ngap_ptr->tai.tac; + + container.protocol_ies.ran_ue_ngap_id.value = ctxt.ran_ue_ngap_id; + container.protocol_ies.amf_ue_ngap_id.value = ctxt.amf_ue_ngap_id.value(); + + return ngap_ptr->sctp_send_ngap_pdu(tx_pdu, ctxt.rnti, "UEContextReleaseComplete"); +} + /******************************************************************************* /* NGAP message handler ********************************************************************************/ diff --git a/srsenb/src/stack/ngap/ngap_ue_bearer_manager.cc b/srsenb/src/stack/ngap/ngap_ue_bearer_manager.cc index 366f11130..6c9a58731 100644 --- a/srsenb/src/stack/ngap/ngap_ue_bearer_manager.cc +++ b/srsenb/src/stack/ngap/ngap_ue_bearer_manager.cc @@ -22,10 +22,8 @@ #include "srsenb/hdr/stack/ngap/ngap_ue_bearer_manager.h" namespace srsenb { -ngap_ue_bearer_manager::ngap_ue_bearer_manager(rrc_interface_ngap_nr* rrc_, - gtpu_interface_rrc* gtpu_, - srslog::basic_logger& logger_) : - gtpu(gtpu_), rrc(rrc_), logger(logger_) +ngap_ue_bearer_manager::ngap_ue_bearer_manager(gtpu_interface_rrc* gtpu_, srslog::basic_logger& logger_) : + gtpu(gtpu_), logger(logger_) {} ngap_ue_bearer_manager::~ngap_ue_bearer_manager(){}; @@ -39,9 +37,6 @@ int ngap_ue_bearer_manager::add_pdu_session(uint16_t uint32_t& teid_in, asn1::ngap_nr::cause_c& cause) { - // RRC call for QoS parameter and lcid <-> ID mapping - lcid = rrc->allocate_lcid(rnti); - // Only add session if gtpu was successful pdu_session_t::gtpu_tunnel tunnel; @@ -52,7 +47,7 @@ int ngap_ue_bearer_manager::add_pdu_session(uint16_t } // TODO: remove lcid and just use pdu_session_id and rnti as id for GTP tunnel - int rtn = add_gtpu_bearer(rnti, lcid, pdu_session_id, teid_out, addr_out, tunnel); + int rtn = add_gtpu_bearer(rnti, pdu_session_id, teid_out, addr_out, tunnel); if (rtn != SRSRAN_SUCCESS) { logger.error("Adding PDU Session ID=%d to GTPU", pdu_session_id); return SRSRAN_ERROR; @@ -70,8 +65,16 @@ int ngap_ue_bearer_manager::add_pdu_session(uint16_t return SRSRAN_SUCCESS; } +int ngap_ue_bearer_manager::reset_pdu_sessions(uint16_t rnti) +{ + for (auto iter = pdu_session_list.begin(); iter != pdu_session_list.end(); iter++) { + auto pdu_session_id = iter->first; + rem_gtpu_bearer(pdu_session_id, rnti); + } + return true; +} + int ngap_ue_bearer_manager::add_gtpu_bearer(uint16_t rnti, - uint32_t lcid, uint32_t pdu_session_id, uint32_t teid_out, asn1::bounded_bitstring<1, 160, true, true> address_out, @@ -80,7 +83,8 @@ int ngap_ue_bearer_manager::add_gtpu_bearer(uint16_t { // Initialize ERAB tunnel in GTPU right-away. DRBs are only created during RRC setup/reconf uint32_t addr_in; - srsran::expected rtn = gtpu->add_bearer(rnti, lcid, address_out.to_number(), teid_out, addr_in, props); + srsran::expected rtn = + gtpu->add_bearer(rnti, pdu_session_id, address_out.to_number(), teid_out, addr_in, props); if (rtn.is_error()) { logger.error("Failed adding pdu_session_id=%d to GTPU", pdu_session_id); return SRSRAN_ERROR; @@ -94,10 +98,9 @@ int ngap_ue_bearer_manager::add_gtpu_bearer(uint16_t tunnel.address_in.from_number(addr_in); tunnel.teid_in = rtn.value(); - logger.info("Added GTPU tunnel rnti 0x%04x, lcid %d, pdu_session_id=%d, teid_out %d, teid_in %d, address out 0x%x, " + logger.info("Added GTPU tunnel rnti 0x%04x, pdu_session_id=%d, teid_out %d, teid_in %d, address out 0x%x, " "address in 0x%x", rnti, - lcid, pdu_session_id, tunnel.teid_out, tunnel.teid_in, diff --git a/srsenb/src/stack/ngap/ngap_ue_proc.cc b/srsenb/src/stack/ngap/ngap_ue_proc.cc index 3efe8b118..ea61a2920 100644 --- a/srsenb/src/stack/ngap/ngap_ue_proc.cc +++ b/srsenb/src/stack/ngap/ngap_ue_proc.cc @@ -75,19 +75,23 @@ proc_outcome_t ngap_ue_initial_context_setup_proc::step() ngap_ue_ue_context_release_proc::ngap_ue_ue_context_release_proc(ngap_interface_ngap_proc* parent_, rrc_interface_ngap_nr* rrc_, ngap_ue_ctxt_t* ue_ctxt_, + ngap_ue_bearer_manager* bearer_manager_, srslog::basic_logger& logger_) : logger(logger_) { - parent = parent_; - rrc = rrc_; - ue_ctxt = ue_ctxt_; + parent = parent_; + rrc = rrc_; + ue_ctxt = ue_ctxt_; + bearer_manager = bearer_manager_; }; proc_outcome_t ngap_ue_ue_context_release_proc::init(const asn1::ngap_nr::ue_context_release_cmd_s& msg) { - // ue_ngap_ids_c ue_ngap_ids = msg.protocol_ies.ue_ngap_ids.value; - // cause_c cause = msg.protocol_ies.cause.value; logger.info("Started %s", name()); + // TODO: How to approach erasing users ? + bearer_manager->reset_pdu_sessions(ue_ctxt->rnti); + rrc->release_bearers(ue_ctxt->rnti); + parent->send_ue_ctxt_release_complete(); return proc_outcome_t::success; } diff --git a/srsenb/src/stack/rrc/mac_controller.cc b/srsenb/src/stack/rrc/mac_controller.cc index 2c7e5e97f..05890b35d 100644 --- a/srsenb/src/stack/rrc/mac_controller.cc +++ b/srsenb/src/stack/rrc/mac_controller.cc @@ -291,7 +291,7 @@ void mac_controller::handle_intraenb_ho_cmd(const asn1::rrc::rrc_conn_recfg_r8_i set_drb_activation(false); // Stop any SRB UL (including SRs) - for (uint32_t i = srb_to_lcid(lte_srb::srb1); i <= srb_to_lcid(lte_srb::srb2); ++i) { + for (uint32_t i = srb_to_lcid(lte_srb::srb0); i <= srb_to_lcid(lte_srb::srb2); ++i) { current_sched_ue_cfg.ue_bearers[i].direction = mac_lc_ch_cfg_t::DL; } diff --git a/srsenb/src/stack/rrc/nr/cell_asn1_config.cc b/srsenb/src/stack/rrc/nr/cell_asn1_config.cc index 328897c5f..43243aa3f 100644 --- a/srsenb/src/stack/rrc/nr/cell_asn1_config.cc +++ b/srsenb/src/stack/rrc/nr/cell_asn1_config.cc @@ -23,11 +23,62 @@ using namespace asn1::rrc_nr; +#define HANDLE_ERROR(ret) \ + if ((ret) != SRSRAN_SUCCESS) { \ + return SRSRAN_ERROR; \ + } + namespace srsenb { -int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +srslog::basic_logger& get_logger(const rrc_nr_cfg_t& cfg) +{ + static srslog::basic_logger& log_obj = srslog::fetch_basic_logger(cfg.log_name); + return log_obj; +} + +/// Fill list of CSI-ReportConfig with gNB config +int fill_csi_report_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +{ + csi_meas_cfg.csi_report_cfg_to_add_mod_list_present = true; + csi_meas_cfg.csi_report_cfg_to_add_mod_list.resize(1); + + auto& csi_report = csi_meas_cfg.csi_report_cfg_to_add_mod_list[0]; + csi_report.report_cfg_id = 0; + csi_report.res_for_ch_meas = 0; + csi_report.csi_im_res_for_interference_present = true; + csi_report.csi_im_res_for_interference = 1; + csi_report.report_cfg_type.set_periodic(); + csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80(); + csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1); + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0; + csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = 1; // was 17 in orig PCAP + csi_report.report_quant.set_cri_ri_pmi_cqi(); + // Report freq config (optional) + csi_report.report_freq_cfg_present = true; + csi_report.report_freq_cfg.cqi_format_ind_present = true; + csi_report.report_freq_cfg.cqi_format_ind = + asn1::rrc_nr::csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi; + csi_report.time_restrict_for_ch_meass = asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured; + csi_report.time_restrict_for_interference_meass = + asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured; + csi_report.group_based_beam_report.set_disabled(); + // Skip CQI table (optional) + csi_report.cqi_table_present = true; + csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2; + csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; + + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 5; + } else { + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 7; + } + + return SRSRAN_SUCCESS; +} + +/// Fill lists of NZP-CSI-RS-Resource and NZP-CSI-RS-ResourceSet with gNB config +void fill_nzp_csi_rs_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) { - // Fill NZP-CSI Resources csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list_present = true; if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.resize(5); @@ -52,81 +103,32 @@ int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas nzp_csi_res[0].qcl_info_periodic_csi_rs_present = true; nzp_csi_res[0].qcl_info_periodic_csi_rs = 0; // item 1 + nzp_csi_res[1] = nzp_csi_res[0]; nzp_csi_res[1].nzp_csi_rs_res_id = 1; nzp_csi_res[1].res_map.freq_domain_alloc.set_row1(); nzp_csi_res[1].res_map.freq_domain_alloc.row1().from_number(0b0001); - nzp_csi_res[1].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; nzp_csi_res[1].res_map.first_ofdm_symbol_in_time_domain = 4; - nzp_csi_res[1].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; nzp_csi_res[1].res_map.density.set_three(); - nzp_csi_res[1].res_map.freq_band.start_rb = 0; - nzp_csi_res[1].res_map.freq_band.nrof_rbs = 52; - nzp_csi_res[1].pwr_ctrl_offset = 0; - // Skip pwr_ctrl_offset_ss_present - nzp_csi_res[1].scrambling_id = cfg.cell_list[0].phy_cell.cell_id; - nzp_csi_res[1].periodicity_and_offset_present = true; nzp_csi_res[1].periodicity_and_offset.set_slots40(); nzp_csi_res[1].periodicity_and_offset.slots40() = 11; - // optional - nzp_csi_res[1].qcl_info_periodic_csi_rs_present = true; - nzp_csi_res[1].qcl_info_periodic_csi_rs = 0; // item 2 - nzp_csi_res[2].nzp_csi_rs_res_id = 2; - nzp_csi_res[2].res_map.freq_domain_alloc.set_row1(); - nzp_csi_res[2].res_map.freq_domain_alloc.row1().from_number(0b0001); - nzp_csi_res[2].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res[2] = nzp_csi_res[1]; + nzp_csi_res[2].nzp_csi_rs_res_id = 2; nzp_csi_res[2].res_map.first_ofdm_symbol_in_time_domain = 8; - nzp_csi_res[2].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; - nzp_csi_res[2].res_map.density.set_three(); - nzp_csi_res[2].res_map.freq_band.start_rb = 0; - nzp_csi_res[2].res_map.freq_band.nrof_rbs = 52; - nzp_csi_res[2].pwr_ctrl_offset = 0; - // Skip pwr_ctrl_offset_ss_present - nzp_csi_res[2].scrambling_id = cfg.cell_list[0].phy_cell.cell_id; - nzp_csi_res[2].periodicity_and_offset_present = true; nzp_csi_res[2].periodicity_and_offset.set_slots40(); nzp_csi_res[2].periodicity_and_offset.slots40() = 11; - // optional - nzp_csi_res[2].qcl_info_periodic_csi_rs_present = true; - nzp_csi_res[2].qcl_info_periodic_csi_rs = 0; // item 3 - nzp_csi_res[3].nzp_csi_rs_res_id = 3; - nzp_csi_res[3].res_map.freq_domain_alloc.set_row1(); - nzp_csi_res[3].res_map.freq_domain_alloc.row1().from_number(0b0001); - nzp_csi_res[3].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res[3] = nzp_csi_res[1]; + nzp_csi_res[3].nzp_csi_rs_res_id = 3; nzp_csi_res[3].res_map.first_ofdm_symbol_in_time_domain = 4; - nzp_csi_res[3].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; - nzp_csi_res[3].res_map.density.set_three(); - nzp_csi_res[3].res_map.freq_band.start_rb = 0; - nzp_csi_res[3].res_map.freq_band.nrof_rbs = 52; - nzp_csi_res[3].pwr_ctrl_offset = 0; - // Skip pwr_ctrl_offset_ss_present - nzp_csi_res[3].scrambling_id = cfg.cell_list[0].phy_cell.cell_id; - nzp_csi_res[3].periodicity_and_offset_present = true; nzp_csi_res[3].periodicity_and_offset.set_slots40(); nzp_csi_res[3].periodicity_and_offset.slots40() = 12; - // optional - nzp_csi_res[3].qcl_info_periodic_csi_rs_present = true; - nzp_csi_res[3].qcl_info_periodic_csi_rs = 0; // item 4 - nzp_csi_res[4].nzp_csi_rs_res_id = 4; - nzp_csi_res[4].res_map.freq_domain_alloc.set_row1(); - nzp_csi_res[4].res_map.freq_domain_alloc.row1().from_number(0b0001); - nzp_csi_res[4].res_map.nrof_ports = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; + nzp_csi_res[4] = nzp_csi_res[1]; + nzp_csi_res[4].nzp_csi_rs_res_id = 4; nzp_csi_res[4].res_map.first_ofdm_symbol_in_time_domain = 8; - nzp_csi_res[4].res_map.cdm_type = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; - nzp_csi_res[4].res_map.density.set_three(); - nzp_csi_res[4].res_map.freq_band.start_rb = 0; - nzp_csi_res[4].res_map.freq_band.nrof_rbs = 52; - nzp_csi_res[4].pwr_ctrl_offset = 0; - // Skip pwr_ctrl_offset_ss_present - nzp_csi_res[4].scrambling_id = cfg.cell_list[0].phy_cell.cell_id; - nzp_csi_res[4].periodicity_and_offset_present = true; nzp_csi_res[4].periodicity_and_offset.set_slots40(); nzp_csi_res[4].periodicity_and_offset.slots40() = 12; - // optional - nzp_csi_res[4].qcl_info_periodic_csi_rs_present = true; - nzp_csi_res[4].qcl_info_periodic_csi_rs = 0; } else { csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list.resize(1); auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list; @@ -164,7 +166,7 @@ int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas nzp_csi_res_set[1].nzp_csi_rs_res[1] = 2; nzp_csi_res_set[1].nzp_csi_rs_res[2] = 3; nzp_csi_res_set[1].nzp_csi_rs_res[3] = 4; - // Skip TRS info + // // Skip TRS info } else { csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list.resize(1); auto& nzp_csi_res_set = csi_meas_cfg.nzp_csi_rs_res_set_to_add_mod_list; @@ -173,13 +175,236 @@ int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas nzp_csi_res_set[0].nzp_csi_rs_res[0] = 0; // Skip TRS info } +} + +/// Fill csi-ResoureConfigToAddModList +void fill_csi_resource_cfg_to_add(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +{ + if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + csi_meas_cfg.csi_res_cfg_to_add_mod_list_present = true; + csi_meas_cfg.csi_res_cfg_to_add_mod_list.resize(3); + + csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].csi_res_cfg_id = 0; + auto& nzp = csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].csi_rs_res_set_list.set_nzp_csi_rs_ssb(); + nzp.nzp_csi_rs_res_set_list_present = true; + nzp.nzp_csi_rs_res_set_list.push_back(0); + csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].bwp_id = 0; + csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].res_type.value = csi_res_cfg_s::res_type_opts::periodic; + + csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_res_cfg_id = 1; + auto& imres = csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_rs_res_set_list.set_csi_im_res_set_list(); + imres.push_back(0); + csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].bwp_id = 0; + csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].res_type.value = csi_res_cfg_s::res_type_opts::periodic; + + csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].csi_res_cfg_id = 2; + auto& nzp2 = csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].csi_rs_res_set_list.set_nzp_csi_rs_ssb(); + nzp2.nzp_csi_rs_res_set_list_present = true; + nzp2.nzp_csi_rs_res_set_list.push_back(1); + csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].bwp_id = 0; + csi_meas_cfg.csi_res_cfg_to_add_mod_list[2].res_type.value = csi_res_cfg_s::res_type_opts::periodic; + } +} + +/// Fill CSI-MeasConfig with gNB config +int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg) +{ + // // Fill CSI Report + // if (fill_csi_report_from_enb_cfg(cfg, csi_meas_cfg) != SRSRAN_SUCCESS) { + // get_logger(cfg).error("Failed to configure eNB CSI Report"); + // return SRSRAN_ERROR; + // } + + // Fill CSI resource config + fill_csi_resource_cfg_to_add(cfg, csi_meas_cfg); + + // Fill NZP-CSI Resources + fill_nzp_csi_rs_from_enb_cfg(cfg, csi_meas_cfg); + + // CSI IM config + // TODO: add csi im config + + // CSI resource config + // TODO: add csi resource config + return SRSRAN_SUCCESS; } +void fill_init_dl_bwp(const rrc_nr_cfg_t& cfg, bwp_dl_ded_s& bwp) +{ + bwp.pdcch_cfg_present = true; + auto& pdcch_ded_setup = bwp.pdcch_cfg.set_setup(); + pdcch_ded_setup.ctrl_res_set_to_add_mod_list_present = true; + pdcch_ded_setup.ctrl_res_set_to_add_mod_list.resize(1); + pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].ctrl_res_set_id = 2; + pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].freq_domain_res.from_number( + 0b111111110000000000000000000000000000000000000); + pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].dur = 1; + pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].cce_reg_map_type.set_non_interleaved(); + pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].precoder_granularity = + asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; + + // search spaces + pdcch_ded_setup.search_spaces_to_add_mod_list_present = true; + pdcch_ded_setup.search_spaces_to_add_mod_list.resize(1); + pdcch_ded_setup.search_spaces_to_add_mod_list[0].search_space_id = 2; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].ctrl_res_set_id_present = true; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].ctrl_res_set_id = 2; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].monitoring_slot_periodicity_and_offset_present = true; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].monitoring_slot_periodicity_and_offset.set_sl1(); + pdcch_ded_setup.search_spaces_to_add_mod_list[0].monitoring_symbols_within_slot_present = true; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].monitoring_symbols_within_slot.from_number(0b10000000000000); + pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates_present = true; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level1 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level1_opts::n0; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level2 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level2_opts::n2; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level4 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level4_opts::n2; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level8 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level16 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].search_space_type_present = true; + pdcch_ded_setup.search_spaces_to_add_mod_list[0].search_space_type.set_ue_specific(); + pdcch_ded_setup.search_spaces_to_add_mod_list[0].search_space_type.ue_specific().dci_formats = asn1::rrc_nr:: + search_space_s::search_space_type_c_::ue_specific_s_::dci_formats_opts::formats0_minus0_and_minus1_minus0; +} + +/// Fill ServingCellConfig with gNB config int fill_serv_cell_from_enb_cfg(const rrc_nr_cfg_t& cfg, serving_cell_cfg_s& serv_cell) { serv_cell.csi_meas_cfg_present = true; - return fill_csi_meas_from_enb_cfg(cfg, serv_cell.csi_meas_cfg.set_setup()); + HANDLE_ERROR(fill_csi_meas_from_enb_cfg(cfg, serv_cell.csi_meas_cfg.set_setup())); + + serv_cell.init_dl_bwp_present = true; + fill_init_dl_bwp(cfg, serv_cell.init_dl_bwp); + + // TODO: remaining fields + + return SRSRAN_SUCCESS; +} + +void fill_dl_cfg_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, dl_cfg_common_s& dl_cfg_common) +{ + dl_cfg_common.init_dl_bwp_present = true; + dl_cfg_common.init_dl_bwp.pdcch_cfg_common_present = true; + + auto& pdcch_cfg_setup = dl_cfg_common.init_dl_bwp.pdcch_cfg_common.set_setup(); + pdcch_cfg_setup.common_ctrl_res_set_present = true; + pdcch_cfg_setup.common_ctrl_res_set.ctrl_res_set_id = 1; + pdcch_cfg_setup.common_ctrl_res_set.freq_domain_res.from_number(0b111111110000000000000000000000000000000000000); + pdcch_cfg_setup.common_ctrl_res_set.dur = 1; + pdcch_cfg_setup.common_ctrl_res_set.cce_reg_map_type.set_non_interleaved(); + pdcch_cfg_setup.common_ctrl_res_set.precoder_granularity = + asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; + + // common search space list + pdcch_cfg_setup.common_search_space_list_present = true; + pdcch_cfg_setup.common_search_space_list.resize(1); + pdcch_cfg_setup.common_search_space_list[0].search_space_id = 1; + pdcch_cfg_setup.common_search_space_list[0].ctrl_res_set_id_present = true; + pdcch_cfg_setup.common_search_space_list[0].ctrl_res_set_id = 1; + pdcch_cfg_setup.common_search_space_list[0].search_space_type_present = true; + pdcch_cfg_setup.common_search_space_list[0].search_space_type.set_common(); + pdcch_cfg_setup.common_search_space_list[0].search_space_type.common().dci_format0_minus0_and_format1_minus0_present = + true; + pdcch_cfg_setup.common_search_space_list[0].nrof_candidates_present = true; + pdcch_cfg_setup.common_search_space_list[0].nrof_candidates.aggregation_level1 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level1_opts::n1; + pdcch_cfg_setup.common_search_space_list[0].nrof_candidates.aggregation_level2 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level2_opts::n1; + pdcch_cfg_setup.common_search_space_list[0].nrof_candidates.aggregation_level4 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1; + pdcch_cfg_setup.common_search_space_list[0].nrof_candidates.aggregation_level8 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; + pdcch_cfg_setup.common_search_space_list[0].nrof_candidates.aggregation_level16 = + asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; + pdcch_cfg_setup.common_search_space_list[0].monitoring_slot_periodicity_and_offset_present = true; + pdcch_cfg_setup.common_search_space_list[0].monitoring_slot_periodicity_and_offset.set_sl1(); + pdcch_cfg_setup.common_search_space_list[0].monitoring_symbols_within_slot_present = true; + pdcch_cfg_setup.common_search_space_list[0].monitoring_symbols_within_slot.from_number(0b10000000000000); + pdcch_cfg_setup.ra_search_space_present = true; + pdcch_cfg_setup.ra_search_space = 1; + + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + pdcch_cfg_setup.ext = false; + } +} + +/// Fill ServingCellConfigCommon with gNB config +int fill_serv_cell_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, serving_cell_cfg_common_s& serv_common) +{ + auto& cell_cfg = cfg.cell_list.at(cc); + + serv_common.ss_pbch_block_pwr = 20; + serv_common.n_timing_advance_offset_present = true; + serv_common.n_timing_advance_offset = asn1::rrc_nr::serving_cell_cfg_common_s::n_timing_advance_offset_opts::n0; + serv_common.n_timing_advance_offset_present = true; + serv_common.dmrs_type_a_position = asn1::rrc_nr::serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2; + + serv_common.pci_present = true; + serv_common.pci = cell_cfg.phy_cell.carrier.pci; + + serv_common.ssb_periodicity_serving_cell_present = true; + if (not asn1::number_to_enum(serv_common.ssb_periodicity_serving_cell, cell_cfg.ssb_cfg.periodicity_ms)) { + get_logger(cfg).error("Config Error: Invalid SSB periodicity = %d\n", cell_cfg.ssb_cfg.periodicity_ms); + return SRSRAN_ERROR; + } + + // Fill SSB config + serv_common.ssb_positions_in_burst_present = true; + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + serv_common.ssb_positions_in_burst.set_short_bitmap().from_number(0b1000); + } else { + serv_common.ssb_positions_in_burst.set_medium_bitmap().from_number(0b10000000); + } + + // Set SSB SCS + serv_common.ssb_subcarrier_spacing_present = true; + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { + serv_common.ssb_subcarrier_spacing = subcarrier_spacing_opts::khz15; + } else { + serv_common.ssb_subcarrier_spacing = subcarrier_spacing_opts::khz30; + } + + if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + // TDD UL-DL config + serv_common.tdd_ul_dl_cfg_common_present = true; + auto& tdd_config = serv_common.tdd_ul_dl_cfg_common; + tdd_config.ref_subcarrier_spacing = subcarrier_spacing_e::khz15; + tdd_config.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10; + tdd_config.pattern1.nrof_dl_slots = 6; + tdd_config.pattern1.nrof_dl_symbols = 0; + tdd_config.pattern1.nrof_ul_slots = 4; + tdd_config.pattern1.nrof_ul_symbols = 0; + } + + serv_common.dl_cfg_common_present = true; + fill_dl_cfg_common_from_enb_cfg(cfg, cc, serv_common.dl_cfg_common); + + return SRSRAN_SUCCESS; +} + +/// Fill reconfigurationWithSync with gNB config +int fill_recfg_with_sync_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, recfg_with_sync_s& sync) +{ + sync.sp_cell_cfg_common_present = true; + HANDLE_ERROR(fill_serv_cell_common_from_enb_cfg(cfg, cc, sync.sp_cell_cfg_common)); + + return SRSRAN_SUCCESS; +} + +/// Fill spCellConfig with gNB config +int fill_sp_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, sp_cell_cfg_s& sp_cell) +{ + sp_cell.recfg_with_sync_present = true; + HANDLE_ERROR(fill_recfg_with_sync_from_enb_cfg(cfg, cc, sp_cell.recfg_with_sync)); + + sp_cell.sp_cell_cfg_ded_present = true; + HANDLE_ERROR(fill_serv_cell_from_enb_cfg(cfg, sp_cell.sp_cell_cfg_ded)); + + return SRSRAN_SUCCESS; } } // namespace srsenb diff --git a/srsenb/src/stack/rrc/rrc.cc b/srsenb/src/stack/rrc/rrc.cc index 527dddc9f..f50a0884e 100644 --- a/srsenb/src/stack/rrc/rrc.cc +++ b/srsenb/src/stack/rrc/rrc.cc @@ -88,6 +88,14 @@ int32_t rrc::init(const rrc_cfg_t& cfg_, // Loads the PRACH root sequence cfg.sibs[1].sib2().rr_cfg_common.prach_cfg.root_seq_idx = cfg.cell_list[0].root_seq_idx; + if (cfg.num_nr_cells > 0) { + cfg.sibs[1].sib2().ext = true; + cfg.sibs[1].sib2().plmn_info_list_r15.set_present(); + cfg.sibs[1].sib2().plmn_info_list_r15.get()->resize(1); + auto& plmn = cfg.sibs[1].sib2().plmn_info_list_r15.get()->back(); + plmn.upper_layer_ind_r15_present = true; + } + if (generate_sibs() != SRSRAN_SUCCESS) { logger.error("Couldn't generate SIBs."); return false; @@ -570,23 +578,50 @@ void rrc::set_erab_status(uint16_t rnti, const asn1::s1ap::bearers_subject_to_st void rrc::sgnb_addition_ack(uint16_t eutra_rnti, sgnb_addition_ack_params_t params) { - logger.info("Received SgNB addition acknowledgement for rnti=%d", eutra_rnti); - users.at(eutra_rnti)->endc_handler->trigger(ue::rrc_endc::sgnb_add_req_ack_ev{params}); + logger.info("Received SgNB addition acknowledgement for rnti=0x%x", eutra_rnti); + auto ue_it = users.find(eutra_rnti); + if (ue_it == users.end()) { + logger.warning("rnti=0x%x does not exist", eutra_rnti); + return; + } + ue_it->second->endc_handler->trigger(ue::rrc_endc::sgnb_add_req_ack_ev{params}); // trigger RRC Reconfiguration to send NR config to UE - users.at(eutra_rnti)->send_connection_reconf(); + ue_it->second->send_connection_reconf(); } void rrc::sgnb_addition_reject(uint16_t eutra_rnti) { logger.error("Received SgNB addition reject for rnti=%d", eutra_rnti); - users.at(eutra_rnti)->endc_handler->trigger(ue::rrc_endc::sgnb_add_req_reject_ev{}); + auto ue_it = users.find(eutra_rnti); + if (ue_it == users.end()) { + logger.warning("rnti=0x%x does not exist", eutra_rnti); + return; + } + ue_it->second->endc_handler->trigger(ue::rrc_endc::sgnb_add_req_reject_ev{}); } void rrc::sgnb_addition_complete(uint16_t eutra_rnti, uint16_t nr_rnti) { logger.info("User rnti=0x%x successfully enabled EN-DC", eutra_rnti); - users.at(eutra_rnti)->endc_handler->trigger(ue::rrc_endc::sgnb_add_complete_ev{nr_rnti}); + auto ue_it = users.find(eutra_rnti); + if (ue_it == users.end()) { + logger.warning("rnti=0x%x does not exist", eutra_rnti); + return; + } + ue_it->second->endc_handler->trigger(ue::rrc_endc::sgnb_add_complete_ev{nr_rnti}); +} + +void rrc::sgnb_release_ack(uint16_t eutra_rnti) +{ + auto ue_it = users.find(eutra_rnti); + if (ue_it != users.end()) { + logger.info("Received SgNB release acknowledgement for rnti=0x%x", eutra_rnti); + ue_it->second->endc_handler->trigger(ue::rrc_endc::sgnb_rel_req_ack_ev{}); + } else { + // The EUTRA does not need to wait for Release Ack in case it wants to destroy the EUTRA UE + logger.info("Received SgNB release acknowledgement for already released rnti=0x%x", eutra_rnti); + } } /******************************************************************************* diff --git a/srsenb/src/stack/rrc/rrc_endc.cc b/srsenb/src/stack/rrc/rrc_endc.cc index 9d9eef55f..fbeebc60e 100644 --- a/srsenb/src/stack/rrc/rrc_endc.cc +++ b/srsenb/src/stack/rrc/rrc_endc.cc @@ -51,10 +51,15 @@ rrc::ue::rrc_endc::rrc_endc(rrc::ue* outer_ue, const rrc_endc_cfg_t& endc_cfg_) } } +rrc::ue::rrc_endc::~rrc_endc() +{ + start_sgnb_release(); +} + //! Method to add NR fields to a RRC Connection Reconfiguration Message bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn_recfg) { - if (not endc_supported) { + if (not is_endc_supported()) { // skipping ENDC-related field return false; } @@ -66,11 +71,14 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn meas_cfg.meas_obj_to_add_mod_list_present = true; + // store id of nr meas object to remove it in second reconfiguration message + nr_meas_obj_id = meas_cfg.meas_obj_to_add_mod_list.size() + 1; + meas_obj_to_add_mod_s meas_obj = {}; - meas_obj.meas_obj_id = meas_cfg.meas_obj_to_add_mod_list.size() + 1; + meas_obj.meas_obj_id = nr_meas_obj_id; meas_obj.meas_obj.set_meas_obj_nr_r15(); auto& meas_obj_nr = meas_obj.meas_obj.meas_obj_nr_r15(); - meas_obj_nr.carrier_freq_r15 = endc_cfg.nr_dl_arfcn; + meas_obj_nr.carrier_freq_r15 = endc_cfg.abs_frequency_ssb; meas_obj_nr.rs_cfg_ssb_r15.meas_timing_cfg_r15.periodicity_and_offset_r15 = endc_cfg.ssb_period_offset; meas_obj_nr.rs_cfg_ssb_r15.meas_timing_cfg_r15.ssb_dur_r15 = endc_cfg.ssb_duration; meas_obj_nr.rs_cfg_ssb_r15.subcarrier_spacing_ssb_r15 = endc_cfg.ssb_ssc; @@ -81,9 +89,12 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn // report config meas_cfg.report_cfg_to_add_mod_list_present = true; - report_cfg_to_add_mod_s report_cfg = {}; - report_cfg.report_cfg_id = meas_cfg.report_cfg_to_add_mod_list.size() + 1; + // store id of nr report config to remove it in second reconfiguration message + nr_report_cfg_id = meas_cfg.report_cfg_to_add_mod_list.size() + 1; + + report_cfg_to_add_mod_s report_cfg = {}; + report_cfg.report_cfg_id = nr_report_cfg_id; report_cfg.report_cfg.set_report_cfg_inter_rat(); report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.set_event(); report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().event_id.set_event_b1_nr_r15(); @@ -100,7 +111,7 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().hysteresis = 0; report_cfg.report_cfg.report_cfg_inter_rat().trigger_type.event().time_to_trigger = time_to_trigger_opts::ms100; - report_cfg.report_cfg.report_cfg_inter_rat().max_report_cells = 1; + report_cfg.report_cfg.report_cfg_inter_rat().max_report_cells = 8; report_cfg.report_cfg.report_cfg_inter_rat().report_interv = report_interv_opts::ms120; report_cfg.report_cfg.report_cfg_inter_rat().report_amount = report_cfg_inter_rat_s::report_amount_opts::r1; report_cfg.report_cfg.report_cfg_inter_rat().report_quant_cell_nr_r15.set_present(true); @@ -112,10 +123,14 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn // measIdToAddModList meas_cfg.meas_id_to_add_mod_list_present = true; - meas_id_to_add_mod_s meas_id = {}; - meas_id.meas_id = meas_cfg.meas_id_to_add_mod_list.size() + 1; - meas_id.meas_obj_id = meas_obj.meas_obj_id; - meas_id.report_cfg_id = report_cfg.report_cfg_id; + + // store id of nr meas to remove it in second reconfiguration message + nr_meas_id = meas_cfg.meas_id_to_add_mod_list.size() + 1; + + meas_id_to_add_mod_s meas_id = {}; + meas_id.meas_id = nr_meas_id; + meas_id.meas_obj_id = meas_obj.meas_obj_id; + meas_id.report_cfg_id = report_cfg.report_cfg_id; meas_cfg.meas_id_to_add_mod_list.push_back(meas_id); // quantityConfig @@ -129,15 +144,58 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn meas_quant[0].meas_quant_cell_nr_r15.filt_coeff_rsrp_r15 = filt_coef_opts::fc3; // measGapConfig - meas_cfg.meas_gap_cfg_present = true; + meas_cfg.meas_gap_cfg_present = false; // No LTE measGaps allowed while in NSA mode meas_cfg.meas_gap_cfg.set_setup(); meas_cfg.meas_gap_cfg.setup().gap_offset.set_gp0() = 16; } else if (is_in_state()) { + // Deactivate measurement reports, as we do not support measurements after NR activation + conn_recfg->meas_cfg_present = true; + meas_cfg_s& meas_cfg = conn_recfg->meas_cfg; + // Remove meas config + meas_cfg.meas_obj_to_rem_list_present = true; + meas_cfg.meas_obj_to_rem_list.resize(1); + meas_cfg.meas_obj_to_rem_list[0] = nr_meas_obj_id; + // remove report config + meas_cfg.report_cfg_to_rem_list_present = true; + meas_cfg.report_cfg_to_rem_list.resize(1); + meas_cfg.report_cfg_to_rem_list[0] = nr_report_cfg_id; + // remove meas id + meas_cfg.meas_id_to_rem_list_present = true; + meas_cfg.meas_id_to_rem_list.resize(1); + meas_cfg.meas_id_to_rem_list[0] = nr_meas_id; + // FIXME: use bearer manager to remove EUTRA DRB conn_recfg->rr_cfg_ded.drb_to_release_list_present = true; conn_recfg->rr_cfg_ded.drb_to_release_list.resize(1); conn_recfg->rr_cfg_ded.drb_to_release_list[0] = 1; + // don't send EUTRA dedicated config again + conn_recfg->rr_cfg_ded.phys_cfg_ded_present = false; + + // set MAC main config dedicated + conn_recfg->rr_cfg_ded.mac_main_cfg_present = true; + conn_recfg->rr_cfg_ded.mac_main_cfg.set_explicit_value(); + + auto& mac_main_cfg = conn_recfg->rr_cfg_ded.mac_main_cfg.explicit_value(); + + mac_main_cfg.time_align_timer_ded = time_align_timer_opts::infinity; + mac_main_cfg.phr_cfg_present = true; + mac_main_cfg.phr_cfg.set_setup(); + mac_main_cfg.phr_cfg.setup().dl_pathloss_change = + asn1::rrc::mac_main_cfg_s::phr_cfg_c_::setup_s_::dl_pathloss_change_opts::db3; + mac_main_cfg.phr_cfg.setup().periodic_phr_timer = + asn1::rrc::mac_main_cfg_s::phr_cfg_c_::setup_s_::periodic_phr_timer_opts::sf500; + mac_main_cfg.phr_cfg.setup().prohibit_phr_timer = + asn1::rrc::mac_main_cfg_s::phr_cfg_c_::setup_s_::prohibit_phr_timer_opts::sf200; + + // Disable DC-PHR reporting + mac_main_cfg.ext = false; + mac_main_cfg.mac_main_cfg_v1020.set_present(); + mac_main_cfg.dual_connect_phr.set_present(); + mac_main_cfg.dual_connect_phr.get()->set_setup(); + mac_main_cfg.dual_connect_phr.get()->setup().phr_mode_other_cg_r12 = + asn1::rrc::mac_main_cfg_s::dual_connect_phr_c_::setup_s_::phr_mode_other_cg_r12_opts::real; + // only add reconfigure EN-DC extension/release 15.10 field if ENDC activation is active conn_recfg->non_crit_ext_present = true; conn_recfg->non_crit_ext.non_crit_ext_present = true; @@ -151,7 +209,7 @@ bool rrc::ue::rrc_endc::fill_conn_recfg(asn1::rrc::rrc_conn_recfg_r8_ies_s* conn .non_crit_ext_present = true; rrc_conn_recfg_v1510_ies_s& reconf_v1510 = conn_recfg->non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext .non_crit_ext.non_crit_ext.non_crit_ext.non_crit_ext; - reconf_v1510.nr_cfg_r15_present = true; + reconf_v1510.nr_cfg_r15_present = true; reconf_v1510.nr_cfg_r15.set_setup(); reconf_v1510.nr_cfg_r15.setup().endc_release_and_add_r15 = false; @@ -179,6 +237,7 @@ void rrc::ue::rrc_endc::handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_ // skip any further checks if eNB runs without NR cells if (rrc_enb->cfg.num_nr_cells == 0) { Debug("Skipping UE capabilities. No NR cell configured."); + trigger(disable_endc_ev{}); return; } @@ -208,7 +267,7 @@ void rrc::ue::rrc_endc::handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_ if (ue_cap_v1510.irat_params_nr_r15_present) { if (ue_cap_v1510.irat_params_nr_r15.en_dc_r15_present) { logger.info("Enabling ENDC support for rnti=%d", rrc_ue->rnti); - endc_supported = true; + return; } } } @@ -224,13 +283,15 @@ void rrc::ue::rrc_endc::handle_eutra_capabilities(const asn1::rrc::ue_eutra_cap_ } } } + + trigger(disable_endc_ev{}); } //! Method called whenever the eNB receives a MeasReport from the UE void rrc::ue::rrc_endc::handle_ue_meas_report(const meas_report_s& msg) { // Ignore event if ENDC isn't supported - if (not endc_supported) { + if (not is_endc_supported()) { return; } @@ -260,13 +321,19 @@ void rrc::ue::rrc_endc::start_sgnb_addition() rrc_nr_interface_rrc::sgnb_addition_req_params_t params = {}; params.eps_bearer_id = rrc_enb->bearer_manager.get_lcid_bearer(rrc_ue->rnti, drb_to_lcid((lte_drb)eutra_drb_id)).eps_bearer_id; - logger.info("Triggering SgNB addition"); + logger.info("Triggering SgNB addition for E-UTRA rnti=0x%x", rrc_ue->rnti); rrc_enb->rrc_nr->sgnb_addition_request(rrc_ue->rnti, params); sgnb_add_req_sent_ev sgnb_add_req{}; trigger(sgnb_add_req); } +void rrc::ue::rrc_endc::start_sgnb_release() +{ + sgnb_rel_req_ev sgnb_rel_req{nr_rnti}; + trigger(sgnb_rel_req); +} + rrc::ue::rrc_endc::prepare_recfg_st::prepare_recfg_st(rrc_endc* parent_) : logger(parent_->logger) {} void rrc::ue::rrc_endc::prepare_recfg_st::enter(rrc_endc* f, const sgnb_add_req_ack_ev& ev) @@ -289,20 +356,44 @@ void rrc::ue::rrc_endc::handle_sgnb_add_req_ack(wait_sgnb_add_req_resp_st& s, co // TODO: path update procedure with GTPU modify bearer request (for mode 3A and 3X) - // delete EPS bearer mapping over EUTRA PDCP - rrc_enb->bearer_manager.remove_eps_bearer(rrc_ue->rnti, ev.params.eps_bearer_id); - // re-register EPS bearer over NR PDCP rrc_enb->bearer_manager.add_eps_bearer( ev.params.nr_rnti, ev.params.eps_bearer_id, srsran::srsran_rat_t::nr, lcid_drb_nr); // change GTPU tunnel RNTI to match NR RNTI rrc_enb->gtpu->mod_bearer_rnti(rrc_ue->rnti, ev.params.nr_rnti); + + // store RNTI for later + nr_rnti = ev.params.nr_rnti; +} + +void rrc::ue::rrc_endc::handle_sgnb_rel_req(const sgnb_rel_req_ev& ev) +{ + logger.info("Triggering SgNB release for E-UTRA rnti=0x%x", rrc_ue->rnti); + rrc_enb->bearer_manager.rem_user(nr_rnti); + rrc_enb->rrc_nr->sgnb_release_request(nr_rnti); } bool rrc::ue::rrc_endc::is_endc_supported() { - return endc_supported; + return not is_in_state(); +} + +void rrc::ue::rrc_endc::handle_rrc_reest(endc_activated_st& s, const rrc_reest_rx_ev& ev) +{ + // Transition GTPU tunnel rnti back from NR RNTI to LTE RNTI, given that the reconfiguration failed + rrc_enb->gtpu->mod_bearer_rnti(nr_rnti, rrc_ue->rnti); +} + +void rrc::ue::rrc_endc::handle_endc_disabled(const disable_endc_ev& ev) +{ + logger.info("Disabling NR EN-DC support for rnti=0x%x", nr_rnti); +} + +bool rrc::ue::rrc_endc::requires_rel_req(const sgnb_rel_req_ev& ev) +{ + return not is_in_state() and not is_in_state() and + not is_in_state(); } } // namespace srsenb diff --git a/srsenb/src/stack/rrc/rrc_mobility.cc b/srsenb/src/stack/rrc/rrc_mobility.cc index db293f141..650bf8e35 100644 --- a/srsenb/src/stack/rrc/rrc_mobility.cc +++ b/srsenb/src/stack/rrc/rrc_mobility.cc @@ -1151,6 +1151,13 @@ void rrc::ue::rrc_mobility::handle_crnti_ce(intraenb_ho_st& s, const user_crnti_ s.last_temp_crnti = ev.temp_crnti; if (is_first_crnti_ce) { + // Stop all running RLF timers + // Note: The RLF timer can be triggered during Handover because the UE did not RLC-ACK the Handover Command + // Once the Handover is complete, to avoid releasing the UE, the RLF timer should stop. + rrc_ue->rlc_rlf_timer.stop(); + rrc_ue->phy_dl_rlf_timer.stop(); + rrc_ue->phy_ul_rlf_timer.stop(); + // Need to reset SNs of bearers. rrc_enb->rlc->reestablish(rrc_ue->rnti); rrc_enb->pdcp->reestablish(rrc_ue->rnti); diff --git a/srsenb/src/stack/rrc/rrc_nr.cc b/srsenb/src/stack/rrc/rrc_nr.cc index f57f26636..466440ce2 100644 --- a/srsenb/src/stack/rrc/rrc_nr.cc +++ b/srsenb/src/stack/rrc/rrc_nr.cc @@ -64,6 +64,19 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_, return SRSRAN_ERROR; } + // Fill base ASN1 cell config. + int ret = fill_sp_cell_cfg_from_enb_cfg(cfg, UE_PSCELL_CC_IDX, base_sp_cell_cfg); + srsran_assert(ret == SRSRAN_SUCCESS, "Failed to configure cell"); + + // Fill rrc_nr_cfg with UE-specific search spaces and coresets + bool ret2 = srsran::fill_phy_pdcch_cfg_common( + base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(), + &cfg.cell_list[0].phy_cell.pdcch); + srsran_assert(ret2, "Invalid NR cell configuration."); + ret2 = srsran::fill_phy_pdcch_cfg(base_sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(), + &cfg.cell_list[0].phy_cell.pdcch); + srsran_assert(ret2, "Invalid NR cell configuration."); + config_phy(); // if PHY is not yet initialized, config will be stored and applied on initialization config_mac(); @@ -154,10 +167,10 @@ rrc_nr_cfg_t rrc_nr::update_default_cfg(const rrc_nr_cfg_t& current) } // This function is called from PRACH worker (can wait) -int rrc_nr::add_user(uint16_t rnti) +int rrc_nr::add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg) { if (users.count(rnti) == 0) { - users.insert(std::make_pair(rnti, std::unique_ptr(new ue(this, rnti)))); + users.insert(std::make_pair(rnti, std::unique_ptr(new ue(this, rnti, uecfg)))); rlc->add_user(rnti); pdcp->add_user(rnti); logger.info("Added new user rnti=0x%x", rnti); @@ -191,13 +204,14 @@ void rrc_nr::rem_user(uint16_t rnti) int rrc_nr::update_user(uint16_t new_rnti, uint16_t old_rnti) { if (new_rnti == old_rnti) { - logger.error("rnti=0x%x received MAC CRNTI CE with same rnti", new_rnti); + logger.warning("rnti=0x%x received MAC CRNTI CE with same rnti", new_rnti); return SRSRAN_ERROR; } // Remove new_rnti auto new_ue_it = users.find(new_rnti); if (new_ue_it != users.end()) { + new_ue_it->second->deactivate_bearers(); task_sched.defer_task([this, new_rnti]() { rem_user(new_rnti); }); } @@ -252,11 +266,16 @@ void rrc_nr::config_mac() std::vector sched_cells_cfg = {srsenb::get_default_cells_cfg(1)}; sched_nr_interface::cell_cfg_t& cell = sched_cells_cfg[0]; - asn1::rrc_nr::serving_cell_cfg_s serv_cell; - int ret = fill_serv_cell_from_enb_cfg(cfg, serv_cell); - srsran_assert(ret == SRSRAN_SUCCESS, "Failed to configure cell"); - bool ret2 = srsran::make_pdsch_cfg_from_serv_cell(serv_cell, &cell.bwps[0].pdsch); - srsran_assert(ret2, "Failed to configure cell"); + // Derive cell config from rrc_nr_cfg_t + cell.bwps[0].pdcch = cfg.cell_list[0].phy_cell.pdcch; + // Derive cell config from ASN1 + bool ret2 = srsran::make_pdsch_cfg_from_serv_cell(base_sp_cell_cfg.sp_cell_cfg_ded, &cell.bwps[0].pdsch); + srsran_assert(ret2, "Invalid NR cell configuration."); + ret2 = srsran::make_phy_ssb_cfg( + cfg.cell_list[0].phy_cell.carrier, base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common, &cell.ssb); + srsran_assert(ret2, "Invalid NR cell configuration."); + ret2 = srsran::make_duplex_cfg_from_serv_cell(base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common, &cell.duplex); + srsran_assert(ret2, "Invalid NR cell configuration."); // FIXME: entire SI configuration, etc needs to be ported to NR sched_interface::cell_cfg_t cell_cfg; @@ -459,6 +478,11 @@ int rrc_nr::establish_rrc_bearer(uint16_t rnti, uint16_t pdu_session_id, srsran: return SRSRAN_SUCCESS; } +int rrc_nr::release_bearers(uint16_t rnti) +{ + return SRSRAN_SUCCESS; +} + int rrc_nr::allocate_lcid(uint16_t rnti) { return SRSRAN_SUCCESS; @@ -470,41 +494,58 @@ void rrc_nr::write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) {} Interface for EUTRA RRC *******************************************************************************/ -int rrc_nr::sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) +void rrc_nr::sgnb_addition_request(uint16_t eutra_rnti, const sgnb_addition_req_params_t& params) { - task_sched.defer_task([this, eutra_rnti, params]() { - // try to allocate new user - uint16_t nr_rnti = mac->reserve_rnti(0); - if (nr_rnti == SRSRAN_INVALID_RNTI) { - logger.error("Failed to allocate RNTI at MAC"); - rrc_eutra->sgnb_addition_reject(eutra_rnti); - return; - } - - if (add_user(nr_rnti) != SRSRAN_SUCCESS) { - logger.error("Failed to allocate RNTI at RRC"); - rrc_eutra->sgnb_addition_reject(eutra_rnti); - return; - } + // try to allocate new user + sched_nr_ue_cfg_t uecfg{}; + uecfg.carriers.resize(1); + uecfg.carriers[0].active = true; + uecfg.carriers[0].cc = 0; + uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH; + srsran::phy_cfg_nr_default_t::reference_cfg_t ref_args{}; + ref_args.duplex = cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_TDD + ? srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_TDD_CUSTOM_6_4 + : srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD; + uecfg.phy_cfg = srsran::phy_cfg_nr_default_t{ref_args}; + uecfg.phy_cfg.csi = {}; // disable CSI until RA is complete + + uint16_t nr_rnti = mac->reserve_rnti(0, uecfg); + if (nr_rnti == SRSRAN_INVALID_RNTI) { + logger.error("Failed to allocate RNTI at MAC"); + rrc_eutra->sgnb_addition_reject(eutra_rnti); + return; + } - // new RNTI is now registered at MAC and RRC - auto user_it = users.find(nr_rnti); - if (user_it == users.end()) { - logger.warning("Unrecognised rnti: 0x%x", nr_rnti); - return; - } - user_it->second->handle_sgnb_addition_request(eutra_rnti, params); - }); + if (add_user(nr_rnti, uecfg) != SRSRAN_SUCCESS) { + logger.error("Failed to allocate RNTI at RRC"); + rrc_eutra->sgnb_addition_reject(eutra_rnti); + return; + } - // return straight away - return SRSRAN_SUCCESS; + // new RNTI is now registered at MAC and RRC + auto user_it = users.find(nr_rnti); + if (user_it == users.end()) { + logger.warning("Unrecognised rnti: 0x%x", nr_rnti); + return; + } + user_it->second->handle_sgnb_addition_request(eutra_rnti, params); } -int rrc_nr::sgnb_reconfiguration_complete(uint16_t eutra_rnti, asn1::dyn_octstring reconfig_response) +void rrc_nr::sgnb_reconfiguration_complete(uint16_t eutra_rnti, const asn1::dyn_octstring& reconfig_response) { // user has completeted the reconfiguration and has acked on 4G side, wait until RA on NR logger.info("Received Reconfiguration complete for RNTI=0x%x", eutra_rnti); - return SRSRAN_SUCCESS; +} + +void rrc_nr::sgnb_release_request(uint16_t nr_rnti) +{ + // remove user + auto it = users.find(nr_rnti); + uint16_t eutra_rnti = it != users.end() ? it->second->get_eutra_rnti() : SRSRAN_INVALID_RNTI; + rem_user(nr_rnti); + if (eutra_rnti != SRSRAN_INVALID_RNTI) { + rrc_eutra->sgnb_release_ack(eutra_rnti); + } } /******************************************************************************* @@ -513,7 +554,12 @@ int rrc_nr::sgnb_reconfiguration_complete(uint16_t eutra_rnti, asn1::dyn_octstri Every function in UE class is called from a mutex environment thus does not need extra protection. *******************************************************************************/ -rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_) : parent(parent_), rnti(rnti_), uecfg(srsenb::get_default_ue_cfg(1)) {} +rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_, const sched_nr_ue_cfg_t& uecfg_) : + parent(parent_), rnti(rnti_), uecfg(uecfg_) +{ + // Derive UE cfg from rrc_cfg_nr_t + uecfg.phy_cfg.pdcch = parent->cfg.cell_list[0].phy_cell.pdcch; +} void rrc_nr::ue::send_connection_setup() { @@ -562,31 +608,31 @@ int rrc_nr::ue::pack_secondary_cell_group_rlc_cfg(asn1::rrc_nr::cell_group_cfg_s // RLC for DRB1 (with fixed LCID) cell_group_cfg_pack.rlc_bearer_to_add_mod_list_present = true; cell_group_cfg_pack.rlc_bearer_to_add_mod_list.resize(1); - auto& rlc = cell_group_cfg_pack.rlc_bearer_to_add_mod_list[0]; - rlc.lc_ch_id = drb1_lcid; - rlc.served_radio_bearer_present = true; - rlc.served_radio_bearer.set_drb_id(); - rlc.served_radio_bearer.drb_id() = 1; - rlc.rlc_cfg_present = true; - rlc.rlc_cfg.set_um_bi_dir(); - rlc.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present = true; - rlc.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len = sn_field_len_um_opts::size12; - rlc.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present = true; - rlc.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len = sn_field_len_um_opts::size12; - rlc.rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_opts::ms50; + auto& rlc_bearer = cell_group_cfg_pack.rlc_bearer_to_add_mod_list[0]; + rlc_bearer.lc_ch_id = drb1_lcid; + rlc_bearer.served_radio_bearer_present = true; + rlc_bearer.served_radio_bearer.set_drb_id(); + rlc_bearer.served_radio_bearer.drb_id() = 1; + rlc_bearer.rlc_cfg_present = true; + rlc_bearer.rlc_cfg.set_um_bi_dir(); + rlc_bearer.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present = true; + rlc_bearer.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len = sn_field_len_um_opts::size12; + rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present = true; + rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len = sn_field_len_um_opts::size12; + rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_opts::ms50; // MAC logical channel config - rlc.mac_lc_ch_cfg_present = true; - rlc.mac_lc_ch_cfg.ul_specific_params_present = true; - rlc.mac_lc_ch_cfg.ul_specific_params.prio = 11; - rlc.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = + rlc_bearer.mac_lc_ch_cfg_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.prio = 11; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::prioritised_bit_rate_opts::kbps0; - rlc.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::bucket_size_dur_opts::ms100; - rlc.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; - rlc.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 6; - rlc.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; - rlc.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 6; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; return SRSRAN_SUCCESS; } @@ -605,7 +651,23 @@ int rrc_nr::ue::pack_secondary_cell_group_mac_cfg(asn1::rrc_nr::cell_group_cfg_s mac_cell_group.bsr_cfg_present = true; mac_cell_group.bsr_cfg.periodic_bsr_timer = asn1::rrc_nr::bsr_cfg_s::periodic_bsr_timer_opts::sf20; mac_cell_group.bsr_cfg.retx_bsr_timer = asn1::rrc_nr::bsr_cfg_s::retx_bsr_timer_opts::sf320; + // Skip TAG and PHR config + mac_cell_group.tag_cfg_present = false; + mac_cell_group.tag_cfg.tag_to_add_mod_list_present = true; + mac_cell_group.tag_cfg.tag_to_add_mod_list.resize(1); + mac_cell_group.tag_cfg.tag_to_add_mod_list[0].tag_id = 0; + mac_cell_group.tag_cfg.tag_to_add_mod_list[0].time_align_timer = time_align_timer_opts::infinity; + + mac_cell_group.phr_cfg_present = false; + mac_cell_group.phr_cfg.set_setup(); + mac_cell_group.phr_cfg.setup().phr_periodic_timer = asn1::rrc_nr::phr_cfg_s::phr_periodic_timer_opts::sf500; + mac_cell_group.phr_cfg.setup().phr_prohibit_timer = asn1::rrc_nr::phr_cfg_s::phr_prohibit_timer_opts::sf200; + mac_cell_group.phr_cfg.setup().phr_tx_pwr_factor_change = asn1::rrc_nr::phr_cfg_s::phr_tx_pwr_factor_change_opts::db3; + mac_cell_group.phr_cfg.setup().multiple_phr = true; + mac_cell_group.phr_cfg.setup().dummy = false; + mac_cell_group.phr_cfg.setup().phr_type2_other_cell = false; + mac_cell_group.phr_cfg.setup().phr_mode_other_cg = asn1::rrc_nr::phr_cfg_s::phr_mode_other_cg_opts::real; return SRSRAN_SUCCESS; } @@ -614,53 +676,25 @@ int rrc_nr::ue::pack_sp_cell_cfg_ded_init_dl_bwp(asn1::rrc_nr::cell_group_cfg_s& { cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp_present = true; - pack_sp_cell_cfg_ded_init_dl_bwp_pdcch_cfg(cell_group_cfg_pack); pack_sp_cell_cfg_ded_init_dl_bwp_pdsch_cfg(cell_group_cfg_pack); + pack_sp_cell_cfg_ded_init_dl_bwp_radio_link_monitoring(cell_group_cfg_pack); return SRSRAN_SUCCESS; } -int rrc_nr::ue::pack_sp_cell_cfg_ded_init_dl_bwp_pdcch_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) +int rrc_nr::ue::pack_sp_cell_cfg_ded_init_dl_bwp_radio_link_monitoring( + asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) { - // PDCCH config - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg_present = true; - auto& pdcch_cfg_dedicated = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg; - auto& pdcch_ded_setup = pdcch_cfg_dedicated.set_setup(); - pdcch_ded_setup.ctrl_res_set_to_add_mod_list_present = true; - pdcch_ded_setup.ctrl_res_set_to_add_mod_list.resize(1); - pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].ctrl_res_set_id = 2; - pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].freq_domain_res.from_number( - 0b111111110000000000000000000000000000000000000); - pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].dur = 1; - pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].cce_reg_map_type.set_non_interleaved(); - pdcch_ded_setup.ctrl_res_set_to_add_mod_list[0].precoder_granularity = - asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; - - // search spaces - pdcch_ded_setup.search_spaces_to_add_mod_list_present = true; - pdcch_ded_setup.search_spaces_to_add_mod_list.resize(1); - pdcch_ded_setup.search_spaces_to_add_mod_list[0].search_space_id = 2; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].ctrl_res_set_id_present = true; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].ctrl_res_set_id = 2; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].monitoring_slot_periodicity_and_offset_present = true; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].monitoring_slot_periodicity_and_offset.set_sl1(); - pdcch_ded_setup.search_spaces_to_add_mod_list[0].monitoring_symbols_within_slot_present = true; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].monitoring_symbols_within_slot.from_number(0b10000000000000); - pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates_present = true; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level1 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level1_opts::n0; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level2 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level2_opts::n2; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level4 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level4_opts::n2; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level8 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].nrof_candidates.aggregation_level16 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].search_space_type_present = true; - pdcch_ded_setup.search_spaces_to_add_mod_list[0].search_space_type.set_ue_specific(); - pdcch_ded_setup.search_spaces_to_add_mod_list[0].search_space_type.ue_specific().dci_formats = asn1::rrc_nr:: - search_space_s::search_space_type_c_::ue_specific_s_::dci_formats_opts::formats0_minus0_and_minus1_minus0; + cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.radio_link_monitoring_cfg_present = true; + auto& radio_link_monitoring = cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.radio_link_monitoring_cfg; + radio_link_monitoring.set_setup().fail_detection_res_to_add_mod_list_present = true; + + // add resource to detect RLF + radio_link_monitoring.set_setup().fail_detection_res_to_add_mod_list.resize(1); + auto& fail_detec_res_elem = radio_link_monitoring.set_setup().fail_detection_res_to_add_mod_list[0]; + fail_detec_res_elem.radio_link_monitoring_rs_id = 0; + fail_detec_res_elem.purpose = asn1::rrc_nr::radio_link_monitoring_rs_s::purpose_opts::rlf; + fail_detec_res_elem.detection_res.set_ssb_idx() = 0; return SRSRAN_SUCCESS; } @@ -893,66 +927,6 @@ int rrc_nr::ue::pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(asn1::rrc_nr::cell_g return SRSRAN_SUCCESS; } -int rrc_nr::ue::pack_sp_cell_cfg_ded_csi_meas_cfg_csi_report_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) -{ - // CSI report config - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list_present = true; - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list.resize(1); - auto& csi_report = - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.setup().csi_report_cfg_to_add_mod_list[0]; - csi_report.report_cfg_id = 0; - csi_report.res_for_ch_meas = 0; - csi_report.csi_im_res_for_interference_present = true; - csi_report.csi_im_res_for_interference = 1; - csi_report.report_cfg_type.set_periodic(); - csi_report.report_cfg_type.periodic().report_slot_cfg.set_slots80(); - csi_report.report_cfg_type.periodic().pucch_csi_res_list.resize(1); - csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].ul_bw_part_id = 0; - csi_report.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res = 1; // was 17 in orig PCAP - csi_report.report_quant.set_cri_ri_pmi_cqi(); - // Report freq config (optional) - csi_report.report_freq_cfg_present = true; - csi_report.report_freq_cfg.cqi_format_ind_present = true; - csi_report.report_freq_cfg.cqi_format_ind = - asn1::rrc_nr::csi_report_cfg_s::report_freq_cfg_s_::cqi_format_ind_opts::wideband_cqi; - csi_report.time_restrict_for_ch_meass = asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_ch_meass_opts::not_cfgured; - csi_report.time_restrict_for_interference_meass = - asn1::rrc_nr::csi_report_cfg_s::time_restrict_for_interference_meass_opts::not_cfgured; - csi_report.group_based_beam_report.set_disabled(); - // Skip CQI table (optional) - csi_report.cqi_table_present = true; - csi_report.cqi_table = asn1::rrc_nr::csi_report_cfg_s::cqi_table_opts::table2; - csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1; - - if (parent->cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { - csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 5; - } else { - csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 7; - } - - return SRSRAN_SUCCESS; -} - -int rrc_nr::ue::pack_sp_cell_cfg_ded_csi_meas_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) -{ - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present = true; - cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg.set_setup(); - - // NOTE: Disable CQI configuration until srsENB NR PHY supports it - // pack_sp_cell_cfg_ded_csi_meas_cfg_csi_report_cfg(cell_group_cfg_pack); - - // nzp-CSI-RS Resource and ResourceSet - fill_serv_cell_from_enb_cfg(parent->cfg, cell_group_cfg_pack.sp_cell_cfg.sp_cell_cfg_ded); - - // CSI IM config - // TODO: add csi im config - - // CSI resource config - // TODO: add csi resource config - - return SRSRAN_SUCCESS; -} - int rrc_nr::ue::pack_sp_cell_cfg_ded(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) { // SP Cell Dedicated config @@ -971,8 +945,10 @@ int rrc_nr::ue::pack_sp_cell_cfg_ded(asn1::rrc_nr::cell_group_cfg_s& cell_group_ // Serving cell config (only to setup) pack_sp_cell_cfg_ded_pdcch_serving_cell_cfg(cell_group_cfg_pack); - // CSI meas config - pack_sp_cell_cfg_ded_csi_meas_cfg(cell_group_cfg_pack); + // spCellConfig + if (fill_sp_cell_cfg_from_enb_cfg(parent->cfg, UE_PSCELL_CC_IDX, cell_group_cfg_pack.sp_cell_cfg) != SRSRAN_SUCCESS) { + parent->logger.error("Failed to pack spCellConfig for rnti=0x%x", rnti); + } return SRSRAN_SUCCESS; } @@ -1006,61 +982,6 @@ int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_common_phy_cell_g return SRSRAN_SUCCESS; } -int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdcch_cfg_common( - asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) -{ - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp - .pdcch_cfg_common_present = true; - auto& pdcch_cfg_common = - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common; - - pdcch_cfg_common.set_setup(); - pdcch_cfg_common.setup().common_ctrl_res_set_present = true; - pdcch_cfg_common.setup().common_ctrl_res_set.ctrl_res_set_id = 1; - pdcch_cfg_common.setup().common_ctrl_res_set.freq_domain_res.from_number( - 0b111111110000000000000000000000000000000000000); - pdcch_cfg_common.setup().common_ctrl_res_set.dur = 1; - pdcch_cfg_common.setup().common_ctrl_res_set.cce_reg_map_type.set_non_interleaved(); - pdcch_cfg_common.setup().common_ctrl_res_set.precoder_granularity = - asn1::rrc_nr::ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle; - - // common search space list - pdcch_cfg_common.setup().common_search_space_list_present = true; - pdcch_cfg_common.setup().common_search_space_list.resize(1); - pdcch_cfg_common.setup().common_search_space_list[0].search_space_id = 1; - pdcch_cfg_common.setup().common_search_space_list[0].ctrl_res_set_id_present = true; - pdcch_cfg_common.setup().common_search_space_list[0].ctrl_res_set_id = 1; - pdcch_cfg_common.setup().common_search_space_list[0].search_space_type_present = true; - pdcch_cfg_common.setup().common_search_space_list[0].search_space_type.set_common(); - pdcch_cfg_common.setup() - .common_search_space_list[0] - .search_space_type.common() - .dci_format0_minus0_and_format1_minus0_present = true; - pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates_present = true; - pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level1 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level1_opts::n1; - pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level2 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level2_opts::n1; - pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level4 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1; - pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level8 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0; - pdcch_cfg_common.setup().common_search_space_list[0].nrof_candidates.aggregation_level16 = - asn1::rrc_nr::search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0; - pdcch_cfg_common.setup().common_search_space_list[0].monitoring_slot_periodicity_and_offset_present = true; - pdcch_cfg_common.setup().common_search_space_list[0].monitoring_slot_periodicity_and_offset.set_sl1(); - pdcch_cfg_common.setup().common_search_space_list[0].monitoring_symbols_within_slot_present = true; - pdcch_cfg_common.setup().common_search_space_list[0].monitoring_symbols_within_slot.from_number(0b10000000000000); - pdcch_cfg_common.setup().ra_search_space_present = true; - pdcch_cfg_common.setup().ra_search_space = 1; - - if (parent->cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { - pdcch_cfg_common.setup().ext = false; - } - - return SRSRAN_SUCCESS; -} - int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common( asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) { @@ -1089,7 +1010,6 @@ int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp( init_dl_bwp.generic_params.location_and_bw = 14025; init_dl_bwp.generic_params.subcarrier_spacing = subcarrier_spacing_opts::khz15; - pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdcch_cfg_common(cell_group_cfg_pack); pack_recfg_with_sync_sp_cell_cfg_common_dl_cfg_init_dl_bwp_pdsch_cfg_common(cell_group_cfg_pack); return SRSRAN_SUCCESS; @@ -1113,8 +1033,10 @@ int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_freq_info_ { cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul_present = true; auto& freq_info_ul = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.freq_info_ul; + freq_info_ul.freq_band_list_present = true; freq_info_ul.freq_band_list.push_back(parent->cfg.cell_list[0].band); - freq_info_ul.absolute_freq_point_a = parent->cfg.cell_list[0].ul_absolute_freq_point_a; + freq_info_ul.absolute_freq_point_a_present = true; + freq_info_ul.absolute_freq_point_a = parent->cfg.cell_list[0].ul_absolute_freq_point_a; freq_info_ul.scs_specific_carrier_list.resize(1); auto& ul_carrier = freq_info_ul.scs_specific_carrier_list[0]; @@ -1158,6 +1080,10 @@ int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bw rach_cfg_common_pack.setup().prach_root_seq_idx.set_l839() = 0; // matches value in phy_cfg_nr_default_t() rach_cfg_common_pack.setup().restricted_set_cfg = asn1::rrc_nr::rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set; + rach_cfg_common_pack.setup().ssb_per_rach_occasion_and_cb_preambs_per_ssb_present = true; + rach_cfg_common_pack.setup().ssb_per_rach_occasion_and_cb_preambs_per_ssb.set_one(); + rach_cfg_common_pack.setup().ssb_per_rach_occasion_and_cb_preambs_per_ssb.one() = + asn1::rrc_nr::rach_cfg_common_s::ssb_per_rach_occasion_and_cb_preambs_per_ssb_c_::one_opts::n64; return SRSRAN_SUCCESS; } @@ -1183,7 +1109,8 @@ int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bw pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].map_type = asn1::rrc_nr::pusch_time_domain_res_alloc_s::map_type_opts::type_a; pusch_cfg_common_pack.setup().pusch_time_domain_alloc_list[1].start_symbol_and_len = 27; - pusch_cfg_common_pack.setup().p0_nominal_with_grant = -90; + pusch_cfg_common_pack.setup().p0_nominal_with_grant_present = true; + pusch_cfg_common_pack.setup().p0_nominal_with_grant = -60; // PUCCH config common cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ul_cfg_common.init_ul_bwp @@ -1193,7 +1120,7 @@ int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common_init_ul_bw pucch_cfg_common_pack.set_setup(); pucch_cfg_common_pack.setup().pucch_group_hop = asn1::rrc_nr::pucch_cfg_common_s::pucch_group_hop_opts::neither; pucch_cfg_common_pack.setup().p0_nominal_present = true; - pucch_cfg_common_pack.setup().p0_nominal = -90; + pucch_cfg_common_pack.setup().p0_nominal = -60; return SRSRAN_SUCCESS; } @@ -1226,62 +1153,12 @@ int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common( return SRSRAN_SUCCESS; } -int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_ssb_cfg(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) -{ - // SSB config (optional) - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_positions_in_burst_present = true; - auto& ssb_pos_in_burst = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_positions_in_burst; - - if (parent->cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { - ssb_pos_in_burst.set_short_bitmap().from_number(0b1000); - } else { - ssb_pos_in_burst.set_medium_bitmap().from_number(0b10000000); - } - - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_periodicity_serving_cell_present = true; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_periodicity_serving_cell = - serving_cell_cfg_common_s::ssb_periodicity_serving_cell_opts::ms20; - - return SRSRAN_SUCCESS; -} - -int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common_tdd_ul_dl_cfg_common( - asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) -{ - // TDD UL-DL config - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.tdd_ul_dl_cfg_common_present = true; - auto& tdd_config = cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.tdd_ul_dl_cfg_common; - tdd_config.ref_subcarrier_spacing = subcarrier_spacing_e::khz15; - tdd_config.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10; - tdd_config.pattern1.nrof_dl_slots = 6; - tdd_config.pattern1.nrof_dl_symbols = 0; - tdd_config.pattern1.nrof_ul_slots = 4; - tdd_config.pattern1.nrof_ul_symbols = 0; - - return SRSRAN_SUCCESS; -} - int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) { auto& pscell_cfg = parent->cfg.cell_list.at(UE_PSCELL_CC_IDX); - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common_present = true; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ss_pbch_block_pwr = 0; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.n_timing_advance_offset = - asn1::rrc_nr::serving_cell_cfg_common_s::n_timing_advance_offset_opts::n0; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dmrs_type_a_position = - asn1::rrc_nr::serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci_present = true; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.pci = pscell_cfg.phy_cell.carrier.pci; - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing_present = true; - - if (parent->cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) { - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing = - subcarrier_spacing_opts::khz15; - } else { + if (pscell_cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.smtc.release(); - cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.ssb_subcarrier_spacing = - subcarrier_spacing_opts::khz30; } // DL config @@ -1290,19 +1167,14 @@ int rrc_nr::ue::pack_recfg_with_sync_sp_cell_cfg_common(asn1::rrc_nr::cell_group // UL config pack_recfg_with_sync_sp_cell_cfg_common_ul_cfg_common(cell_group_cfg_pack); - // SSB config (optional) - pack_recfg_with_sync_sp_cell_cfg_common_ssb_cfg(cell_group_cfg_pack); - - if (parent->cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { - pack_recfg_with_sync_sp_cell_cfg_common_tdd_ul_dl_cfg_common(cell_group_cfg_pack); - } - return SRSRAN_SUCCESS; } int rrc_nr::ue::pack_recfg_with_sync(asn1::rrc_nr::cell_group_cfg_s& cell_group_cfg_pack) { // Reconfig with Sync + cell_group_cfg_pack.cell_group_id = 1; // 0 identifies the MCG. Other values identify SCGs. + cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync_present = true; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.new_ue_id = rnti; cell_group_cfg_pack.sp_cell_cfg.recfg_with_sync.t304 = recfg_with_sync_s::t304_opts::ms1000; @@ -1316,6 +1188,7 @@ int rrc_nr::ue::pack_secondary_cell_group_sp_cell_cfg(asn1::rrc_nr::cell_group_c { cell_group_cfg_pack.sp_cell_cfg_present = true; cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx_present = true; + cell_group_cfg_pack.sp_cell_cfg.serv_cell_idx = 1; // Serving cell ID of a PSCell. The PCell of the MCG uses ID 0. pack_sp_cell_cfg_ded(cell_group_cfg_pack); pack_recfg_with_sync(cell_group_cfg_pack); @@ -1445,7 +1318,14 @@ void rrc_nr::ue::crnti_ce_received() uecfg.ue_bearers[drb.lc_ch_id].group = drb.mac_lc_ch_cfg.ul_specific_params.lc_ch_group; } + // Update UE phy params srsran::make_pdsch_cfg_from_serv_cell(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded, &uecfg.phy_cfg.pdsch); + srsran::make_csi_cfg_from_serv_cell(cell_group_cfg.sp_cell_cfg.sp_cell_cfg_ded, &uecfg.phy_cfg.csi); + srsran::make_phy_ssb_cfg(parent->cfg.cell_list[0].phy_cell.carrier, + cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common, + &uecfg.phy_cfg.ssb); + srsran::make_duplex_cfg_from_serv_cell(cell_group_cfg.sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common, + &uecfg.phy_cfg.duplex); parent->mac->ue_cfg(rnti, uecfg); } @@ -1466,18 +1346,18 @@ int rrc_nr::ue::add_drb() cell_group_cfg_pack.rlc_bearer_to_add_mod_list_present = true; cell_group_cfg_pack.rlc_bearer_to_add_mod_list.resize(1); - auto& rlc = cell_group_cfg_pack.rlc_bearer_to_add_mod_list[0]; - rlc.lc_ch_id = drb1_lcid; - rlc.served_radio_bearer_present = true; - rlc.served_radio_bearer.set_drb_id(); - rlc.served_radio_bearer.drb_id() = 1; - rlc.rlc_cfg_present = true; - rlc.rlc_cfg.set_um_bi_dir(); - rlc.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present = true; - rlc.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len = sn_field_len_um_opts::size12; - rlc.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present = true; - rlc.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len = sn_field_len_um_opts::size12; - rlc.rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_opts::ms50; + auto& rlc_bearer = cell_group_cfg_pack.rlc_bearer_to_add_mod_list[0]; + rlc_bearer.lc_ch_id = drb1_lcid; + rlc_bearer.served_radio_bearer_present = true; + rlc_bearer.served_radio_bearer.set_drb_id(); + rlc_bearer.served_radio_bearer.drb_id() = 1; + rlc_bearer.rlc_cfg_present = true; + rlc_bearer.rlc_cfg.set_um_bi_dir(); + rlc_bearer.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len_present = true; + rlc_bearer.rlc_cfg.um_bi_dir().ul_um_rlc.sn_field_len = sn_field_len_um_opts::size12; + rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len_present = true; + rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.sn_field_len = sn_field_len_um_opts::size12; + rlc_bearer.rlc_cfg.um_bi_dir().dl_um_rlc.t_reassembly = t_reassembly_opts::ms50; // add RLC bearer srsran::rlc_config_t rlc_cfg; @@ -1488,17 +1368,17 @@ int rrc_nr::ue::add_drb() parent->rlc->add_bearer(rnti, drb1_lcid, rlc_cfg); // MAC logical channel config - rlc.mac_lc_ch_cfg_present = true; - rlc.mac_lc_ch_cfg.ul_specific_params_present = true; - rlc.mac_lc_ch_cfg.ul_specific_params.prio = 11; - rlc.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = + rlc_bearer.mac_lc_ch_cfg_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.prio = 11; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.prioritised_bit_rate = asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::prioritised_bit_rate_opts::kbps0; - rlc.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.bucket_size_dur = asn1::rrc_nr::lc_ch_cfg_s::ul_specific_params_s_::bucket_size_dur_opts::ms100; - rlc.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; - rlc.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 3; - rlc.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; - rlc.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.lc_ch_group = 3; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.sched_request_id_present = true; + rlc_bearer.mac_lc_ch_cfg.ul_specific_params.sched_request_id = 0; // TODO: add LC config to MAC // PDCP config goes into radio_bearer_cfg @@ -1526,11 +1406,27 @@ int rrc_nr::ue::add_drb() // Add DRB1 to PDCP srsran::pdcp_config_t pdcp_cnfg = srsran::make_drb_pdcp_config_t(drb_item.drb_id, false, drb_item.pdcp_cfg); - parent->pdcp->add_bearer(rnti, rlc.lc_ch_id, pdcp_cnfg); + parent->pdcp->add_bearer(rnti, rlc_bearer.lc_ch_id, pdcp_cnfg); // Note: DRB1 is only activated in the MAC when the C-RNTI CE is received return SRSRAN_SUCCESS; } +/** + * @brief Deactivate all Bearers (MAC logical channel) for this specific RNTI + * + * The function iterates over the bearers or MAC logical channels and deactivates them by setting each one to IDLE + */ +void rrc_nr::ue::deactivate_bearers() +{ + // Iterate over the bearers (MAC LC CH) and set each of them to IDLE + for (auto& ue_bearer : uecfg.ue_bearers) { + ue_bearer.direction = mac_lc_ch_cfg_t::IDLE; + } + + // No need to check the returned value, as the function ue_cfg will return SRSRAN_SUCCESS (it asserts if it fails) + parent->mac->ue_cfg(rnti, uecfg); +} + } // namespace srsenb diff --git a/srsenb/src/stack/rrc/rrc_ue.cc b/srsenb/src/stack/rrc/rrc_ue.cc index cbfb2a157..4441cbdbf 100644 --- a/srsenb/src/stack/rrc/rrc_ue.cc +++ b/srsenb/src/stack/rrc/rrc_ue.cc @@ -406,13 +406,16 @@ void rrc::ue::parse_ul_dcch(uint32_t lcid, srsran::unique_byte_buffer_t pdu) } break; case ul_dcch_msg_type_c::c1_c_::types::meas_report: - if (mobility_handler != nullptr) { - mobility_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report(), std::move(original_pdu)); + if (state == RRC_STATE_REGISTERED) { + if (mobility_handler != nullptr) { + mobility_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report(), std::move(original_pdu)); + } + if (endc_handler != nullptr) { + endc_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report()); + } } else { - parent->logger.warning("Received MeasReport but no mobility configuration is available"); - } - if (endc_handler != nullptr) { - endc_handler->handle_ue_meas_report(ul_dcch_msg.msg.c1().meas_report()); + parent->logger.warning( + "measurementReport for rnti=0x%x ignored. Cause: RRC Reconfiguration is not yet complete", rnti); } break; case ul_dcch_msg_type_c::c1_c_::types::ue_info_resp_r9: @@ -661,6 +664,10 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) srsran::console( "User 0x%x requesting RRC Reestablishment as 0x%x. Cause: %s\n", rnti, old_rnti, req_r8.reest_cause.to_string()); + if (endc_handler != nullptr) { + old_ue->endc_handler->trigger(rrc_endc::rrc_reest_rx_ev{}); + } + // Cancel Handover in Target eNB if on-going asn1::s1ap::cause_c cause; cause.set_radio_network().value = asn1::s1ap::cause_radio_network_opts::interaction_with_other_proc; @@ -699,6 +706,17 @@ void rrc::ue::handle_rrc_con_reest_req(rrc_conn_reest_request_s* msg) eutra_capabilities.to_json(js); parent->logger.debug("rnti=0x%x EUTRA capabilities: %s", rnti, js.to_string().c_str()); } + if (endc_handler) { + if (req_r8.reest_cause.value == reest_cause_opts::recfg_fail) { + // In case of Reestablishment due to ReconfFailure, avoid re-enabling NR EN-DC, otherwise + // the eNB and UE may enter in a reconfiguration + reestablishment loop. + endc_handler->trigger(rrc_endc::disable_endc_ev{}); + } else { + // In case of Reestablishment with cause other than ReconfFailure, recompute whether + // the new RNTI supports NR EN-DC. + endc_handler->handle_eutra_capabilities(eutra_capabilities); + } + } // Recover GTP-U tunnels and S1AP context parent->gtpu->mod_bearer_rnti(old_rnti, rnti); @@ -1237,12 +1255,11 @@ void rrc::ue::update_scells() const ue_cell_ded* pcell = ue_cell_list.get_ue_cc_idx(UE_PCELL_CC_IDX); const enb_cell_common* pcell_cfg = pcell->cell_common; - if (ue_cell_list.nof_cells() == pcell_cfg->scells.size() + 1) { - // SCells already added + // Check whether UE supports CA + if (eutra_capabilities.access_stratum_release.to_number() < 10) { + parent->logger.info("UE doesn't support CA. Skipping SCell activation"); return; } - - // Check whether UE supports CA if (not eutra_capabilities.non_crit_ext_present or not eutra_capabilities.non_crit_ext.non_crit_ext_present or not eutra_capabilities.non_crit_ext.non_crit_ext.non_crit_ext_present or not eutra_capabilities.non_crit_ext.non_crit_ext.non_crit_ext.rf_params_v1020_present or @@ -1252,6 +1269,11 @@ void rrc::ue::update_scells() return; } + if (ue_cell_list.nof_cells() == pcell_cfg->scells.size() + 1) { + // SCells already added + return; + } + for (const enb_cell_common* scell : pcell_cfg->scells) { ue_cell_list.add_cell(scell->enb_cc_idx); } diff --git a/srsenb/src/stack/s1ap/s1ap.cc b/srsenb/src/stack/s1ap/s1ap.cc index 93d1bb231..f1e0293eb 100644 --- a/srsenb/src/stack/s1ap/s1ap.cc +++ b/srsenb/src/stack/s1ap/s1ap.cc @@ -562,17 +562,29 @@ bool s1ap::handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, // Received notification union sctp_notification* notification = (union sctp_notification*)pdu->msg; logger.info("SCTP Notification %04x", notification->sn_header.sn_type); + bool restart_s1 = false; if (notification->sn_header.sn_type == SCTP_SHUTDOWN_EVENT) { logger.info("SCTP Association Shutdown. Association: %d", sri.sinfo_assoc_id); srsran::console("SCTP Association Shutdown. Association: %d\n", sri.sinfo_assoc_id); - rx_socket_handler->remove_socket(mme_socket.get_socket()); - mme_socket.close(); + restart_s1 = true; } else if (notification->sn_header.sn_type == SCTP_PEER_ADDR_CHANGE && notification->sn_paddr_change.spc_state == SCTP_ADDR_UNREACHABLE) { logger.info("SCTP peer addres unreachable. Association: %d", sri.sinfo_assoc_id); srsran::console("SCTP peer address unreachable. Association: %d\n", sri.sinfo_assoc_id); + restart_s1 = true; + } + if (restart_s1) { + logger.info("Restarting S1 connection"); + srsran::console("Restarting S1 connection\n"); rx_socket_handler->remove_socket(mme_socket.get_socket()); mme_socket.close(); + while (users.size() != 0) { + std::unordered_map >::iterator it = users.begin(); + uint16_t rnti = it->second->ctxt.rnti; + rrc->release_erabs(rnti); + rrc->release_ue(rnti); + users.erase(it->second.get()); + } } } else if (pdu->N_bytes == 0) { logger.error("SCTP return 0 bytes. Closing socket"); diff --git a/srsenb/src/stack/upper/gtpu.cc b/srsenb/src/stack/upper/gtpu.cc index 2a860465b..e8ee524f4 100644 --- a/srsenb/src/stack/upper/gtpu.cc +++ b/srsenb/src/stack/upper/gtpu.cc @@ -133,14 +133,16 @@ gtpu_tunnel_manager::add_tunnel(uint16_t rnti, uint32_t eps_bearer_id, uint32_t bool gtpu_tunnel_manager::update_rnti(uint16_t old_rnti, uint16_t new_rnti) { auto* old_rnti_ptr = find_rnti_tunnels(old_rnti); - if (old_rnti_ptr == nullptr or find_rnti_tunnels(new_rnti) != nullptr) { + auto* new_rnti_ptr = find_rnti_tunnels(new_rnti); + if (old_rnti_ptr == nullptr or (new_rnti_ptr != nullptr and not new_rnti_ptr->empty())) { + // The old rnti must exist and the new rnti TEID list must be empty logger.error("Modifying bearer rnti. Old rnti=0x%x, new rnti=0x%x", old_rnti, new_rnti); return false; } logger.info("Modifying bearer rnti. Old rnti: 0x%x, new rnti: 0x%x", old_rnti, new_rnti); // create new RNTI and update TEIDs of old rnti to reflect new rnti - if (not ue_teidin_db.insert(new_rnti, ue_bearer_tunnel_list())) { + if (new_rnti_ptr == nullptr and not ue_teidin_db.insert(new_rnti, ue_bearer_tunnel_list())) { logger.error("Failure to create new rnti=0x%x", new_rnti); return false; } diff --git a/srsenb/test/common/dummy_classes_nr.h b/srsenb/test/common/dummy_classes_nr.h index fb4983e97..d1381a028 100644 --- a/srsenb/test/common/dummy_classes_nr.h +++ b/srsenb/test/common/dummy_classes_nr.h @@ -32,7 +32,7 @@ class rrc_nr_dummy : public rrc_interface_mac_nr public: int read_pdu_bcch_bch(const uint32_t tti, srsran::unique_byte_buffer_t& buffer) { return SRSRAN_SUCCESS; } int read_pdu_bcch_dlsch(uint32_t sib_index, srsran::unique_byte_buffer_t& buffer) { return SRSRAN_SUCCESS; } - int add_user(uint16_t rnti) { return SRSRAN_SUCCESS; } + int add_user(uint16_t rnti, const sched_nr_ue_cfg_t& uecfg) { return SRSRAN_SUCCESS; } int update_user(uint16_t new_rnti, uint16_t old_rnti) { return SRSRAN_SUCCESS; } void set_activity_user(uint16_t rnti) {} }; @@ -49,7 +49,7 @@ class mac_nr_dummy : public mac_interface_rrc_nr { public: int cell_cfg(const std::vector& nr_cells) override { return SRSRAN_SUCCESS; } - uint16_t reserve_rnti(uint32_t enb_cc_idx) override { return 0x4601; } + uint16_t reserve_rnti(uint32_t enb_cc_idx, const sched_nr_ue_cfg_t& uecfg) override { return 0x4601; } int ue_cfg(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg) override { return SRSRAN_SUCCESS; } diff --git a/srsenb/test/mac/nr/sched_nr_cfg_generators.h b/srsenb/test/mac/nr/sched_nr_cfg_generators.h index c3e239a0e..a2f650041 100644 --- a/srsenb/test/mac/nr/sched_nr_cfg_generators.h +++ b/srsenb/test/mac/nr/sched_nr_cfg_generators.h @@ -87,6 +87,25 @@ inline std::vector get_default_cells_cfg( return cells; } +inline sched_nr_interface::ue_cfg_t get_rach_ue_cfg(uint32_t cc) +{ + sched_nr_interface::ue_cfg_t uecfg{}; + + // set Pcell + uecfg.carriers.resize(1); + uecfg.carriers[0].active = true; + uecfg.carriers[0].cc = cc; + + // set SRB0 as active + uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH; + + // set basic PHY config + uecfg.phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}; + uecfg.phy_cfg.csi = {}; + + return uecfg; +} + inline sched_nr_interface::ue_cfg_t get_default_ue_cfg( uint32_t nof_cc, const srsran::phy_cfg_nr_t& phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}) @@ -100,10 +119,6 @@ inline sched_nr_interface::ue_cfg_t get_default_ue_cfg( uecfg.phy_cfg = phy_cfg; uecfg.ue_bearers[0].direction = mac_lc_ch_cfg_t::BOTH; - // Note: dynamic MCS not yet supported - uecfg.fixed_dl_mcs = 28; - uecfg.fixed_ul_mcs = 28; - return uecfg; } diff --git a/srsenb/test/mac/nr/sched_nr_rar_test.cc b/srsenb/test/mac/nr/sched_nr_rar_test.cc index e502c22c3..e10bf898f 100644 --- a/srsenb/test/mac/nr/sched_nr_rar_test.cc +++ b/srsenb/test/mac/nr/sched_nr_rar_test.cc @@ -39,15 +39,15 @@ void test_single_prach() std::default_random_engine rgen(rand_gen()); // Set scheduler configuration - sched_nr_interface::sched_cfg_t sched_cfg{}; + sched_nr_interface::sched_args_t sched_cfg{}; sched_cfg.auto_refill_buffer = std::uniform_int_distribution{0, 1}(rgen) > 0; // Set cells configuration std::vector cells_cfg = get_default_cells_cfg(1); sched_params schedparams{sched_cfg}; schedparams.cells.emplace_back(0, cells_cfg[0], sched_cfg); - const bwp_params& bwpparams = schedparams.cells[0].bwps[0]; - slot_ue_map_t slot_ues; + const bwp_params_t& bwpparams = schedparams.cells[0].bwps[0]; + slot_ue_map_t slot_ues; ra_sched rasched(bwpparams); TESTASSERT(rasched.empty()); @@ -90,7 +90,7 @@ void test_single_prach() } // A PRACH arrives... - sched_nr_interface::dl_sched_rar_info_t rainfo{}; + sched_nr_interface::rar_info_t rainfo{}; rainfo.preamble_idx = 10; rainfo.temp_crnti = rnti; rainfo.prach_slot = prach_slot; diff --git a/srsenb/test/mac/nr/sched_nr_sim_ue.cc b/srsenb/test/mac/nr/sched_nr_sim_ue.cc index cff5bd0ae..0512e29fd 100644 --- a/srsenb/test/mac/nr/sched_nr_sim_ue.cc +++ b/srsenb/test/mac/nr/sched_nr_sim_ue.cc @@ -23,6 +23,7 @@ #include "sched_nr_common_test.h" #include "sched_nr_ue_ded_test_suite.h" #include "srsran/common/test_common.h" +#include "srsran/common/thread_pool.h" namespace srsenb { @@ -46,7 +47,7 @@ sched_nr_ue_sim::sched_nr_ue_sim(uint16_t rnti_, } } -int sched_nr_ue_sim::update(const sched_nr_cc_output_res_t& cc_out) +int sched_nr_ue_sim::update(const sched_nr_cc_result_view& cc_out) { update_dl_harqs(cc_out); @@ -69,7 +70,7 @@ int sched_nr_ue_sim::update(const sched_nr_cc_output_res_t& cc_out) return SRSRAN_SUCCESS; } -void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_output_res_t& cc_out) +void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_result_view& cc_out) { uint32_t cc = cc_out.cc; for (uint32_t i = 0; i < cc_out.dl_cc_result->dl_sched.pdcch_dl.size(); ++i) { @@ -99,93 +100,167 @@ void sched_nr_ue_sim::update_dl_harqs(const sched_nr_cc_output_res_t& cc_out) } } -sched_nr_sim_base::sched_nr_sim_base(const sched_nr_interface::sched_cfg_t& sched_args, - const std::vector& cell_cfg_list, - std::string test_name_) : +sched_nr_base_tester::sched_nr_base_tester(const sched_nr_interface::sched_args_t& sched_args, + const std::vector& cell_cfg_list, + std::string test_name_, + uint32_t nof_workers) : logger(srslog::fetch_basic_logger("TEST")), mac_logger(srslog::fetch_basic_logger("MAC")), sched_ptr(new sched_nr()), test_name(std::move(test_name_)) { - logger.info("\n=========== Start %s ===========", test_name.c_str()); + sem_init(&slot_sem, 0, 1); + + printf("\n=========== Start %s ===========\n", test_name.c_str()); cell_params.reserve(cell_cfg_list.size()); for (uint32_t cc = 0; cc < cell_cfg_list.size(); ++cc) { cell_params.emplace_back(cc, cell_cfg_list[cc], sched_args); } sched_ptr->config(sched_args, cell_cfg_list); // call parent cfg + cc_workers.resize(nof_workers - 1); + for (uint32_t i = 0; i < cc_workers.size(); ++i) { + fmt::memory_buffer fmtbuf; + fmt::format_to(fmtbuf, "worker{}", i + 1); + cc_workers[i].reset(new srsran::task_worker{to_string(fmtbuf), 10}); + } + + cc_results.resize(cell_params.size()); + TESTASSERT(cell_params.size() > 0); } -sched_nr_sim_base::~sched_nr_sim_base() +sched_nr_base_tester::~sched_nr_base_tester() { - logger.info("=========== End %s ==========\n", test_name.c_str()); + stop(); } -int sched_nr_sim_base::add_user(uint16_t rnti, - const sched_nr_interface::ue_cfg_t& ue_cfg_, - slot_point tti_rx, - uint32_t preamble_idx) +void sched_nr_base_tester::stop() { - TESTASSERT(ue_db.count(rnti) == 0); + bool stopping = not stopped.exchange(true); + if (stopping) { + sem_wait(&slot_sem); + sem_post(&slot_sem); + for (auto& worker : cc_workers) { + worker->stop(); + } + sem_destroy(&slot_sem); + printf("============ End %s ===========\n", test_name.c_str()); + } +} +int sched_nr_base_tester::add_user(uint16_t rnti, + const sched_nr_interface::ue_cfg_t& ue_cfg_, + slot_point tti_rx, + uint32_t preamble_idx) +{ + sem_wait(&slot_sem); sched_ptr->ue_cfg(rnti, ue_cfg_); + + TESTASSERT(ue_db.count(rnti) == 0); + ue_db.insert(std::make_pair(rnti, sched_nr_ue_sim(rnti, ue_cfg_, current_slot_tx, preamble_idx))); - sched_nr_interface::dl_sched_rar_info_t rach_info{}; + sched_nr_interface::rar_info_t rach_info{}; rach_info.temp_crnti = rnti; rach_info.prach_slot = tti_rx; rach_info.preamble_idx = preamble_idx; rach_info.msg3_size = 7; sched_ptr->dl_rach_info(ue_cfg_.carriers[0].cc, rach_info); + sem_post(&slot_sem); + return SRSRAN_SUCCESS; } -void sched_nr_sim_base::new_slot(slot_point slot_tx) +void sched_nr_base_tester::run_slot(slot_point slot_tx) { - std::unique_lock lock(mutex); - while (cc_finished > 0) { - cvar.wait(lock); - } + srsran_assert(not stopped.load(std::memory_order_relaxed), "Running scheduler when it has already been stopped"); + // Block concurrent or out-of-order calls to the scheduler + sem_wait(&slot_sem); + current_slot_tx = slot_tx; + nof_cc_remaining = cell_params.size(); + logger.set_context(slot_tx.to_uint()); mac_logger.set_context(slot_tx.to_uint()); + + // Clear previous slot results + for (uint32_t cc = 0; cc < cc_results.size(); ++cc) { + cc_results[cc] = {}; + } logger.info("---------------- TTI=%d ---------------", slot_tx.to_uint()); - current_slot_tx = slot_tx; - cc_finished = cell_params.size(); + + // Process pending feedback for (auto& ue : ue_db) { ue_nr_slot_events events; set_default_slot_events(ue.second.get_ctxt(), events); set_external_slot_events(ue.second.get_ctxt(), events); apply_slot_events(ue.second.get_ctxt(), events); } -} -void sched_nr_sim_base::update(sched_nr_cc_output_res_t& cc_out) -{ - std::unique_lock lock(mutex); + slot_ctxt = get_enb_ctxt(); + slot_start_tp = std::chrono::steady_clock::now(); - sim_nr_enb_ctxt_t ctxt; - ctxt = get_enb_ctxt(); + // Generate CC result (parallel or serialized) + uint32_t worker_idx = 0; + for (uint32_t cc = 0; cc < cell_params.size(); ++cc) { + if (worker_idx == cc_workers.size()) { + generate_cc_result(cc); + } else { + cc_workers[worker_idx]->push_task([this, cc]() { generate_cc_result(cc); }); + } + worker_idx = (worker_idx + 1) % (cc_workers.size() + 1); + } +} - // Run common tests - test_dl_pdcch_consistency(cc_out.dl_cc_result->dl_sched.pdcch_dl); - test_pdsch_consistency(cc_out.dl_cc_result->dl_sched.pdsch); - test_ssb_scheduled_grant(cc_out.slot, ctxt.cell_params[cc_out.cc].cell_cfg, cc_out.dl_cc_result->dl_sched.ssb); +void sched_nr_base_tester::generate_cc_result(uint32_t cc) +{ + // Run scheduler + sched_ptr->run_slot(current_slot_tx, cc, cc_results[cc].dl_res); + sched_ptr->get_ul_sched(current_slot_tx, cc, cc_results[cc].ul_res); + auto tp2 = std::chrono::steady_clock::now(); + cc_results[cc].cc_latency_ns = std::chrono::duration_cast(tp2 - slot_start_tp); + + if (--nof_cc_remaining > 0) { + // there are still missing CC results + return; + } - // Run UE-dedicated tests - test_dl_sched_result(ctxt, cc_out); + // Run tests and update UE state + process_results(); - for (auto& u : ue_db) { - u.second.update(cc_out); - } + // Notify awaiting new slot worker + sem_post(&slot_sem); +} - if (--cc_finished <= 0) { - cvar.notify_one(); +void sched_nr_base_tester::process_results() +{ + // Derived class-defined tests + process_slot_result(slot_ctxt, cc_results); + + sched_nr_cc_result_view cc_out; + cc_out.slot = current_slot_tx; + for (uint32_t cc = 0; cc < cell_params.size(); ++cc) { + cc_out.cc = cc; + cc_out.dl_cc_result = &cc_results[cc].dl_res; + cc_out.ul_cc_result = &cc_results[cc].ul_res; + + // Run common tests + test_dl_pdcch_consistency(cc_out.dl_cc_result->dl_sched.pdcch_dl); + test_pdsch_consistency(cc_out.dl_cc_result->dl_sched.pdsch); + test_ssb_scheduled_grant(cc_out.slot, cell_params[cc_out.cc].cfg, cc_out.dl_cc_result->dl_sched.ssb); + + // Run UE-dedicated tests + test_dl_sched_result(slot_ctxt, cc_out); + + // Update UE state + for (auto& u : ue_db) { + u.second.update(cc_out); + } } } -int sched_nr_sim_base::set_default_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events) +int sched_nr_base_tester::set_default_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events) { pending_events.cc_list.clear(); pending_events.cc_list.resize(cell_params.size()); @@ -216,7 +291,7 @@ int sched_nr_sim_base::set_default_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, return SRSRAN_SUCCESS; } -int sched_nr_sim_base::apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_slot_events& events) +int sched_nr_base_tester::apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_slot_events& events) { for (uint32_t enb_cc_idx = 0; enb_cc_idx < events.cc_list.size(); ++enb_cc_idx) { const auto& cc_feedback = events.cc_list[enb_cc_idx]; @@ -260,7 +335,7 @@ int sched_nr_sim_base::apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_ return SRSRAN_SUCCESS; } -sim_nr_enb_ctxt_t sched_nr_sim_base::get_enb_ctxt() const +sim_nr_enb_ctxt_t sched_nr_base_tester::get_enb_ctxt() const { sim_nr_enb_ctxt_t ctxt; ctxt.cell_params = cell_params; diff --git a/srsenb/test/mac/nr/sched_nr_sim_ue.h b/srsenb/test/mac/nr/sched_nr_sim_ue.h index 8519247bb..1ee2c0720 100644 --- a/srsenb/test/mac/nr/sched_nr_sim_ue.h +++ b/srsenb/test/mac/nr/sched_nr_sim_ue.h @@ -26,6 +26,11 @@ #include "srsenb/hdr/stack/mac/nr/sched_nr.h" #include "srsran/adt/circular_array.h" #include +#include + +namespace srsran { +class task_worker; +} namespace srsenb { @@ -42,7 +47,7 @@ struct ue_nr_harq_ctxt_t { uint32_t tbs = 0; slot_point last_slot_tx, first_slot_tx, last_slot_ack; }; -struct sched_nr_cc_output_res_t { +struct sched_nr_cc_result_view { slot_point slot; uint32_t cc; const sched_nr_interface::dl_sched_res_t* dl_cc_result; @@ -83,8 +88,8 @@ struct sim_nr_ue_ctxt_t { } }; struct sim_nr_enb_ctxt_t { - srsran::span cell_params; - std::map ue_db; + srsran::span cell_params; + std::map ue_db; }; class sched_nr_ue_sim @@ -95,82 +100,78 @@ public: slot_point prach_slot_rx, uint32_t preamble_idx); - int update(const sched_nr_cc_output_res_t& cc_out); + int update(const sched_nr_cc_result_view& cc_out); const sim_nr_ue_ctxt_t& get_ctxt() const { return ctxt; } sim_nr_ue_ctxt_t& get_ctxt() { return ctxt; } private: - void update_dl_harqs(const sched_nr_cc_output_res_t& sf_out); + void update_dl_harqs(const sched_nr_cc_result_view& sf_out); srslog::basic_logger& logger; sim_nr_ue_ctxt_t ctxt; }; -class sched_nr_sim_base +/// Implementation of features common to sched_nr_sim_parallel and sched_nr_sim +class sched_nr_base_tester { public: - sched_nr_sim_base(const sched_nr_interface::sched_cfg_t& sched_args, - const std::vector& cell_params_, - std::string test_name); - virtual ~sched_nr_sim_base(); + struct cc_result_t { + slot_point slot_tx; + uint32_t cc; + sched_nr_interface::dl_sched_res_t dl_res; + sched_nr_interface::ul_sched_t ul_res; + std::chrono::nanoseconds cc_latency_ns; + }; - int add_user(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg_, slot_point tti_rx, uint32_t preamble_idx); + sched_nr_base_tester(const sched_nr_interface::sched_args_t& sched_args, + const std::vector& cell_params_, + std::string test_name, + uint32_t nof_workers = 1); + virtual ~sched_nr_base_tester(); - void new_slot(slot_point slot_tx); - void update(sched_nr_cc_output_res_t& cc_out); + void run_slot(slot_point slot_tx); + void stop(); - sched_nr_ue_sim& at(uint16_t rnti) { return ue_db.at(rnti); } - const sched_nr_ue_sim& at(uint16_t rnti) const { return ue_db.at(rnti); } - sched_nr_ue_sim* find_rnti(uint16_t rnti) - { - auto it = ue_db.find(rnti); - return it != ue_db.end() ? &it->second : nullptr; - } - const sched_nr_ue_sim* find_rnti(uint16_t rnti) const - { - auto it = ue_db.find(rnti); - return it != ue_db.end() ? &it->second : nullptr; - } - bool user_exists(uint16_t rnti) const { return ue_db.count(rnti) > 0; } - const sched_nr_interface::ue_cfg_t* get_user_cfg(uint16_t rnti) const - { - const sched_nr_ue_sim* ret = find_rnti(rnti); - return ret == nullptr ? nullptr : &ret->get_ctxt().ue_cfg; - } - sched_nr* get_sched() { return sched_ptr.get(); } - srsran::const_span get_cell_params() { return cell_params; } - slot_point get_slot_rx() const - { - std::lock_guard lock(mutex); - return current_slot_tx; - } - - sim_nr_enb_ctxt_t get_enb_ctxt() const; + int add_user(uint16_t rnti, const sched_nr_interface::ue_cfg_t& ue_cfg_, slot_point tti_rx, uint32_t preamble_idx); - std::map::iterator begin() { return ue_db.begin(); } - std::map::iterator end() { return ue_db.end(); } + srsran::const_span get_cell_params() { return cell_params; } // configurable by simulator concrete implementation virtual void set_external_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events) {} -private: + // configurable by simulator concrete implementation + virtual void process_slot_result(const sim_nr_enb_ctxt_t& enb_ctxt, srsran::const_span cc_out) {} + +protected: + void generate_cc_result(uint32_t cc); + sim_nr_enb_ctxt_t get_enb_ctxt() const; + int set_default_slot_events(const sim_nr_ue_ctxt_t& ue_ctxt, ue_nr_slot_events& pending_events); int apply_slot_events(sim_nr_ue_ctxt_t& ue_ctxt, const ue_nr_slot_events& events); - std::string test_name; - srslog::basic_logger& logger; - srslog::basic_logger& mac_logger; - std::unique_ptr sched_ptr; - std::vector cell_params; + /// Runs general tests to verify result consistency, and updates UE state + void process_results(); - slot_point current_slot_tx; - int cc_finished = 0; + std::string test_name; + srslog::basic_logger& logger; + srslog::basic_logger& mac_logger; + std::unique_ptr sched_ptr; + std::vector cell_params; + + std::vector > cc_workers; std::map ue_db; - mutable std::mutex mutex; - std::condition_variable cvar; + // slot-specific + slot_point current_slot_tx; + std::chrono::steady_clock::time_point slot_start_tp; + sim_nr_enb_ctxt_t slot_ctxt; + std::vector cc_results; + + std::atomic stopped{false}; + mutable sem_t slot_sem; + std::atomic nof_cc_remaining{0}; }; } // namespace srsenb diff --git a/srsenb/test/mac/nr/sched_nr_test.cc b/srsenb/test/mac/nr/sched_nr_test.cc index 3c5198de5..ce973c12d 100644 --- a/srsenb/test/mac/nr/sched_nr_test.cc +++ b/srsenb/test/mac/nr/sched_nr_test.cc @@ -23,7 +23,6 @@ #include "sched_nr_sim_ue.h" #include "srsran/common/phy_cfg_nr_default.h" #include "srsran/common/test_common.h" -#include "srsran/common/thread_pool.h" #include namespace srsenb { @@ -33,178 +32,92 @@ using dl_sched_t = sched_nr_interface::dl_sched_t; static const srsran::phy_cfg_nr_t default_phy_cfg = srsran::phy_cfg_nr_default_t{srsran::phy_cfg_nr_default_t::reference_cfg_t{}}; -struct task_job_manager { - std::mutex mutex; - int res_count = 0; - int pdsch_count = 0; - srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); - struct slot_guard { - int count = 0; - std::condition_variable cvar; - }; - srsran::bounded_vector slot_counter{}; - - explicit task_job_manager(int max_concurrent_slots = 4) : slot_counter(max_concurrent_slots) {} +class sched_nr_tester : public sched_nr_base_tester +{ +public: + using sched_nr_base_tester::sched_nr_base_tester; - void start_slot(slot_point slot, int nof_sectors) - { - std::unique_lock lock(mutex); - auto& sl = slot_counter[slot.to_uint() % slot_counter.size()]; - while (sl.count > 0) { - sl.cvar.wait(lock); - } - sl.count = nof_sectors; - } - void finish_cc(slot_point slot, - const sched_nr_interface::dl_sched_res_t& dl_res, - const sched_nr_interface::ul_sched_t& ul_res) + void process_slot_result(const sim_nr_enb_ctxt_t& slot_ctxt, srsran::const_span cc_list) override { - std::unique_lock lock(mutex); - TESTASSERT(dl_res.dl_sched.pdcch_dl.size() <= 1); - res_count++; - pdsch_count += dl_res.dl_sched.pdcch_dl.size(); - auto& sl = slot_counter[slot.to_uint() % slot_counter.size()]; - if (--sl.count == 0) { - sl.cvar.notify_one(); - } - } - void wait_task_finish() - { - std::unique_lock lock(mutex); - for (auto& sl : slot_counter) { - while (sl.count > 0) { - sl.cvar.wait(lock); + tot_latency_sched_ns += + std::max_element(cc_list.begin(), cc_list.end(), [](const cc_result_t& lhs, const cc_result_t& rhs) { + return lhs.cc_latency_ns < rhs.cc_latency_ns; + })->cc_latency_ns.count(); + + for (auto& cc_out : cc_list) { + pdsch_count += cc_out.dl_res.dl_sched.pdcch_dl.size(); + cc_res_count++; + + TESTASSERT(cc_out.dl_res.dl_sched.pdcch_dl.size() <= 1); + if (srsran_duplex_nr_is_dl(&cell_params[cc_out.cc].cfg.duplex, 0, current_slot_tx.slot_idx())) { + TESTASSERT(cc_out.dl_res.dl_sched.pdcch_dl.size() == 1 or not cc_out.dl_res.dl_sched.ssb.empty()); } - sl.count = 1; } } + void print_results() const { - test_logger.info("TESTER: %f PDSCH/{slot,cc} were allocated", pdsch_count / (double)res_count); + test_logger.info("TESTER: %f PDSCH/{slot,cc} were allocated", pdsch_count / (double)cc_res_count); srslog::flush(); } + + srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); + + uint64_t tot_latency_sched_ns = 0; + uint32_t cc_res_count = 0; + uint32_t pdsch_count = 0; }; -void sched_nr_cfg_serialized_test() +void run_sched_nr_test(uint32_t nof_workers) { - uint32_t max_nof_ttis = 1000, nof_sectors = 4; - task_job_manager tasks; + srsran_assert(nof_workers > 0, "There must be at least one worker"); + uint32_t max_nof_ttis = 1000, nof_sectors = 4; + uint16_t rnti = 0x4601; - sched_nr_interface::sched_cfg_t cfg; + sched_nr_interface::sched_args_t cfg; cfg.auto_refill_buffer = true; std::vector cells_cfg = get_default_cells_cfg(nof_sectors); - sched_nr_sim_base sched_tester(cfg, cells_cfg, "Serialized Test"); + std::string test_name = "Serialized Test"; + if (nof_workers > 1) { + test_name = fmt::format("Parallel Test with {} workers", nof_workers); + } + sched_nr_tester tester(cfg, cells_cfg, test_name, nof_workers); sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(nof_sectors); - uecfg.fixed_dl_mcs = 15; - uecfg.fixed_ul_mcs = 15; - sched_tester.add_user(0x46, uecfg, slot_point{0, 0}, 0); + tester.add_user(rnti, uecfg, slot_point{0, 0}, 0); - std::vector count_per_cc(nof_sectors, 0); for (uint32_t nof_slots = 0; nof_slots < max_nof_ttis; ++nof_slots) { slot_point slot_rx(0, nof_slots % 10240); slot_point slot_tx = slot_rx + TX_ENB_DELAY; - tasks.start_slot(slot_rx, nof_sectors); - sched_tester.new_slot(slot_tx); - for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { - sched_nr_interface::dl_sched_res_t dl_res; - sched_nr_interface::ul_sched_t ul_res; - auto tp1 = std::chrono::steady_clock::now(); - TESTASSERT(sched_tester.get_sched()->get_dl_sched(slot_tx, cc, dl_res) == SRSRAN_SUCCESS); - TESTASSERT(sched_tester.get_sched()->get_ul_sched(slot_tx, cc, ul_res) == SRSRAN_SUCCESS); - auto tp2 = std::chrono::steady_clock::now(); - count_per_cc[cc] += std::chrono::duration_cast(tp2 - tp1).count(); - sched_nr_cc_output_res_t out{slot_tx, cc, &dl_res, &ul_res}; - sched_tester.update(out); - tasks.finish_cc(slot_rx, dl_res, ul_res); - TESTASSERT(not srsran_duplex_nr_is_dl(&cells_cfg[cc].duplex, 0, (slot_tx).slot_idx()) or - (dl_res.dl_sched.pdcch_dl.size() == 1 or not dl_res.dl_sched.ssb.empty())); - } + tester.run_slot(slot_tx); } - tasks.print_results(); + tester.stop(); + tester.print_results(); // TESTASSERT(tasks.pdsch_count == (int)(max_nof_ttis * nof_sectors * 0.6)); - double final_avg_usec = 0; - for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { - final_avg_usec += count_per_cc[cc]; - } - final_avg_usec = final_avg_usec / 1000.0 / max_nof_ttis; + double final_avg_usec = tester.tot_latency_sched_ns; + final_avg_usec = final_avg_usec / 1000.0 / max_nof_ttis; printf("Total time taken per slot: %f usec\n", final_avg_usec); } -void sched_nr_cfg_parallel_cc_test() -{ - uint32_t nof_sectors = 4; - uint32_t max_nof_ttis = 1000; - task_job_manager tasks; - - sched_nr_interface::sched_cfg_t cfg; - cfg.auto_refill_buffer = true; - - std::vector cells_cfg = get_default_cells_cfg(nof_sectors); - - sched_nr_sim_base sched_tester(cfg, cells_cfg, "Parallel CC Test"); - - sched_nr_interface::ue_cfg_t uecfg = get_default_ue_cfg(cells_cfg.size()); - uecfg.fixed_dl_mcs = 15; - uecfg.fixed_ul_mcs = 15; - sched_tester.add_user(0x46, uecfg, slot_point{0, 0}, 0); - - std::array, SRSRAN_MAX_CARRIERS> nano_count{}; - for (uint32_t nof_slots = 0; nof_slots < max_nof_ttis; ++nof_slots) { - slot_point slot_rx(0, nof_slots % 10240); - slot_point slot_tx = slot_rx + TX_ENB_DELAY; - tasks.start_slot(slot_tx, nof_sectors); - sched_tester.new_slot(slot_tx); - for (uint32_t cc = 0; cc < cells_cfg.size(); ++cc) { - srsran::get_background_workers().push_task([cc, slot_tx, &tasks, &sched_tester, &nano_count]() { - sched_nr_interface::dl_sched_res_t dl_res; - sched_nr_interface::ul_sched_t ul_res; - auto tp1 = std::chrono::steady_clock::now(); - TESTASSERT(sched_tester.get_sched()->get_dl_sched(slot_tx, cc, dl_res) == SRSRAN_SUCCESS); - TESTASSERT(sched_tester.get_sched()->get_ul_sched(slot_tx, cc, ul_res) == SRSRAN_SUCCESS); - auto tp2 = std::chrono::steady_clock::now(); - nano_count[cc].fetch_add(std::chrono::duration_cast(tp2 - tp1).count(), - std::memory_order_relaxed); - sched_nr_cc_output_res_t out{slot_tx, cc, &dl_res, &ul_res}; - sched_tester.update(out); - tasks.finish_cc(slot_tx, dl_res, ul_res); - }); - } - } - - tasks.wait_task_finish(); - - tasks.print_results(); - // TESTASSERT(tasks.pdsch_count == (int)(max_nof_ttis * nof_sectors * 0.6)); - - double final_avg_usec = 0; - for (uint32_t i = 0; i < nof_sectors; ++i) { - final_avg_usec += nano_count[i]; - } - final_avg_usec = final_avg_usec / 1000.0 / max_nof_ttis / nof_sectors; - printf("Total time taken per slot [usec]: %f\n", final_avg_usec); -} - } // namespace srsenb int main() { auto& test_logger = srslog::fetch_basic_logger("TEST"); - test_logger.set_level(srslog::basic_levels::info); - auto& mac_logger = srslog::fetch_basic_logger("MAC"); - mac_logger.set_level(srslog::basic_levels::info); + test_logger.set_level(srslog::basic_levels::warning); + auto& mac_nr_logger = srslog::fetch_basic_logger("MAC-NR"); + mac_nr_logger.set_level(srslog::basic_levels::warning); auto& pool_logger = srslog::fetch_basic_logger("POOL"); - pool_logger.set_level(srslog::basic_levels::info); + pool_logger.set_level(srslog::basic_levels::debug); // Start the log backend. srslog::init(); - srsran::get_background_workers().set_nof_workers(6); - - srsenb::sched_nr_cfg_serialized_test(); - srsenb::sched_nr_cfg_parallel_cc_test(); + srsenb::run_sched_nr_test(1); + srsenb::run_sched_nr_test(2); + srsenb::run_sched_nr_test(4); } diff --git a/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc index 851a8feb5..e73aeb0a4 100644 --- a/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc +++ b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.cc @@ -27,7 +27,7 @@ namespace srsenb { using namespace srsenb::sched_nr_impl; -void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_output_res_t& cc_out) +void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_result_view& cc_out) { slot_point pdcch_slot = cc_out.slot; const pdcch_dl_list_t& pdcchs = cc_out.dl_cc_result->dl_sched.pdcch_dl; diff --git a/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.h b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.h index b864f48db..854778112 100644 --- a/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.h +++ b/srsenb/test/mac/nr/sched_nr_ue_ded_test_suite.h @@ -26,7 +26,7 @@ namespace srsenb { -void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_output_res_t& cc_out); +void test_dl_sched_result(const sim_nr_enb_ctxt_t& enb_ctxt, const sched_nr_cc_result_view& cc_out); } diff --git a/srsenb/test/ngap/ngap_test.cc b/srsenb/test/ngap/ngap_test.cc index d81d96b7c..6efada176 100644 --- a/srsenb/test/ngap/ngap_test.cc +++ b/srsenb/test/ngap/ngap_test.cc @@ -95,6 +95,7 @@ public: { return SRSRAN_SUCCESS; } + int release_bearers(uint16_t rnti) { return SRSRAN_SUCCESS; } int allocate_lcid(uint16_t rnti) { return SRSRAN_SUCCESS; } void write_dl_info(uint16_t rnti, srsran::unique_byte_buffer_t sdu) {} }; diff --git a/srsenb/test/rrc/CMakeLists.txt b/srsenb/test/rrc/CMakeLists.txt index 405e1ad5d..53f008b4d 100644 --- a/srsenb/test/rrc/CMakeLists.txt +++ b/srsenb/test/rrc/CMakeLists.txt @@ -22,8 +22,8 @@ add_library(test_helpers test_helpers.cc) target_link_libraries(test_helpers srsenb_rrc srsenb_common rrc_asn1 rrc_nr_asn1 s1ap_asn1 srsran_common enb_cfg_parser ${LIBCONFIGPP_LIBRARIES}) add_executable(rrc_nr_test rrc_nr_test.cc) -target_link_libraries(rrc_nr_test srsgnb_rrc srsran_common rrc_nr_asn1 ${ATOMIC_LIBS}) -add_test(rrc_nr_test rrc_nr_test) +target_link_libraries(rrc_nr_test srsgnb_rrc test_helpers ${ATOMIC_LIBS}) +add_test(rrc_nr_test rrc_nr_test -i ${CMAKE_CURRENT_SOURCE_DIR}/../..) add_executable(rrc_meascfg_test rrc_meascfg_test.cc) target_link_libraries(rrc_meascfg_test test_helpers ${ATOMIC_LIBS}) diff --git a/srsenb/test/rrc/rrc_nr_test.cc b/srsenb/test/rrc/rrc_nr_test.cc index f01608f81..ed35e82e6 100644 --- a/srsenb/test/rrc/rrc_nr_test.cc +++ b/srsenb/test/rrc/rrc_nr_test.cc @@ -19,9 +19,11 @@ * */ +#include "srsenb/hdr/enb.h" #include "srsenb/hdr/stack/rrc/rrc_nr.h" #include "srsenb/test/common/dummy_classes_common.h" #include "srsenb/test/common/dummy_classes_nr.h" +#include "srsenb/test/rrc/test_helpers.h" #include "srsran/common/test_common.h" #include "srsran/interfaces/gnb_rrc_nr_interfaces.h" #include @@ -84,11 +86,15 @@ int test_rrc_setup() rrc_nr rrc_obj(&task_sched); // set cfg - rrc_nr_cfg_t default_cfg = {}; - rrc_cell_cfg_nr_t cell_cfg = {}; - rrc_nr_cfg_t rrc_cfg_nr = rrc_obj.update_default_cfg(default_cfg); - rrc_cfg_nr.cell_list.push_back(cell_cfg); - + all_args_t args{}; + phy_cfg_t phy_cfg{}; + rrc_nr_cfg_t rrc_cfg_nr = rrc_obj.update_default_cfg(rrc_nr_cfg_t{}); + rrc_cfg_nr.cell_list.emplace_back(); + rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500; + rrc_cfg_nr.cell_list[0].dl_arfcn = 634240; + rrc_cfg_nr.cell_list[0].band = 78; + args.enb.n_prb = 50; + enb_conf_sections::set_derived_args_nr(&args, &rrc_cfg_nr, &phy_cfg); TESTASSERT(rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr, nullptr) == SRSRAN_SUCCESS); @@ -105,8 +111,19 @@ int test_rrc_setup() } // namespace srsenb -int main() +int main(int argc, char** argv) { + auto& logger = srslog::fetch_basic_logger("ASN1"); + logger.set_level(srslog::basic_levels::info); + + srslog::init(); + + if (argc < 3) { + argparse::usage(argv[0]); + return -1; + } + argparse::parse_args(argc, argv); + // FIXME: disabled temporarily until SIB generation is fixed // TESTASSERT(srsenb::test_sib_generation() == SRSRAN_SUCCESS); TESTASSERT(srsenb::test_rrc_setup() == SRSRAN_SUCCESS); diff --git a/srsenb/test/rrc/test_helpers.cc b/srsenb/test/rrc/test_helpers.cc index 53035754c..ba61747fd 100644 --- a/srsenb/test/rrc/test_helpers.cc +++ b/srsenb/test/rrc/test_helpers.cc @@ -55,30 +55,44 @@ int parse_default_cfg_phy(rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, srsenb::all_ar int parse_default_cfg(rrc_cfg_t* rrc_cfg, srsenb::all_args_t& args) { - args = {}; - *rrc_cfg = {}; - args.enb_files.sib_config = argparse::repository_dir + "/sib.conf.example"; - args.enb_files.rr_config = argparse::repository_dir + "/rr.conf.example"; - args.enb_files.rb_config = argparse::repository_dir + "/rb.conf.example"; - srslog::fetch_basic_logger("TEST").debug("sib file path=%s", args.enb_files.sib_config.c_str()); - - args.enb.enb_id = 0x19B; - args.enb.dl_earfcn = 3400; - args.enb.n_prb = 50; - TESTASSERT(srsran::string_to_mcc("001", &args.stack.s1ap.mcc)); - TESTASSERT(srsran::string_to_mnc("01", &args.stack.s1ap.mnc)); - args.enb.transmission_mode = 1; - args.enb.nof_ports = 1; - args.general.eia_pref_list = "EIA2, EIA1, EIA0"; - args.general.eea_pref_list = "EEA0, EEA2, EEA1"; - args.stack.mac.nof_prealloc_ues = 2; - - args.general.rrc_inactivity_timer = 60000; - - phy_cfg_t phy_cfg; + phy_cfg_t phy_cfg; rrc_nr_cfg_t rrc_cfg_nr; + return parse_default_cfg(&args, rrc_cfg, &phy_cfg, &rrc_cfg_nr); +} - return enb_conf_sections::parse_cfg_files(&args, rrc_cfg, &rrc_cfg_nr, &phy_cfg); +int parse_default_cfg(rrc_nr_cfg_t* rrc_nr_cfg) +{ + srsenb::all_args_t args; + phy_cfg_t phy_cfg; + rrc_cfg_t rrc_cfg; + return parse_default_cfg(&args, &rrc_cfg, &phy_cfg, rrc_nr_cfg); +} + +int parse_default_cfg(srsenb::all_args_t* args, rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, rrc_nr_cfg_t* rrc_nr_cfg) +{ + *args = {}; + *rrc_cfg = {}; + *phy_cfg = {}; + + args->enb_files.sib_config = argparse::repository_dir + "/sib.conf.example"; + args->enb_files.rr_config = argparse::repository_dir + "/rr.conf.example"; + args->enb_files.rb_config = argparse::repository_dir + "/rb.conf.example"; + srslog::fetch_basic_logger("TEST").debug("sib file path=%s", args->enb_files.sib_config.c_str()); + + args->enb.enb_id = 0x19B; + args->enb.dl_earfcn = 3400; + args->enb.n_prb = 50; + TESTASSERT(srsran::string_to_mcc("001", &args->stack.s1ap.mcc)); + TESTASSERT(srsran::string_to_mnc("01", &args->stack.s1ap.mnc)); + args->enb.transmission_mode = 1; + args->enb.nof_ports = 1; + args->general.eia_pref_list = "EIA2, EIA1, EIA0"; + args->general.eea_pref_list = "EEA0, EEA2, EEA1"; + args->stack.mac.nof_prealloc_ues = 2; + + args->general.rrc_inactivity_timer = 60000; + + return enb_conf_sections::parse_cfg_files(args, rrc_cfg, rrc_nr_cfg, phy_cfg); } int bring_rrc_to_reconf_state(srsenb::rrc& rrc, srsran::timer_handler& timers, uint16_t rnti) diff --git a/srsenb/test/rrc/test_helpers.h b/srsenb/test/rrc/test_helpers.h index 5dd11becf..aa84fa0f8 100644 --- a/srsenb/test/rrc/test_helpers.h +++ b/srsenb/test/rrc/test_helpers.h @@ -214,7 +214,9 @@ public: namespace test_helpers { +int parse_default_cfg(srsenb::all_args_t* args, rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, rrc_nr_cfg_t* rrc_nr_cfg); int parse_default_cfg(rrc_cfg_t* rrc_cfg, srsenb::all_args_t& args); +int parse_default_cfg(rrc_nr_cfg_t* rrc_nr_cfg); int parse_default_cfg_phy(rrc_cfg_t* rrc_cfg, phy_cfg_t* phy_cfg, srsenb::all_args_t& args); template diff --git a/srsepc/hdr/hss/hss.h b/srsepc/hdr/hss/hss.h index 4e86a98cf..4eeb74bb3 100644 --- a/srsepc/hdr/hss/hss.h +++ b/srsepc/hdr/hss/hss.h @@ -43,15 +43,15 @@ namespace srsepc { -typedef struct { +struct hss_args_t { std::string db_file; uint16_t mcc; uint16_t mnc; -} hss_args_t; +}; enum hss_auth_algo { HSS_ALGO_XOR, HSS_ALGO_MILENAGE }; -typedef struct { +struct hss_ue_ctx_t { // Members std::string name; uint64_t imsi; @@ -70,7 +70,7 @@ typedef struct { void set_sqn(const uint8_t* sqn_); void set_last_rand(const uint8_t* rand_); void get_last_rand(uint8_t* rand_); -} hss_ue_ctx_t; +}; class hss : public hss_interface_nas { diff --git a/srsepc/src/CMakeLists.txt b/srsepc/src/CMakeLists.txt index 2613e44aa..8c05a87f2 100644 --- a/srsepc/src/CMakeLists.txt +++ b/srsepc/src/CMakeLists.txt @@ -43,6 +43,7 @@ target_link_libraries( srsepc srsepc_mme srsran_asn1 srsran_common srslog + support ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${SEC_LIBRARIES} diff --git a/srsepc/src/main.cc b/srsepc/src/main.cc index d112298db..777f9eff0 100644 --- a/srsepc/src/main.cc +++ b/srsepc/src/main.cc @@ -27,9 +27,10 @@ #include "srsran/common/common_helper.h" #include "srsran/common/config_file.h" #include "srsran/common/crash_handler.h" -#include "srsran/common/signal_handler.h" #include "srsran/srslog/srslog.h" #include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" +#include "srsran/support/signal_handler.h" #include #include @@ -64,6 +65,9 @@ typedef struct { log_args_t log_args; } all_args_t; +static srslog::sink* log_sink = nullptr; +static std::atomic running = {true}; + /********************************************************************** * Program arguments processing ***********************************************************************/ @@ -270,18 +274,18 @@ void parse_args(all_args_t* args, int argc, char* argv[]) cout << "Using default mme.integrity_algo: EIA1" << endl; } - args->mme_args.s1ap_args.mme_bind_addr = mme_bind_addr; - args->mme_args.s1ap_args.mme_name = mme_name; - args->mme_args.s1ap_args.dns_addr = dns_addr; + args->mme_args.s1ap_args.mme_bind_addr = mme_bind_addr; + args->mme_args.s1ap_args.mme_name = mme_name; + args->mme_args.s1ap_args.dns_addr = dns_addr; args->mme_args.s1ap_args.full_net_name = full_net_name; args->mme_args.s1ap_args.short_net_name = short_net_name; - args->mme_args.s1ap_args.mme_apn = mme_apn; - args->mme_args.s1ap_args.paging_timer = paging_timer; - args->spgw_args.gtpu_bind_addr = spgw_bind_addr; - args->spgw_args.sgi_if_addr = sgi_if_addr; - args->spgw_args.sgi_if_name = sgi_if_name; - args->spgw_args.max_paging_queue = max_paging_queue; - args->hss_args.db_file = hss_db_file; + args->mme_args.s1ap_args.mme_apn = mme_apn; + args->mme_args.s1ap_args.paging_timer = paging_timer; + args->spgw_args.gtpu_bind_addr = spgw_bind_addr; + args->spgw_args.sgi_if_addr = sgi_if_addr; + args->spgw_args.sgi_if_name = sgi_if_name; + args->spgw_args.max_paging_queue = max_paging_queue; + args->hss_args.db_file = hss_db_file; // Apply all_level to any unset layers if (vm.count("log.all_level")) { @@ -362,9 +366,23 @@ std::string get_build_string() return ss.str(); } +static void emergency_cleanup_handler(void* data) +{ + srslog::flush(); + if (log_sink) { + log_sink->flush(); + } +} + +static void signal_handler() +{ + running = false; +} + int main(int argc, char* argv[]) { - srsran_register_signal_handler(); + srsran_register_signal_handler(signal_handler); + add_emergency_cleanup_handler(emergency_cleanup_handler, nullptr); // print build info cout << endl << get_build_string() << endl; diff --git a/srsepc/src/mme/nas.cc b/srsepc/src/mme/nas.cc index 9d950884f..493532f87 100644 --- a/srsepc/src/mme/nas.cc +++ b/srsepc/src/mme/nas.cc @@ -976,7 +976,7 @@ bool nas::handle_attach_request(srsran::byte_buffer_t* nas_rx) bool nas::handle_authentication_response(srsran::byte_buffer_t* nas_rx) { LIBLTE_MME_AUTHENTICATION_RESPONSE_MSG_STRUCT auth_resp = {}; - bool ue_valid = true; + bool ue_valid = true; // Get NAS authentication response LIBLTE_ERROR_ENUM err = liblte_mme_unpack_authentication_response_msg((LIBLTE_BYTE_MSG_STRUCT*)nas_rx, &auth_resp); @@ -1710,12 +1710,14 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) return false; } + uint32_t estimated_count = (m_sec_ctx.ul_nas_count & 0xffffffe0) | (pdu->msg[1] & 0x1f); + switch (m_sec_ctx.integ_algo) { case srsran::INTEGRITY_ALGORITHM_ID_EIA0: break; case srsran::INTEGRITY_ALGORITHM_ID_128_EIA1: srsran::security_128_eia1(&m_sec_ctx.k_nas_int[16], - m_sec_ctx.ul_nas_count, + estimated_count, 0, srsran::SECURITY_DIRECTION_UPLINK, &pdu->msg[0], @@ -1724,7 +1726,7 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) break; case srsran::INTEGRITY_ALGORITHM_ID_128_EIA2: srsran::security_128_eia2(&m_sec_ctx.k_nas_int[16], - m_sec_ctx.ul_nas_count, + estimated_count, 0, srsran::SECURITY_DIRECTION_UPLINK, &pdu->msg[0], @@ -1733,7 +1735,7 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) break; case srsran::INTEGRITY_ALGORITHM_ID_128_EIA3: srsran::security_128_eia3(&m_sec_ctx.k_nas_int[16], - m_sec_ctx.ul_nas_count, + estimated_count, 0, srsran::SECURITY_DIRECTION_UPLINK, &pdu->msg[0], @@ -1743,12 +1745,13 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) default: break; } + // Check if expected mac equals the sent mac for (i = 0; i < 2; i++) { if (exp_mac[i + 2] != mac[i]) { m_logger.warning("Short integrity check failure. Local: count=%d, [%02x %02x %02x %02x], " "Received: count=%d, [%02x %02x]", - m_sec_ctx.ul_nas_count, + estimated_count, exp_mac[0], exp_mac[1], exp_mac[2], @@ -1759,7 +1762,9 @@ bool nas::short_integrity_check(srsran::byte_buffer_t* pdu) return false; } } + m_logger.info("Integrity check ok. Local: count=%d, Received: count=%d", m_sec_ctx.ul_nas_count, pdu->msg[1] & 0x1F); + m_sec_ctx.ul_nas_count = estimated_count; return true; } diff --git a/srsue/hdr/phy/nr/state.h b/srsue/hdr/phy/nr/state.h index f690dbfb8..ec0b85048 100644 --- a/srsue/hdr/phy/nr/state.h +++ b/srsue/hdr/phy/nr/state.h @@ -353,14 +353,21 @@ public: uci_data.value.sr = sr_count_positive; } - void get_periodic_csi(const uint32_t& tti, srsran_uci_data_nr_t& uci_data) + void get_periodic_csi(const srsran_slot_cfg_t& slot_cfg, srsran_uci_data_nr_t& uci_data) { - int n = srsran_csi_generate_reports(&cfg.csi, tti, csi_measurements.data(), uci_data.cfg.csi, uci_data.value.csi); + // Generate report configurations + int n = srsran_csi_reports_generate(&cfg.csi, &slot_cfg, uci_data.cfg.csi); if (n > SRSRAN_SUCCESS) { uci_data.cfg.nof_csi = n; } - uci_data.cfg.pucch.rnti = stack->get_ul_sched_rnti_nr(tti).id; + // Quantify reports from measurements + n = srsran_csi_reports_quantify(uci_data.cfg.csi, csi_measurements.data(), uci_data.value.csi); + if (n > SRSRAN_SUCCESS) { + uci_data.cfg.nof_csi = n; + } + + uci_data.cfg.pucch.rnti = stack->get_ul_sched_rnti_nr(slot_cfg.idx).id; } /** diff --git a/srsue/hdr/phy/nr/worker_pool.h b/srsue/hdr/phy/nr/worker_pool.h index 0b0487593..2fdeebc81 100644 --- a/srsue/hdr/phy/nr/worker_pool.h +++ b/srsue/hdr/phy/nr/worker_pool.h @@ -36,7 +36,12 @@ private: srsran::thread_pool pool; std::vector > workers; state phy_state; - std::unique_ptr prach_buffer = nullptr; + std::unique_ptr prach_buffer = nullptr; + uint32_t prach_nof_sf = 0; + uint32_t prach_sf_count = 0; + cf_t* prach_ptr = nullptr; + float prach_target_power = 0.0f; + uint32_t sf_sz = 0; public: sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } diff --git a/srsue/hdr/phy/phy_metrics.h b/srsue/hdr/phy/phy_metrics.h index 801615105..b14b47b7d 100644 --- a/srsue/hdr/phy/phy_metrics.h +++ b/srsue/hdr/phy/phy_metrics.h @@ -88,7 +88,10 @@ struct ch_metrics_t { { count++; PHY_METRICS_SET(n); - PHY_METRICS_SET(sinr); + // We exclude inf and nan from the average SINR + if (!std::isnan(other.sinr) || !std::isinf(other.sinr)) { + PHY_METRICS_SET(sinr); + } PHY_METRICS_SET(rsrp); PHY_METRICS_SET(rsrq); PHY_METRICS_SET(rssi); diff --git a/srsue/hdr/phy/prach.h b/srsue/hdr/phy/prach.h index 5aa1776a9..8ba709774 100644 --- a/srsue/hdr/phy/prach.h +++ b/srsue/hdr/phy/prach.h @@ -50,37 +50,25 @@ public: private: bool generate_buffer(uint32_t f_idx); - bool is_buffer_generated(uint32_t f_idx, uint32_t preamble_index) const - { - return buffer_bitmask.test(f_idx * 64 + preamble_index); - } - - void set_buffer_as_generated(uint32_t f_idx, uint32_t preamble_index) - { - buffer_bitmask.set(f_idx * 64 + preamble_index); - } - private: static constexpr unsigned MAX_LEN_SF = 3; static constexpr unsigned max_fs = 12; static constexpr unsigned max_preambles = 64; - srslog::basic_logger& logger; - srsran_prach_t prach_obj = {}; - srsran_cell_t cell = {}; - srsran_cfo_t cfo_h = {}; - srsran_prach_cfg_t cfg = {}; - std::array, max_fs> buffer = {}; - cf_t* signal_buffer = nullptr; - int preamble_idx = -1; - uint32_t len = 0; - int allowed_subframe = 0; - int transmitted_tti = 0; - float target_power_dbm = 0; - bool mem_initiated = false; - bool cell_initiated = false; - std::bitset buffer_bitmask; - mutable std::mutex mutex; + srslog::basic_logger& logger; + srsran_prach_t prach_obj = {}; + srsran_cell_t cell = {}; + srsran_cfo_t cfo_h = {}; + srsran_prach_cfg_t cfg = {}; + cf_t* signal_buffer = nullptr; + int preamble_idx = -1; + uint32_t len = 0; + int allowed_subframe = 0; + int transmitted_tti = 0; + float target_power_dbm = 0; + bool mem_initiated = false; + bool cell_initiated = false; + mutable std::mutex mutex; }; } // namespace srsue diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index bd56180dd..5a48be22d 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -268,7 +268,7 @@ private: search::ret_code cell_search_ret = search::CELL_NOT_FOUND; - // Sampling rate mode (find is 1.96 MHz, camp is the full cell BW) + // Sampling rate mode (find is 1.92 MHz, camp is the full cell BW) class srate_safe { public: @@ -288,15 +288,11 @@ private: std::lock_guard lock(mutex); return std::isnormal(current_srate) and current_srate > 0.0f; } - bool set_camp(float new_srate) + void set_camp(float new_srate) { std::lock_guard lock(mutex); - if (current_srate != new_srate || srate_mode != SRATE_CAMP) { - current_srate = new_srate; - srate_mode = SRATE_CAMP; - return true; - } - return false; + current_srate = new_srate; + srate_mode = SRATE_CAMP; } bool set_find() { diff --git a/srsue/hdr/stack/mac/ul_harq.h b/srsue/hdr/stack/mac/ul_harq.h index 48d8289b6..20eca9655 100644 --- a/srsue/hdr/stack/mac/ul_harq.h +++ b/srsue/hdr/stack/mac/ul_harq.h @@ -75,7 +75,56 @@ private: void new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant, mac_interface_phy_lte::tb_action_ul_t* action); private: - mac_interface_phy_lte::mac_grant_ul_t cur_grant; + /// Thread safe wrapper for a mac_grant_ul_t object. + class lockable_grant + { + mac_interface_phy_lte::mac_grant_ul_t grant = {}; + mutable std::mutex mutex; + + public: + void set(const mac_interface_phy_lte::mac_grant_ul_t& other) + { + std::lock_guard lock(mutex); + grant = other; + } + + void reset() + { + std::lock_guard lock(mutex); + grant = {}; + } + + void set_ndi(bool ndi) + { + std::lock_guard lock(mutex); + grant.tb.ndi = ndi; + } + + bool get_ndi() const + { + std::lock_guard lock(mutex); + return grant.tb.ndi; + } + + uint32_t get_tbs() const + { + std::lock_guard lock(mutex); + return grant.tb.tbs; + } + + int get_rv() const + { + std::lock_guard lock(mutex); + return grant.tb.rv; + } + + void set_rv(int rv) + { + std::lock_guard lock(mutex); + grant.tb.rv = rv; + } + }; + lockable_grant cur_grant; uint32_t pid; uint32_t current_tx_nb; @@ -108,6 +157,7 @@ private: ue_rnti* rntis = nullptr; srsran::ul_harq_cfg_t harq_cfg = {}; + std::mutex config_mutex; std::atomic average_retx{0}; std::atomic nof_pkts{0}; diff --git a/srsue/hdr/stack/mac_nr/dl_harq_nr.h b/srsue/hdr/stack/mac_nr/dl_harq_nr.h index ecbbf48d2..fc9c112dd 100644 --- a/srsue/hdr/stack/mac_nr/dl_harq_nr.h +++ b/srsue/hdr/stack/mac_nr/dl_harq_nr.h @@ -51,7 +51,7 @@ public: ~dl_harq_entity_nr(); int32_t set_config(const srsran::dl_harq_cfg_nr_t& cfg_); - void reset(); + void reset(); /// PHY->MAC interface for DL processes void new_grant_dl(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_t* action); @@ -78,7 +78,7 @@ private: uint8_t get_ndi(); void - new_grant_dl(const mac_nr_grant_dl_t& grant, const bool& ndi_toggled, mac_interface_phy_nr::tb_action_dl_t* action); + new_grant_dl(const mac_nr_grant_dl_t& grant, const bool& ndi_toggled, mac_interface_phy_nr::tb_action_dl_t* action); void tb_decoded(const mac_nr_grant_dl_t& grant, mac_interface_phy_nr::tb_action_dl_result_t result); private: @@ -105,7 +105,8 @@ private: srslog::basic_logger& logger; uint16_t last_temporal_crnti = SRSRAN_INVALID_RNTI; dl_harq_metrics_t metrics = {}; - uint8_t cc_idx = 0; + std::mutex metrics_mutex; + uint8_t cc_idx = 0; pthread_rwlock_t rwlock; }; diff --git a/srsue/hdr/stack/mac_nr/mac_nr.h b/srsue/hdr/stack/mac_nr/mac_nr.h index f9898f068..f4ebee4e6 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr.h +++ b/srsue/hdr/stack/mac_nr/mac_nr.h @@ -57,7 +57,6 @@ public: int init(const mac_nr_args_t& args_, phy_interface_mac_nr* phy_, rlc_interface_mac* rlc_, rrc_interface_mac* rrc_); void stop(); - void reset(); void run_tti(const uint32_t tti); void start_pcap(srsran::mac_pcap* pcap_); @@ -84,6 +83,7 @@ public: void get_metrics(mac_metrics_t* metrics); /// Interface for RRC (RRC -> MAC) + void reset(); int setup_lcid(const srsran::logical_channel_config_t& config); int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg); int set_config(const srsran::sr_cfg_nr_t& sr_cfg); @@ -116,7 +116,8 @@ public: bool msg3_is_empty() { return mux.msg3_is_empty(); } /// RRC - void rrc_ra_problem() { rrc->ra_problem(); } + void rrc_ra_problem(); + void rrc_ra_completed(); /// stack interface void process_pdus(); diff --git a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h index 9d94fea7a..105dc06f6 100644 --- a/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h +++ b/srsue/hdr/stack/mac_nr/mac_nr_interfaces.h @@ -33,9 +33,9 @@ class mac_interface_proc_ra_nr { public: // Functions for identity handling, e.g., contention id and c-rnti - virtual uint64_t get_contention_id() = 0; - virtual uint16_t get_crnti() = 0; - virtual bool set_crnti(uint16_t c_rnti) = 0; + virtual uint64_t get_contention_id() = 0; + virtual uint16_t get_crnti() = 0; + virtual bool set_crnti(uint16_t c_rnti) = 0; // Functions for msg3 manipulation which shall be transparent to the procedure virtual bool msg3_is_transmitted() = 0; @@ -44,7 +44,8 @@ public: virtual bool msg3_is_empty() = 0; // RRC functions - virtual void rrc_ra_problem() = 0; + virtual void rrc_ra_problem() = 0; + virtual void rrc_ra_completed() = 0; }; /** diff --git a/srsue/hdr/stack/mac_nr/mux_nr.h b/srsue/hdr/stack/mac_nr/mux_nr.h index 41142a917..f4ec97597 100644 --- a/srsue/hdr/stack/mac_nr/mux_nr.h +++ b/srsue/hdr/stack/mac_nr/mux_nr.h @@ -39,7 +39,7 @@ class mux_nr final : mux_base, public mux_interface_bsr_nr public: explicit mux_nr(mac_interface_mux_nr& mac_, srslog::basic_logger& logger); ~mux_nr(){}; - void reset(); + void reset(); int32_t init(rlc_interface_mac* rlc_); void msg3_flush(); @@ -62,9 +62,9 @@ private: // internal helper methods // ctor configured members - mac_interface_mux_nr& mac; - rlc_interface_mac* rlc = nullptr; - srslog::basic_logger& logger; + mac_interface_mux_nr& mac; + rlc_interface_mac* rlc = nullptr; + srslog::basic_logger& logger; // Msg3 related srsran::unique_byte_buffer_t msg3_buff = nullptr; @@ -78,7 +78,8 @@ private: srsran::mac_sch_pdu_nr tx_pdu; /// single MAC PDU for packing - enum { no_bsr, sbsr_ce, lbsr_ce } add_bsr_ce = no_bsr; /// BSR procedure requests MUX to add a BSR CE + enum bsr_req_t { no_bsr, sbsr_ce, lbsr_ce }; + std::atomic add_bsr_ce = {bsr_req_t::no_bsr}; /// BSR procedure requests MUX to add a BSR CE // Mutex for exclusive access std::mutex mutex; diff --git a/srsue/hdr/stack/mac_nr/proc_ra_nr.h b/srsue/hdr/stack/mac_nr/proc_ra_nr.h index e2ac7fd1a..fbc4f9f50 100644 --- a/srsue/hdr/stack/mac_nr/proc_ra_nr.h +++ b/srsue/hdr/stack/mac_nr/proc_ra_nr.h @@ -44,8 +44,8 @@ public: void set_config(const srsran::rach_nr_cfg_t& rach_cfg); bool is_contention_resolution(); - bool is_rar_opportunity(uint32_t tti); - bool has_rar_rnti(); + bool is_rar_opportunity(uint32_t tti); + bool has_rar_rnti(); uint16_t get_rar_rnti(); bool has_temp_crnti(); uint16_t get_temp_crnti(); @@ -70,9 +70,9 @@ private: srsran::ext_task_sched_handle* task_sched = nullptr; srsran::task_multiqueue::queue_handle task_queue; - int ra_window_length = -1, ra_window_start = -1; - uint16_t rar_rnti = SRSRAN_INVALID_RNTI; - uint16_t temp_crnti = SRSRAN_INVALID_RNTI; + int ra_window_length = -1, ra_window_start = -1; + uint16_t rar_rnti = SRSRAN_INVALID_RNTI; + uint16_t temp_crnti = SRSRAN_INVALID_RNTI; std::mutex mutex; srsran::rach_nr_cfg_t rach_cfg = {}; @@ -99,18 +99,18 @@ private: srsran::timer_handler::unique_timer backoff_timer; // 38.321 5.1.1 Variables - uint32_t preamble_index = 0; - uint32_t preamble_transmission_counter = 0; + uint32_t preamble_index = 0; + uint32_t preamble_transmission_counter = 0; uint32_t preamble_backoff = 0; // in ms - uint32_t preamble_power_ramping_step = 0; - int preamble_received_target_power = 0; - uint32_t scaling_factor_bi = 0; + uint32_t preamble_power_ramping_step = 0; + int preamble_received_target_power = 0; + uint32_t scaling_factor_bi = 0; // uint32_t temporary_c_rnti; uint32_t power_offset_2step_ra = 0; // not explicty mentioned uint32_t preambleTransMax = 0; - uint32_t prach_occasion = 0; + uint32_t prach_occasion = 0; uint32_t current_ta = 0; void timer_expired(uint32_t timer_id); @@ -121,7 +121,7 @@ private: void ra_response_reception(const mac_interface_phy_nr::tb_action_dl_result_t& tb); void ra_contention_resolution(); void ra_contention_resolution(uint64_t rx_contention_id); - void ra_completion(); + void ra_completion(); void ra_error(); }; } // namespace srsue diff --git a/srsue/hdr/stack/mac_nr/ul_harq_nr.h b/srsue/hdr/stack/mac_nr/ul_harq_nr.h index 58631029a..0bbff4be5 100644 --- a/srsue/hdr/stack/mac_nr/ul_harq_nr.h +++ b/srsue/hdr/stack/mac_nr/ul_harq_nr.h @@ -63,9 +63,9 @@ private: ul_harq_process_nr(); ~ul_harq_process_nr(); - bool init(uint32_t pid_, ul_harq_entity_nr* entity_); - void reset(); - void reset_ndi(); + bool init(uint32_t pid_, ul_harq_entity_nr* entity_); + void reset(); + void reset_ndi(); uint8_t get_ndi(); bool has_grant(); @@ -84,7 +84,7 @@ private: mac_interface_phy_nr::tb_action_ul_t* action); private: - mac_interface_phy_nr::mac_nr_grant_ul_t current_grant = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t current_grant = {}; bool grant_configured = false; uint32_t pid = 0; @@ -112,6 +112,7 @@ private: srsran::ul_harq_cfg_t harq_cfg = {}; ul_harq_metrics_t metrics = {}; + std::mutex metrics_mutex; }; typedef std::unique_ptr ul_harq_entity_nr_ptr; diff --git a/srsue/hdr/stack/rrc/nr/rrc_nr_config.h b/srsue/hdr/stack/rrc/nr/rrc_nr_config.h new file mode 100644 index 000000000..ae3b37b70 --- /dev/null +++ b/srsue/hdr/stack/rrc/nr/rrc_nr_config.h @@ -0,0 +1,40 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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. + * + */ + +#ifndef SRSRAN_RRC_NR_CONFIG_H +#define SRSRAN_RRC_NR_CONFIG_H + +#include +#include + +namespace srsue { + +// Expert arguments to create GW without proper RRC +struct core_less_args_t { + std::string ip_addr; + uint8_t drb_lcid; +}; + +struct rrc_nr_args_t { + core_less_args_t coreless; + uint32_t sim_nr_meas_pci; + bool pdcp_short_sn_support; + std::string supported_bands_nr_str; + std::vector supported_bands_nr; + std::vector supported_bands_eutra; + std::string log_level; + uint32_t log_hex_limit; +}; + +} // namespace srsue + +#endif // SRSRAN_RRC_NR_CONFIG_H diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index 330482cea..efe2c7057 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -23,6 +23,7 @@ #define SRSUE_RRC_H #include "rrc_cell.h" +#include "rrc_config.h" #include "rrc_metrics.h" #include "rrc_rlf_report.h" #include "srsran/asn1/rrc_utils.h" @@ -45,29 +46,6 @@ using srsran::byte_buffer_t; namespace srsue { -#define SRSRAN_RRC_N_BANDS 43 -typedef struct { - std::string ue_category_str; - uint32_t ue_category; - int ue_category_ul; - int ue_category_dl; - uint32_t release; - uint32_t feature_group; - std::array supported_bands; - std::vector supported_bands_nr; - uint32_t nof_supported_bands; - bool support_ca; - int mbms_service_id; - uint32_t mbms_service_port; -} rrc_args_t; - -#define SRSRAN_UE_CATEGORY_DEFAULT "4" -#define SRSRAN_UE_CATEGORY_MIN 1 -#define SRSRAN_UE_CATEGORY_MAX 21 -#define SRSRAN_RELEASE_MIN 8 -#define SRSRAN_RELEASE_MAX 15 -#define SRSRAN_RELEASE_DEFAULT (SRSRAN_RELEASE_MIN) - class phy_controller; class usim_interface_rrc; class gw_interface_rrc; diff --git a/srsue/hdr/stack/rrc/rrc_config.h b/srsue/hdr/stack/rrc/rrc_config.h new file mode 100644 index 000000000..1f66c3eb7 --- /dev/null +++ b/srsue/hdr/stack/rrc/rrc_config.h @@ -0,0 +1,44 @@ +/** + * + * \section COPYRIGHT + * + * Copyright 2013-2021 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. + * + */ + +#ifndef SRSRAN_RRC_CONFIG_H +#define SRSRAN_RRC_CONFIG_H + +namespace srsue { + +#define SRSRAN_RRC_N_BANDS 43 + +struct rrc_args_t { + std::string ue_category_str; + uint32_t ue_category; + int ue_category_ul; + int ue_category_dl; + uint32_t release; + uint32_t feature_group; + std::array supported_bands; + std::vector supported_bands_nr; + uint32_t nof_supported_bands; + bool support_ca; + int mbms_service_id; + uint32_t mbms_service_port; +}; + +#define SRSRAN_UE_CATEGORY_DEFAULT "4" +#define SRSRAN_UE_CATEGORY_MIN 1 +#define SRSRAN_UE_CATEGORY_MAX 21 +#define SRSRAN_RELEASE_MIN 8 +#define SRSRAN_RELEASE_MAX 15 +#define SRSRAN_RELEASE_DEFAULT (SRSRAN_RELEASE_MIN) + +} // namespace srsue + +#endif // SRSRAN_RRC_CONFIG_H diff --git a/srsue/hdr/stack/rrc/rrc_nr.h b/srsue/hdr/stack/rrc/rrc_nr.h index a34835719..781210b2f 100644 --- a/srsue/hdr/stack/rrc/rrc_nr.h +++ b/srsue/hdr/stack/rrc/rrc_nr.h @@ -22,6 +22,7 @@ #ifndef SRSUE_RRC_NR_H #define SRSUE_RRC_NR_H +#include "nr/rrc_nr_config.h" #include "srsran/adt/circular_map.h" #include "srsran/asn1/rrc_nr.h" #include "srsran/asn1/rrc_nr_utils.h" @@ -41,24 +42,6 @@ class usim_interface_rrc_nr; class pdcp_interface_rrc; class rlc_interface_rrc; -// Expert arguments to create GW without proper RRC -struct core_less_args_t { - std::string ip_addr; - srsue::gw_args_t gw_args; - uint8_t drb_lcid; -}; - -struct rrc_nr_args_t { - core_less_args_t coreless; - uint32_t sim_nr_meas_pci; - bool pdcp_short_sn_support; - std::string supported_bands_nr_str; - std::vector supported_bands_nr; - std::vector supported_bands_eutra; - std::string log_level; - uint32_t log_hex_limit; -}; - struct rrc_nr_metrics_t {}; class rrc_nr final : public rrc_interface_phy_nr, @@ -139,6 +122,7 @@ public: uint32_t sk_counter_r15, bool nr_radio_bearer_cfg1_r15_present, asn1::dyn_octstring nr_radio_bearer_cfg1_r15); + void rrc_release(); bool configure_sk_counter(uint16_t sk_counter); bool is_config_pending(); // STACK interface diff --git a/srsue/hdr/stack/ue_stack_base.h b/srsue/hdr/stack/ue_stack_base.h index 07a53b0a7..8bac5695d 100644 --- a/srsue/hdr/stack/ue_stack_base.h +++ b/srsue/hdr/stack/ue_stack_base.h @@ -22,11 +22,10 @@ #ifndef SRSUE_UE_STACK_BASE_H #define SRSUE_UE_STACK_BASE_H +#include "rrc/nr/rrc_nr_config.h" +#include "rrc/rrc_config.h" #include "srsue/hdr/stack/upper/nas_config.h" #include "srsue/hdr/ue_metrics_interface.h" - -#include "rrc/rrc.h" -#include "rrc/rrc_nr.h" #include "upper/gw.h" #include "upper/usim.h" diff --git a/srsue/hdr/stack/ue_stack_lte.h b/srsue/hdr/stack/ue_stack_lte.h index d83fd079d..0bfff3774 100644 --- a/srsue/hdr/stack/ue_stack_lte.h +++ b/srsue/hdr/stack/ue_stack_lte.h @@ -26,20 +26,10 @@ #ifndef SRSUE_UE_STACK_LTE_H #define SRSUE_UE_STACK_LTE_H -#include -#include -#include -#include - #include "mac/mac.h" #include "mac_nr/mac_nr.h" #include "rrc/rrc.h" -#include "srsran/radio/radio.h" -#include "srsran/rlc/rlc.h" -#include "srsran/upper/pdcp.h" -#include "upper/nas.h" -#include "upper/usim.h" - +#include "rrc/rrc_nr.h" #include "srsran/common/bearer_manager.h" #include "srsran/common/buffer_pool.h" #include "srsran/common/multiqueue.h" @@ -48,8 +38,17 @@ #include "srsran/common/thread_pool.h" #include "srsran/common/time_prof.h" #include "srsran/interfaces/ue_interfaces.h" +#include "srsran/radio/radio.h" +#include "srsran/rlc/rlc.h" +#include "srsran/upper/pdcp.h" #include "srsue/hdr/ue_metrics_interface.h" #include "ue_stack_base.h" +#include "upper/nas.h" +#include "upper/usim.h" +#include +#include +#include +#include namespace srsue { diff --git a/srsue/hdr/stack/ue_stack_nr.h b/srsue/hdr/stack/ue_stack_nr.h index dc8fe2731..ccd3fd6c1 100644 --- a/srsue/hdr/stack/ue_stack_nr.h +++ b/srsue/hdr/stack/ue_stack_nr.h @@ -28,7 +28,6 @@ #include #include "mac_nr/mac_nr.h" -#include "rrc/rrc_nr.h" #include "srsran/radio/radio.h" #include "srsran/rlc/rlc.h" #include "srsran/upper/pdcp.h" @@ -39,6 +38,7 @@ #include "srsran/common/mac_pcap.h" #include "srsran/common/multiqueue.h" #include "srsran/common/thread_pool.h" +#include "srsran/interfaces/ue_interfaces.h" #include "srsran/interfaces/ue_nr_interfaces.h" #include "srsue/hdr/ue_metrics_interface.h" @@ -46,6 +46,8 @@ namespace srsue { +class rrc_nr; + /** \brief L2/L3 stack class for 5G/NR UEs. * * This class wraps all L2/L3 blocks and provides a single interface towards the PHY. diff --git a/srsue/hdr/stack/upper/nas.h b/srsue/hdr/stack/upper/nas.h index d833171a9..fb51f7504 100644 --- a/srsue/hdr/stack/upper/nas.h +++ b/srsue/hdr/stack/upper/nas.h @@ -231,8 +231,8 @@ private: void enter_emm_deregistered_initiated(); // security context persistence file - bool read_ctxt_file(nas_sec_ctxt* ctxt); - bool write_ctxt_file(nas_sec_ctxt ctxt_); + bool read_ctxt_file(nas_sec_ctxt* ctxt_, nas_sec_base_ctxt* ctxt_base_); + bool write_ctxt_file(nas_sec_ctxt ctxt_, nas_sec_base_ctxt ctxt_base_); // ctxt file helpers std::string hex_to_string(uint8_t* hex, int size); diff --git a/srsue/hdr/stack/upper/nas_5g.h b/srsue/hdr/stack/upper/nas_5g.h index 0d394d993..a721da520 100644 --- a/srsue/hdr/stack/upper/nas_5g.h +++ b/srsue/hdr/stack/upper/nas_5g.h @@ -58,7 +58,10 @@ class nas_5g : public nas_base, public nas_5g_interface_rrc_nr, public nas_5g_in public: explicit nas_5g(srslog::basic_logger& logger_, srsran::task_sched_handle task_sched_); virtual ~nas_5g(); - int init(usim_interface_nas* usim_, rrc_nr_interface_nas_5g* rrc_nr_, gw_interface_nas* gw_, const nas_args_t& cfg_); + int init(usim_interface_nas* usim_, + rrc_nr_interface_nas_5g* rrc_nr_, + gw_interface_nas* gw_, + const nas_5g_args_t& cfg_); void stop(); void run_tti(); @@ -77,6 +80,9 @@ public: int disable_data(); int start_service_request(); + // Metrics getter + void get_metrics(nas_5g_metrics_t& metrics); + private: rrc_nr_interface_nas_5g* rrc_nr = nullptr; usim_interface_nas* usim = nullptr; @@ -89,8 +95,8 @@ private: srsran::nas_5g::nas_5gs_msg initial_registration_request_stored; - nas_args_t cfg = {}; - mm5g_state_t state; + nas_5g_args_t cfg = {}; + mm5g_state_t state; // Security bool ia5g_caps[8] = {}; @@ -135,20 +141,26 @@ private: int send_authentication_response(const uint8_t res[16]); int send_security_mode_reject(const srsran::nas_5g::cause_5gmm_t::cause_5gmm_type_::options cause); int send_authentication_failure(const srsran::nas_5g::cause_5gmm_t::cause_5gmm_type_::options cause, - const uint8_t* auth_fail_param); + const uint8_t res_star[16]); int send_security_mode_complete(const srsran::nas_5g::security_mode_command_t& security_mode_command); int send_registration_complete(); int send_pdu_session_establishment_request(uint32_t transaction_identity, uint16_t pdu_session_id, const pdu_session_cfg_t& pdu_session); + int send_deregistration_request_ue_originating(bool switch_off); + int send_identity_response(srsran::nas_5g::identity_type_5gs_t::identity_types_::options requested_identity_type); + int send_configuration_update_complete(); + // Helper functions void fill_security_caps(srsran::nas_5g::ue_security_capability_t& sec_caps); int apply_security_config(srsran::unique_byte_buffer_t& pdu, uint8_t sec_hdr_type); + bool check_replayed_ue_security_capabilities(srsran::nas_5g::ue_security_capability_t& caps); // message handler int handle_registration_accept(srsran::nas_5g::registration_accept_t& registration_accept); int handle_registration_reject(srsran::nas_5g::registration_reject_t& registration_reject); int handle_authentication_request(srsran::nas_5g::authentication_request_t& authentication_request); + int handle_authentication_reject(srsran::nas_5g::authentication_reject_t& authentication_reject); int handle_identity_request(srsran::nas_5g::identity_request_t& identity_request); int handle_service_accept(srsran::nas_5g::service_accept_t& service_accept); int handle_service_reject(srsran::nas_5g::service_reject_t& service_reject); @@ -156,6 +168,8 @@ private: srsran::unique_byte_buffer_t pdu); int handle_deregistration_accept_ue_terminated( srsran::nas_5g::deregistration_accept_ue_terminated_t& deregistration_accept_ue_terminated); + int handle_deregistration_accept_ue_originating( + srsran::nas_5g::deregistration_accept_ue_originating_t& deregistration_accept_ue_originating); int handle_deregistration_request_ue_terminated( srsran::nas_5g::deregistration_request_ue_terminated_t& deregistration_request_ue_terminated); int handle_configuration_update_command(srsran::nas_5g::configuration_update_command_t& configuration_update_command); @@ -177,6 +191,8 @@ private: int configure_pdu_session(uint16_t pdu_session_id); bool unestablished_pdu_sessions(); int get_unestablished_pdu_session(uint16_t& pdu_session_id, pdu_session_cfg_t& pdu_session_cfg); + int reset_pdu_sessions(); + uint32_t num_of_est_pdu_sessions(); struct pdu_session_t { bool configured; diff --git a/srsue/hdr/stack/upper/nas_5g_metrics.h b/srsue/hdr/stack/upper/nas_5g_metrics.h index 89c61654c..73f29cc9f 100644 --- a/srsue/hdr/stack/upper/nas_5g_metrics.h +++ b/srsue/hdr/stack/upper/nas_5g_metrics.h @@ -27,7 +27,7 @@ namespace srsue { struct nas_5g_metrics_t { - uint32_t nof_active_5g_bearers; + uint32_t nof_active_pdu_sessions; mm5g_state_t::state_t state; }; diff --git a/srsue/hdr/stack/upper/nas_base.h b/srsue/hdr/stack/upper/nas_base.h index d95b9e997..a59192545 100644 --- a/srsue/hdr/stack/upper/nas_base.h +++ b/srsue/hdr/stack/upper/nas_base.h @@ -47,28 +47,33 @@ protected: srsran::nas_pcap* pcap = nullptr; // Security context + // Base context applies for LTE and 5G + struct nas_sec_base_ctxt { + uint8_t k_nas_enc[32] = {}; + uint8_t k_nas_int[32] = {}; + srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; + srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; + uint32_t tx_count; + uint32_t rx_count; + }; + + // Only applies for LTE struct nas_sec_ctxt { uint8_t ksi; uint8_t k_asme[32]; - uint32_t tx_count; - uint32_t rx_count; uint32_t k_enb_count; - srsran::CIPHERING_ALGORITHM_ID_ENUM cipher_algo; - srsran::INTEGRITY_ALGORITHM_ID_ENUM integ_algo; LIBLTE_MME_EPS_MOBILE_ID_GUTI_STRUCT guti; }; + // Only applies for 5G struct nas_5g_sec_ctxt { uint8_t ksi; uint8_t k_amf[32]; - uint32_t tx_count; - uint32_t rx_count; }; - nas_sec_ctxt ctxt = {}; - nas_5g_sec_ctxt ctxt_5g = {}; - uint8_t k_nas_enc[32] = {}; - uint8_t k_nas_int[32] = {}; + nas_sec_base_ctxt ctxt_base = {}; + nas_sec_ctxt ctxt = {}; + nas_5g_sec_ctxt ctxt_5g = {}; int parse_security_algorithm_list(std::string algorithm_string, bool* algorithm_caps); diff --git a/srsue/hdr/stack/upper/nas_config.h b/srsue/hdr/stack/upper/nas_config.h index 90d196142..8400e2150 100644 --- a/srsue/hdr/stack/upper/nas_config.h +++ b/srsue/hdr/stack/upper/nas_config.h @@ -27,10 +27,10 @@ namespace srsue { -typedef struct { +struct nas_sim_args_t { int airplane_t_on_ms = -1; int airplane_t_off_ms = -1; -} nas_sim_args_t; +}; class nas_args_t { @@ -52,5 +52,22 @@ public: std::vector pdu_session_cfgs; }; +class nas_5g_args_t +{ +public: + nas_5g_args_t() : force_imsi_attach(false) {} + ~nas_5g_args_t() = default; + bool force_imsi_attach; + + // Need EPS sec capabilities in 5G + std::string eia; + std::string eea; + + // 5G Security capabilities + std::string ia5g; + std::string ea5g; + std::vector pdu_session_cfgs; +}; + } // namespace srsue #endif // SRSUE_NAS_COMMON_H diff --git a/srsue/src/CMakeLists.txt b/srsue/src/CMakeLists.txt index 01000fecc..e856c0270 100644 --- a/srsue/src/CMakeLists.txt +++ b/srsue/src/CMakeLists.txt @@ -34,7 +34,7 @@ endif (RPATH) add_executable(srsue main.cc ue.cc metrics_stdout.cc metrics_csv.cc metrics_json.cc) set(SRSUE_SOURCES srsue_phy srsue_stack srsue_upper srsue_mac srsue_rrc srslog system) -set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_radio srsran_gtpu srsran_rlc srsran_pdcp rrc_asn1 srslog system) +set(SRSRAN_SOURCES srsran_common srsran_mac srsran_phy srsran_radio srsran_gtpu srsran_rlc srsran_pdcp rrc_asn1 srslog support system) set(SRSUE_SOURCES ${SRSUE_SOURCES} srsue_nr_stack srsue_rrc_nr srsue_mac_nr) set(SRSRAN_SOURCES ${SRSRAN_SOURCES} rrc_nr_asn1 ngap_nr_asn1) diff --git a/srsue/src/main.cc b/srsue/src/main.cc index 3dda9e26d..b614da75e 100644 --- a/srsue/src/main.cc +++ b/srsue/src/main.cc @@ -23,11 +23,13 @@ #include "srsran/common/config_file.h" #include "srsran/common/crash_handler.h" #include "srsran/common/metrics_hub.h" -#include "srsran/common/signal_handler.h" +#include "srsran/common/multiqueue.h" #include "srsran/common/tsan_options.h" #include "srsran/srslog/event_trace.h" #include "srsran/srslog/srslog.h" #include "srsran/srsran.h" +#include "srsran/support/emergency_handlers.h" +#include "srsran/support/signal_handler.h" #include "srsran/version.h" #include "srsue/hdr/metrics_csv.h" #include "srsue/hdr/metrics_json.h" @@ -35,6 +37,7 @@ #include "srsue/hdr/ue.h" #include #include +#include #include #include #include @@ -53,8 +56,10 @@ namespace bpo = boost::program_options; * Local static variables ***********************************************************************/ -static bool do_metrics = false; -static metrics_stdout* metrics_screen = nullptr; +static bool do_metrics = false; +static metrics_stdout* metrics_screen = nullptr; +static srslog::sink* log_sink = nullptr; +static std::atomic running = {true}; /********************************************************************** * Program arguments processing @@ -664,9 +669,25 @@ static size_t fixup_log_file_maxsize(int x) return (x < 0) ? 0 : size_t(x) * 1024u; } +extern "C" void srsran_dft_exit(); +static void emergency_cleanup_handler(void* data) +{ + srslog::flush(); + if (log_sink) { + log_sink->flush(); + } + srsran_dft_exit(); +} + +static void signal_handler() +{ + running = false; +} + int main(int argc, char* argv[]) { - srsran_register_signal_handler(); + srsran_register_signal_handler(signal_handler); + add_emergency_cleanup_handler(emergency_cleanup_handler, nullptr); srsran_debug_handle_crash(argc, argv); all_args_t args = {}; diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc index e97415ceb..901cbd561 100644 --- a/srsue/src/metrics_stdout.cc +++ b/srsue/src/metrics_stdout.cc @@ -144,7 +144,11 @@ void metrics_stdout::set_metrics_helper(const phy_metrics_t& phy, fmt::print(" |"); fmt::print(" {:>2}", int(phy.dl[r].mcs)); - fmt::print(" {:>3}", int(phy.ch[r].sinr)); + if (std::isnan(phy.ch[r].sinr) || std::isinf(phy.ch[r].sinr)) { + fmt::print(" {:>3}", "n/a"); + } else { + fmt::print(" {:>3}", int(phy.ch[r].sinr)); + } fmt::print(" {:>4.1f}", phy.dl[r].fec_iters); fmt::print(" {:>6.6}", float_to_eng_string((float)mac[r].rx_brate / (mac[r].nof_tti * 1e-3), 2)); diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 80fc45fca..c7faf0cbc 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -94,15 +94,8 @@ bool cc_worker::update_cfg() return false; } - srsran_ssb_cfg_t ssb_cfg = {}; + srsran_ssb_cfg_t ssb_cfg = phy.cfg.get_ssb_cfg(); ssb_cfg.srate_hz = srsran_min_symbol_sz_rb(phy.cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs); - ssb_cfg.center_freq_hz = phy.cfg.carrier.dl_center_frequency_hz; - ssb_cfg.ssb_freq_hz = phy.cfg.carrier.ssb_center_freq_hz; - ssb_cfg.scs = phy.cfg.ssb.scs; - ssb_cfg.pattern = phy.cfg.ssb.pattern; - ssb_cfg.duplex_mode = phy.cfg.duplex.mode; - ssb_cfg.periodicity_ms = phy.cfg.ssb.periodicity_ms; - if (srsran_ssb_set_cfg(&ssb, &ssb_cfg) < SRSRAN_SUCCESS) { logger.error("Error setting SSB configuration"); return false; @@ -176,23 +169,16 @@ void cc_worker::decode_pdcch_dl() if (logger.debug.enabled()) { for (uint32_t i = 0; i < ue_dl.pdcch_info_count; i++) { - const srsran_ue_dl_nr_pdcch_info_t* info = &ue_dl.pdcch_info[i]; - logger.debug( - "PDCCH: dci=%s, %s-rnti=%x, crst_id=%d, ss_type=%s, ncce=%d, al=%d, EPRE=%+.2f, RSRP=%+.2f, corr=%.3f; " - "evm=%f; nof_bits=%d; crc=%s;", - srsran_dci_format_nr_string(info->dci_ctx.format), - srsran_rnti_type_str_short(info->dci_ctx.rnti_type), - info->dci_ctx.rnti, - info->dci_ctx.coreset_id, - srsran_ss_type_str(info->dci_ctx.ss_type), - info->dci_ctx.location.ncce, - info->dci_ctx.location.L, - info->measure.epre_dBfs, - info->measure.rsrp_dBfs, - info->measure.norm_corr, - info->result.evm, - info->nof_bits, - info->result.crc ? "OK" : "KO"); + const srsran_ue_dl_nr_pdcch_info_t* info = &ue_dl.pdcch_info[i]; + std::array dci_ctx = {}; + srsran_dci_ctx_to_str(&info->dci_ctx, dci_ctx.data(), (uint32_t)dci_ctx.size()); + logger.debug("PDCCH: %sEPRE=%+.2f, RSRP=%+.2f, corr=%.3f nof_bits=%d crc=%s", + dci_ctx.data(), + info->measure.epre_dBfs, + info->measure.rsrp_dBfs, + info->measure.norm_corr, + info->nof_bits, + info->result.crc ? "OK" : "KO"); } } } @@ -353,7 +339,10 @@ bool cc_worker::decode_pdsch_dl() dl_m.evm = pdsch_res.evm[0]; phy.set_dl_metrics(dl_m); } - + ch_metrics_t ch_metrics = {}; + ch_metrics.sinr = ue_dl.chest.snr_db; + ch_metrics.sync_err = ue_dl.chest.sync_error; + phy.set_channel_metrics(ch_metrics); return true; } @@ -401,13 +390,17 @@ bool cc_worker::measure_csi() } // Check if the SFN matches - // ... + if (mib.sfn != dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs)) { + logger.error("PBCH-MIB: NR SFN (%d) does not match current SFN (%d)", + mib.sfn, + dl_slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(phy.cfg.carrier.scs)); + } // Log MIB information if (logger.debug.enabled()) { std::array str = {}; srsran_pbch_msg_nr_mib_info(&mib, str.data(), (uint32_t)str.size()); - logger.debug("PBCH-MIB: sfn_4lsb=%d; %s", (dl_slot_cfg.idx / 10) & 0xf, str.data()); + logger.debug("PBCH-MIB: %s", str.data()); } } else { // CRC shall never fail if the UE is in sync @@ -599,7 +592,7 @@ bool cc_worker::work_ul() } // Add CSI reports to UCI data if available - phy.get_periodic_csi(ul_slot_cfg.idx, uci_data); + phy.get_periodic_csi(ul_slot_cfg, uci_data); // Setup frequency offset srsran_ue_ul_nr_set_freq_offset(&ue_ul, phy.get_ul_cfo()); diff --git a/srsue/src/phy/nr/sf_worker.cc b/srsue/src/phy/nr/sf_worker.cc index b3dfeb2fe..c33fcb91d 100644 --- a/srsue/src/phy/nr/sf_worker.cc +++ b/srsue/src/phy/nr/sf_worker.cc @@ -97,13 +97,6 @@ void sf_worker::work_imp() tx_buffer.set(phy_state.args.rf_channel_offset, prach_ptr); tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB_NR(phy_state.cfg.carrier.nof_prb)); - // Notify MAC about PRACH transmission - phy_state.stack->prach_sent(TTI_TX(tti_rx), - srsran_prach_nr_start_symbol(phy_state.cfg.prach.config_idx, phy_state.cfg.duplex.mode), - SRSRAN_SLOT_NR_MOD(phy_state.cfg.carrier.scs, TTI_TX(tti_rx)), - 0, - 0); - // Transmit NR PRACH common.worker_end(context, true, tx_buffer); diff --git a/srsue/src/phy/nr/worker_pool.cc b/srsue/src/phy/nr/worker_pool.cc index d32c00759..27097197a 100644 --- a/srsue/src/phy/nr/worker_pool.cc +++ b/srsue/src/phy/nr/worker_pool.cc @@ -87,10 +87,39 @@ sf_worker* worker_pool::wait_worker(uint32_t tti) // Generate PRACH if ready if (prach_buffer->is_ready_to_send(tti, phy_state.cfg.carrier.pci)) { - uint32_t nof_prach_sf = 0; - float prach_target_power = 0.0f; - cf_t* prach_ptr = prach_buffer->generate(phy_state.get_ul_cfo() / 15000, &nof_prach_sf, &prach_target_power); - worker->set_prach(prach_ptr, prach_target_power); + prach_ptr = prach_buffer->generate(phy_state.get_ul_cfo() / 15000, &prach_nof_sf, &prach_target_power); + + // Scale signal to maximum + { + float* ptr = (float*)prach_ptr; + int max_i = srsran_vec_max_abs_fi(ptr, 2 * sf_sz); + float max = ptr[max_i]; + if (std::isnormal(max)) { + srsran_vec_sc_prod_cfc(prach_ptr, 0.99f / max, prach_ptr, sf_sz * prach_nof_sf); + } + } + + // Notify MAC about PRACH transmission + phy_state.stack->prach_sent(TTI_TX(tti), + srsran_prach_nr_start_symbol(phy_state.cfg.prach.config_idx, phy_state.cfg.duplex.mode), + SRSRAN_SLOT_NR_MOD(phy_state.cfg.carrier.scs, TTI_TX(tti)), + 0, + 0); + } + + // Set PRACH transmission buffer in workers if it is pending + if (prach_nof_sf > 0) { + // Set worker PRACH buffer + worker->set_prach(&prach_ptr[sf_sz * prach_sf_count], prach_target_power); + + // Increment SF counter + prach_sf_count++; + + // Reset PRACH pending subframe count + if (prach_sf_count >= prach_nof_sf) { + prach_nof_sf = 0; + prach_sf_count = 0; + } } return worker; @@ -150,6 +179,7 @@ bool worker_pool::set_config(const srsran::phy_cfg_nr_t& cfg) { uint32_t dl_arfcn = srsran::srsran_band_helper().freq_to_nr_arfcn(cfg.carrier.dl_center_frequency_hz); phy_state.cfg = cfg; + sf_sz = SRSRAN_SF_LEN_PRB_NR(cfg.carrier.nof_prb); logger.info("Setting new PHY configuration ARFCN=%d, PCI=%d", dl_arfcn, cfg.carrier.pci); diff --git a/srsue/src/phy/phy_common.cc b/srsue/src/phy/phy_common.cc index 61041a141..cc1d1731c 100644 --- a/srsue/src/phy/phy_common.cc +++ b/srsue/src/phy/phy_common.cc @@ -696,7 +696,7 @@ void phy_common::update_measurements(uint32_t cc_idx, rx_gain_offset = get_radio()->get_rx_gain() + args->rx_gain_offset; } rssi_read_cnt++; - if (rssi_read_cnt == 1000) { + if (rssi_read_cnt >= 1000) { rssi_read_cnt = 0; } } diff --git a/srsue/src/phy/prach.cc b/srsue/src/phy/prach.cc index 7781635d7..6dae6abeb 100644 --- a/srsue/src/phy/prach.cc +++ b/srsue/src/phy/prach.cc @@ -42,15 +42,6 @@ using namespace srsue; void prach::init(uint32_t max_prb) { std::lock_guard lock(mutex); - for (auto& i : buffer) { - for (auto& j : i) { - j = srsran_vec_cf_malloc(SRSRAN_PRACH_MAX_LEN); - if (!j) { - perror("malloc"); - return; - } - } - } if (srsran_cfo_init(&cfo_h, SRSRAN_PRACH_MAX_LEN)) { ERROR("PRACH: Error initiating CFO"); @@ -59,7 +50,7 @@ void prach::init(uint32_t max_prb) srsran_cfo_set_tol(&cfo_h, 0); - signal_buffer = srsran_vec_cf_malloc(MAX_LEN_SF * 30720U); + signal_buffer = srsran_vec_cf_malloc(SRSRAN_MAX(MAX_LEN_SF * 30720U, SRSRAN_PRACH_MAX_LEN)); if (!signal_buffer) { perror("malloc"); return; @@ -80,12 +71,6 @@ void prach::stop() return; } - for (auto& i : buffer) { - for (auto& j : i) { - free(j); - } - } - free(signal_buffer); srsran_cfo_free(&cfo_h); srsran_prach_free(&prach_obj); @@ -127,7 +112,6 @@ bool prach::set_cell(srsran_cell_t cell_, srsran_prach_cfg_t prach_cfg) return false; } - buffer_bitmask.reset(); len = prach_obj.N_seq + prach_obj.N_cp; transmitted_tti = -1; cell_initiated = true; @@ -139,22 +123,16 @@ bool prach::set_cell(srsran_cell_t cell_, srsran_prach_cfg_t prach_cfg) bool prach::generate_buffer(uint32_t f_idx) { - if (is_buffer_generated(f_idx, preamble_idx)) { - return true; - } - uint32_t freq_offset = cfg.freq_offset; if (cell.frame_type == SRSRAN_TDD) { freq_offset = srsran_prach_f_ra_tdd( cfg.config_idx, cfg.tdd_config.sf_config, (f_idx / 6) * 10, f_idx % 6, cfg.freq_offset, cell.nof_prb); } - if (srsran_prach_gen(&prach_obj, preamble_idx, freq_offset, buffer[f_idx][preamble_idx])) { + if (srsran_prach_gen(&prach_obj, preamble_idx, freq_offset, signal_buffer)) { Error("Generating PRACH preamble %d", preamble_idx); return false; } - set_buffer_as_generated(f_idx, preamble_idx); - return true; } @@ -248,16 +226,11 @@ cf_t* prach::generate(float cfo, uint32_t* nof_sf, float* target_power) return nullptr; } - if (!is_buffer_generated(f_idx, preamble_idx)) { - Error("PRACH Buffer not generated: f_idx=%d preamble_idx=%d", f_idx, preamble_idx); - return nullptr; - } - // Correct CFO before transmission - srsran_cfo_correct(&cfo_h, buffer[f_idx][preamble_idx], signal_buffer, cfo / srsran_symbol_sz(cell.nof_prb)); + srsran_cfo_correct(&cfo_h, signal_buffer, signal_buffer, cfo / srsran_symbol_sz(cell.nof_prb)); // pad guard symbols with zeros - uint32_t nsf = (len - 1) / SRSRAN_SF_LEN_PRB(cell.nof_prb) + 1; + uint32_t nsf = SRSRAN_CEIL(len, SRSRAN_SF_LEN_PRB(cell.nof_prb)); srsran_vec_cf_zero(&signal_buffer[len], (nsf * SRSRAN_SF_LEN_PRB(cell.nof_prb) - len)); *nof_sf = nsf; diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 8c3461959..92cb92d69 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -144,10 +144,10 @@ void sync::stop() } running = false; + wait_thread_finish(); + // Reset (stop Rx stream) as soon as possible to avoid base-band Rx buffer overflow radio_h->reset(); - - wait_thread_finish(); } void sync::reset() @@ -936,13 +936,10 @@ void sync::set_sampling_rate() return; } - if (srate.set_camp(new_srate)) { - Info("SYNC: Setting sampling rate %.2f MHz", new_srate / 1000000); - radio_h->set_rx_srate(new_srate); - radio_h->set_tx_srate(new_srate); - } else { - Error("Error setting sampling rate for cell with %d PRBs", cell.get().nof_prb); - } + srate.set_camp(new_srate); + Info("SYNC: Setting sampling rate %.2f MHz", new_srate / 1000000); + radio_h->set_rx_srate(new_srate); + radio_h->set_tx_srate(new_srate); } uint32_t sync::get_current_tti() diff --git a/srsue/src/phy/test/scell_search_test.cc b/srsue/src/phy/test/scell_search_test.cc index 494326a8a..831491eb6 100644 --- a/srsue/src/phy/test/scell_search_test.cc +++ b/srsue/src/phy/test/scell_search_test.cc @@ -20,6 +20,7 @@ */ #include "srsran/interfaces/phy_interface_types.h" +#include "srsran/srslog/bundled/fmt/chrono.h" #include "srsran/srslog/srslog.h" #include "srsue/hdr/phy/scell/intra_measure_lte.h" #include @@ -45,6 +46,8 @@ static std::string active_cell_list; static std::string simulation_cell_list; static int phy_lib_log_level; static srsue::phy_args_t phy_args; +static bool enable_json_report; +static std::string json_report_filename; // On the Fly parameters static int earfcn_dl; @@ -221,8 +224,31 @@ public: } }; +/// Cell container metrics. +DECLARE_METRIC("earfcn_dl", metric_earfcn, int, ""); +DECLARE_METRIC("PCI", metric_pci, uint32_t, ""); +DECLARE_METRIC("RSRP", metric_rsrp, float, ""); +DECLARE_METRIC("RSRQ", metric_rsrq, float, ""); +DECLARE_METRIC("false_alarm", metric_false_alarm, std::string, ""); +DECLARE_METRIC_SET("cell_container", + mset_cell_container, + metric_earfcn, + metric_pci, + metric_rsrp, + metric_rsrq, + metric_false_alarm); + +/// Report root object. +DECLARE_METRIC("timestamp", metric_timestamp_tag, std::string, ""); +DECLARE_METRIC_LIST("cell_list", mlist_cell, std::vector); + +/// Report context. +using report_context_t = srslog::build_context_type; + class meas_itf_listener : public srsue::scell::intra_measure_base::meas_itf { + srslog::log_channel& json_channel; + public: typedef struct { float rsrp_avg; @@ -234,6 +260,8 @@ public: uint32_t count; } cell_meas_t; + explicit meas_itf_listener(srslog::log_channel& json_channel) : json_channel(json_channel) {} + std::map cells; void cell_meas_reset(uint32_t cc_idx) override {} @@ -271,6 +299,14 @@ public: uint32_t ideal_true_counts = (pcis_to_simulate.size() - 1) * tti_count; uint32_t ideal_false_counts = tti_count * cells.size() - ideal_true_counts; + report_context_t ctx("JSON Report"); + auto current_time = fmt::localtime(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())); + fmt::memory_buffer ts_buffer; + fmt::format_to(ts_buffer, "{:%F}T{:%T}", current_time, current_time); + ctx.write(srsran::to_c_str(ts_buffer)); + + auto& cell_list = ctx.get(); + for (auto& e : cells) { bool false_alarm = pcis_to_simulate.find(e.first) == pcis_to_simulate.end(); @@ -290,8 +326,19 @@ public: e.second.rsrq_min, e.second.rsrq_avg, e.second.rsrq_max); + + cell_list.emplace_back(); + auto& cell_container = cell_list.back(); + + cell_container.write(e.first); + cell_container.write(earfcn_dl); + cell_container.write(e.second.rsrp_avg); + cell_container.write(e.second.rsrq_avg); + cell_container.write(false_alarm ? "yes" : "no"); } + json_channel(ctx); + float prob_detection = (ideal_true_counts) ? (float)true_counts / (float)ideal_true_counts : 0.0f; float prob_false_alarm = (ideal_false_counts) ? (float)false_counts / (float)ideal_false_counts : 0.0f; printf("\n"); @@ -324,6 +371,8 @@ int parse_args(int argc, char** argv) ("intra_freq_meas_period_ms", bpo::value(&phy_args.intra_freq_meas_period_ms)->default_value(200), "Intra measurement measurement period") ("phy_lib_log_level", bpo::value(&phy_lib_log_level)->default_value(SRSRAN_VERBOSE_NONE), "Phy lib log level (0: none, 1: info, 2: debug)") ("active_cell_list", bpo::value(&active_cell_list)->default_value("10,17,24,31,38,45,52"), "Comma separated neighbour PCI cell list") + ("enable_json_report", bpo::value(&enable_json_report)->default_value(false), "Enable JSON file reporting") + ("json_report_filename", bpo::value(&json_report_filename)->default_value("/tmp/scell_search.json"), "JSON report filename") ; over_the_air.add_options() @@ -403,11 +452,14 @@ int main(int argc, char** argv) // Common for simulation and over-the-air srslog::basic_logger& logger = srslog::fetch_basic_logger("intra_measure"); + srslog::sink& json_sink = srslog::fetch_file_sink(json_report_filename, 0, false, srslog::create_json_formatter()); + srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {}); + json_channel.set_enabled(enable_json_report); srslog::init(); cf_t* baseband_buffer = srsran_vec_cf_malloc(SRSRAN_SF_LEN_MAX); srsran::rf_timestamp_t ts = {}; - meas_itf_listener rrc; + meas_itf_listener rrc(json_channel); srsue::scell::intra_measure_lte intra_measure(logger, rrc); // Simulation only diff --git a/srsue/src/stack/mac/ul_harq.cc b/srsue/src/stack/mac/ul_harq.cc index e48311a72..cd0c348ea 100644 --- a/srsue/src/stack/mac/ul_harq.cc +++ b/srsue/src/stack/mac/ul_harq.cc @@ -66,6 +66,7 @@ void ul_harq_entity::reset_ndi() void ul_harq_entity::set_config(srsran::ul_harq_cfg_t& harq_cfg_) { + std::lock_guard lock(config_mutex); harq_cfg = harq_cfg_; } @@ -120,8 +121,6 @@ ul_harq_entity::ul_harq_process::ul_harq_process() : logger(srslog::fetch_basic_ pdu_ptr = NULL; payload_buffer = NULL; - bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_ul_t)); - harq_feedback = false; is_initiated = false; is_grant_configured = false; @@ -163,12 +162,12 @@ void ul_harq_entity::ul_harq_process::reset() current_tx_nb = 0; current_irv = 0; is_grant_configured = false; - bzero(&cur_grant, sizeof(mac_interface_phy_lte::mac_grant_ul_t)); + cur_grant.reset(); } void ul_harq_entity::ul_harq_process::reset_ndi() { - cur_grant.tb.ndi = false; + cur_grant.set_ndi(false); } void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t grant, @@ -183,10 +182,13 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr // Get maximum retransmissions uint32_t max_retx; - if (grant.rnti == harq_entity->rntis->get_temp_rnti()) { - max_retx = harq_entity->harq_cfg.max_harq_msg3_tx; - } else { - max_retx = harq_entity->harq_cfg.max_harq_tx; + { + std::lock_guard lock(harq_entity->config_mutex); + if (grant.rnti == harq_entity->rntis->get_temp_rnti()) { + max_retx = harq_entity->harq_cfg.max_harq_msg3_tx; + } else { + max_retx = harq_entity->harq_cfg.max_harq_tx; + } } // Check maximum retransmissions, do not consider last retx ACK @@ -203,8 +205,9 @@ void ul_harq_entity::ul_harq_process::new_grant_ul(mac_interface_phy_lte::mac_gr // Reset HARQ process if TB has changed if (harq_feedback && has_grant() && grant.tb.ndi_present) { - if (grant.tb.tbs != cur_grant.tb.tbs && cur_grant.tb.tbs > 0 && grant.tb.tbs > 0) { - Debug("UL %d: Reset due to change of dci size last_grant=%d, new_grant=%d", pid, cur_grant.tb.tbs, grant.tb.tbs); + uint32_t cur_grant_tbs = cur_grant.get_tbs(); + if (grant.tb.tbs != cur_grant_tbs && cur_grant_tbs > 0 && grant.tb.tbs > 0) { + Debug("UL %d: Reset due to change of dci size last_grant=%d, new_grant=%d", pid, cur_grant_tbs, grant.tb.tbs); reset(); } } @@ -291,7 +294,7 @@ bool ul_harq_entity::ul_harq_process::has_grant() bool ul_harq_entity::ul_harq_process::get_ndi() { - return cur_grant.tb.ndi; + return cur_grant.get_ndi(); } bool ul_harq_entity::ul_harq_process::is_sps() @@ -306,7 +309,7 @@ uint32_t ul_harq_entity::ul_harq_process::get_nof_retx() int ul_harq_entity::ul_harq_process::get_current_tbs() { - return cur_grant.tb.tbs; + return cur_grant.get_tbs(); } // Retransmission with or w/o dci (Section 5.4.2.2) @@ -328,15 +331,15 @@ void ul_harq_entity::ul_harq_process::generate_retx(mac_interface_phy_lte::mac_g grant.tb.tbs, harq_feedback ? "ACK" : "NACK", grant.tb.ndi, - cur_grant.tb.ndi); + cur_grant.get_ndi()); - cur_grant = grant; + cur_grant.set(grant); harq_feedback = false; generate_tx(action); // Reset the RV received in this grant - cur_grant.tb.rv = -1; + cur_grant.set_rv(-1); // HARQ entity requests a non-adaptive transmission } else if (!harq_feedback) { @@ -345,7 +348,7 @@ void ul_harq_entity::ul_harq_process::generate_retx(mac_interface_phy_lte::mac_g pid, current_tx_nb, get_rv(), - cur_grant.tb.tbs, + cur_grant.get_tbs(), harq_feedback ? "ACK" : "NACK"); generate_tx(action); @@ -361,7 +364,7 @@ void ul_harq_entity::ul_harq_process::generate_new_tx(mac_interface_phy_lte::mac harq_entity->average_retx.load(std::memory_order_relaxed), harq_entity->nof_pkts++), std::memory_order_relaxed); - cur_grant = grant; + cur_grant.set(grant); harq_feedback = false; is_grant_configured = true; current_tx_nb = 0; @@ -373,7 +376,7 @@ void ul_harq_entity::ul_harq_process::generate_new_tx(mac_interface_phy_lte::mac pid, grant.rnti == harq_entity->rntis->get_temp_rnti() ? " for Msg3" : "", get_rv(), - cur_grant.tb.tbs); + cur_grant.get_tbs()); generate_tx(action); } @@ -385,7 +388,8 @@ void ul_harq_entity::ul_harq_process::generate_tx(mac_interface_phy_lte::tb_acti action->current_tx_nb = current_tx_nb; action->expect_ack = true; - action->tb.rv = cur_grant.tb.rv > 0 ? cur_grant.tb.rv : get_rv(); + int rv = cur_grant.get_rv(); + action->tb.rv = rv > 0 ? rv : get_rv(); action->tb.enabled = true; action->tb.payload = pdu_ptr; action->tb.softbuffer.tx = &softbuffer; diff --git a/srsue/src/stack/mac_nr/dl_harq_nr.cc b/srsue/src/stack/mac_nr/dl_harq_nr.cc index b23cac552..ed4e86efb 100644 --- a/srsue/src/stack/mac_nr/dl_harq_nr.cc +++ b/srsue/src/stack/mac_nr/dl_harq_nr.cc @@ -135,8 +135,9 @@ void dl_harq_entity_nr::reset() dl_harq_entity_nr::dl_harq_metrics_t dl_harq_entity_nr::get_metrics() { - dl_harq_metrics_t tmp = metrics; - metrics = {}; + std::lock_guard lock(metrics_mutex); + dl_harq_metrics_t tmp = metrics; + metrics = {}; return tmp; } @@ -239,9 +240,11 @@ void dl_harq_entity_nr::dl_harq_process_nr::tb_decoded(const mac_nr_grant_dl_t& } } + std::lock_guard lock(harq_entity->metrics_mutex); harq_entity->metrics.rx_ok++; harq_entity->metrics.rx_brate += grant.tbs * 8; } else { + std::lock_guard lock(harq_entity->metrics_mutex); harq_entity->metrics.rx_ko++; } diff --git a/srsue/src/stack/mac_nr/mac_nr.cc b/srsue/src/stack/mac_nr/mac_nr.cc index 9f0bf0546..d0cbc6695 100644 --- a/srsue/src/stack/mac_nr/mac_nr.cc +++ b/srsue/src/stack/mac_nr/mac_nr.cc @@ -21,6 +21,7 @@ #include "srsue/hdr/stack/mac_nr/mac_nr.h" #include "srsran/interfaces/ue_rlc_interfaces.h" +#include "srsran/interfaces/ue_rrc_interfaces.h" #include "srsran/mac/mac_rar_pdu_nr.h" #include "srsue/hdr/stack/mac_nr/proc_ra_nr.h" @@ -105,6 +106,12 @@ void mac_nr::stop() void mac_nr::reset() { logger.info("Resetting MAC-NR"); + + // TODO: Implement all the steps in 5.9 + proc_bsr.reset(); + proc_sr.reset(); + proc_ra.reset(); + mux.reset(); } void mac_nr::run_tti(const uint32_t tti) @@ -509,6 +516,16 @@ void mac_nr::get_metrics(mac_metrics_t m[SRSRAN_MAX_CARRIERS]) metrics = {}; } +void mac_nr::rrc_ra_problem() +{ + rrc->ra_problem(); +} + +void mac_nr::rrc_ra_completed() +{ + rrc->ra_completed(); +} + /** * Called from the main stack thread to process received PDUs */ diff --git a/srsue/src/stack/mac_nr/mux_nr.cc b/srsue/src/stack/mac_nr/mux_nr.cc index f54f37aa8..18da2f802 100644 --- a/srsue/src/stack/mac_nr/mux_nr.cc +++ b/srsue/src/stack/mac_nr/mux_nr.cc @@ -44,6 +44,12 @@ int32_t mux_nr::init(rlc_interface_mac* rlc_) return SRSRAN_SUCCESS; } +void mux_nr::reset() +{ + std::lock_guard lock(mutex); + this->logical_channels.clear(); +} + int mux_nr::setup_lcid(const srsran::logical_channel_config_t& config) { std::lock_guard lock(mutex); @@ -97,10 +103,10 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) uint8_t* rd = rlc_buff->msg; // Determine space for RLC - remaining_len -= remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2; + int32_t subpdu_header_len = (remaining_len >= srsran::mac_sch_subpdu_nr::MAC_SUBHEADER_LEN_THRESHOLD ? 3 : 2); - // Read PDU from RLC - int pdu_len = rlc->read_pdu(lc.lcid, rd, remaining_len); + // Read PDU from RLC (account for subPDU header) + int pdu_len = rlc->read_pdu(lc.lcid, rd, remaining_len - subpdu_header_len); if (pdu_len > remaining_len) { logger.error("Can't add SDU of %d B. Available space %d B", pdu_len, remaining_len); @@ -117,10 +123,11 @@ srsran::unique_byte_buffer_t mux_nr::get_pdu(uint32_t max_pdu_len) break; } } else { + // couldn't read PDU from RLC break; } - remaining_len -= pdu_len; + remaining_len -= (pdu_len + subpdu_header_len); logger.debug("%d B remaining PDU", remaining_len); } } diff --git a/srsue/src/stack/mac_nr/proc_bsr_nr.cc b/srsue/src/stack/mac_nr/proc_bsr_nr.cc index e3b106467..fd1e5686b 100644 --- a/srsue/src/stack/mac_nr/proc_bsr_nr.cc +++ b/srsue/src/stack/mac_nr/proc_bsr_nr.cc @@ -72,6 +72,8 @@ void proc_bsr_nr::reset() timer_periodic.stop(); timer_retx.stop(); + lcg_priorities.clear(); + triggered_bsr_type = NONE; } @@ -228,11 +230,14 @@ void proc_bsr_nr::set_padding_bytes(uint32_t nof_bytes) const uint32_t LBSR_CE_SUBHEADER_LEN = 1; // if the number of padding bits is equal to or larger than the size of the Short BSR plus its subheader but smaller // than the size of the Long BSR plus its subheader - if (nof_bytes >= SBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(SHORT_BSR, true) && - nof_bytes < LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(LONG_BSR, true)) { + if (nof_bytes >= (SBSR_CE_SUBHEADER_LEN + + srsran::mac_sch_subpdu_nr::sizeof_ce(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::SHORT_BSR, true)) && + nof_bytes < (LBSR_CE_SUBHEADER_LEN + + srsran::mac_sch_subpdu_nr::sizeof_ce(srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR, true))) { // generate short padding BSR mux->generate_bsr_mac_ce(SHORT_BSR); - } else if (nof_bytes >= LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce(LONG_BSR, true)) { + } else if (nof_bytes >= (LBSR_CE_SUBHEADER_LEN + srsran::mac_sch_subpdu_nr::sizeof_ce( + srsran::mac_sch_subpdu_nr::nr_lcid_sch_t::LONG_BSR, true))) { // report Long BSR if more than one LCG has data to send mux->generate_bsr_mac_ce(LONG_BSR); } diff --git a/srsue/src/stack/mac_nr/proc_ra_nr.cc b/srsue/src/stack/mac_nr/proc_ra_nr.cc index 4e7960140..b99b64864 100644 --- a/srsue/src/stack/mac_nr/proc_ra_nr.cc +++ b/srsue/src/stack/mac_nr/proc_ra_nr.cc @@ -137,7 +137,7 @@ void proc_ra_nr::timer_expired(uint32_t timer_id) { if (prach_send_timer.id() == timer_id) { logger.warning("PRACH Send timer expired. PRACH was not transmitted within %d ttis by phy. (TODO)", - prach_send_timer.duration()); + prach_send_timer.duration()); ra_error(); } else if (rar_timeout_timer.id() == timer_id) { logger.warning("RAR Timer expired. RA response not received within the response window"); @@ -158,10 +158,10 @@ void proc_ra_nr::ra_procedure_initialization() { mac.msg3_flush(); preamble_transmission_counter = 1; - preamble_power_ramping_step = rach_cfg.powerRampingStep; - scaling_factor_bi = 1; + preamble_power_ramping_step = rach_cfg.powerRampingStep; + scaling_factor_bi = 1; preamble_backoff = 0; - preambleTransMax = rach_cfg.preambleTransMax; + preambleTransMax = rach_cfg.preambleTransMax; ra_resource_selection(); } @@ -287,6 +287,7 @@ void proc_ra_nr::ra_completion() srsran::console("Random Access Complete. c-rnti=0x%x, ta=%d\n", mac.get_crnti(), current_ta); logger.info("Random Access Complete. c-rnti=0x%x, ta=%d", mac.get_crnti(), current_ta); temp_crnti = SRSRAN_INVALID_RNTI; + mac.rrc_ra_completed(); reset(); } @@ -302,9 +303,9 @@ void proc_ra_nr::ra_error() if (preamble_transmission_counter >= rach_cfg.preambleTransMax + 1) { logger.warning("Maximum number of transmissions reached (%d)", rach_cfg.preambleTransMax); // if the Random Access Preamble is transmitted on the SpCell assumption (TODO) - mac.rrc_ra_problem(); // indicate a Random Access problem to upper layers; + mac.rrc_ra_problem(); // indicate a Random Access problem to upper layers; if (started_by == initiators_t::MAC) { // if this Random Access procedure was triggered for SI request - ra_procedure_completed = true; // consider the Random Access procedure unsuccessfully completed. + ra_procedure_completed = true; // consider the Random Access procedure unsuccessfully completed. reset(); } } else { diff --git a/srsue/src/stack/mac_nr/test/mac_nr_test.cc b/srsue/src/stack/mac_nr/test/mac_nr_test.cc index 014bcac36..373d8ac6e 100644 --- a/srsue/src/stack/mac_nr/test/mac_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/mac_nr_test.cc @@ -110,8 +110,12 @@ public: return 0; } - if (read_len > 0 && read_len < (int32_t)nof_bytes) { - nof_bytes = read_len; + if (read_len.size() >= read_len_idx) { + int32_t tmp_read_len = read_len.at(read_len_idx); + if (read_len.at(read_len_idx) > 0 && tmp_read_len < (int32_t)nof_bytes) { + nof_bytes = tmp_read_len; + } + read_len_idx = (read_len_idx + 1) % read_len.size(); } uint32_t len = SRSRAN_MIN(ul_queues[lcid], nof_bytes); @@ -136,7 +140,7 @@ public: uint32_t get_received_pdus() { return received_pdus; } void disable_read() { read_enable = false; } - void set_read_len(uint32_t len) { read_len = len; } + void set_read_len(const std::vector& read_len_) { read_len = read_len_; } void set_read_min(uint32_t len) { read_min = len; } void reset_queues() { @@ -147,7 +151,8 @@ public: private: bool read_enable = true; - int32_t read_len = -1; // read all + std::vector read_len = {-1}; // read all + uint32_t read_len_idx = 0; uint32_t read_min = 0; // minimum "grant size" for read_pdu() to return data uint32_t received_bytes = 0; uint32_t received_pdus = 0; @@ -434,6 +439,176 @@ int mac_nr_ul_logical_channel_prioritization_test2() return SRSRAN_SUCCESS; } +// Correct packing of MAC PDU with multiple subHeader with 16bit L field +int mac_nr_ul_logical_channel_prioritization_test3() +{ + // PDU layout (4737 B in total) + // TV skipped because it is very big + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + const uint16_t crnti = 0x1001; + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + mac.set_crnti(crnti); + + stack.init(&mac, &phy); + + // generate config (default DRB2 config for EN-DC) + std::vector lcids; + srsran::logical_channel_config_t config = {}; + config.lcid = 4; + config.lcg = 6; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 11; + lcids.push_back(config); + + // setup LCIDs in MAC + for (auto& channel : lcids) { + mac.setup_lcid(channel); + } + + // write muliple SDUs to DRB2 + const uint32_t sdu_len = 1502; + for (uint32_t i = 0; i < 10; ++i) { + rlc.write_sdu(4, sdu_len); + } + std::vector read_len; + read_len.push_back(80); + read_len.push_back(1480); + read_len.push_back(1480); + read_len.push_back(1480); + read_len.push_back(203); + rlc.set_read_len(read_len); + + // run TTI to setup Bj, BSR should be generated + stack.run_tti(0); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.tti = 0; + mac_grant.tbs = 4737; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(0); + mac.stop(); + + return SRSRAN_SUCCESS; +} + +// Correct packing of MAC PDU with two SDUs for different LCIDs +int mac_nr_ul_logical_channel_prioritization_test4() +{ + // PDU layout (24 B in total) + const uint8_t tv[] = {0x04, 0x0a, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, 0x04, + 0x05, 0x0a, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05, 0x05}; + + // dummy layers + dummy_phy phy; + rlc_dummy rlc; + rrc_dummy rrc; + stack_dummy stack; + + // the actual MAC + mac_nr mac(&stack.task_sched); + + const uint16_t crnti = 0x1001; + mac_nr_args_t args = {}; + mac.init(args, &phy, &rlc, &rrc); + mac.set_crnti(crnti); + + stack.init(&mac, &phy); + + // generate config for two DRBs + std::vector lcids; + srsran::logical_channel_config_t config = {}; + config.lcid = 4; + config.lcg = 6; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 11; + lcids.push_back(config); + + config.lcid = 5; + config.lcg = 7; + config.PBR = 0; + config.BSD = 1000; // 1000ms + config.priority = 12; + lcids.push_back(config); + + // setup LCIDs in MAC + for (auto& channel : lcids) { + mac.setup_lcid(channel); + } + + // write one SDU to each DRB + const uint32_t sdu_len = 10; + rlc.write_sdu(4, sdu_len); + rlc.write_sdu(5, sdu_len); + + // run TTI to setup Bj, BSR should be generated + stack.run_tti(0); + usleep(100); + + // create UL action and grant and read MAC PDU + { + mac_interface_phy_nr::tb_action_ul_t ul_action = {}; + mac_interface_phy_nr::mac_nr_grant_ul_t mac_grant = {}; + + mac_grant.rnti = crnti; // make sure MAC picks it up as valid UL grant + mac_grant.pid = 0; + mac_grant.tti = 0; + mac_grant.tbs = 24; + int cc_idx = 0; + + // Send grant to MAC and get action for this TB, 0x + mac.new_grant_ul(cc_idx, mac_grant, &ul_action); + + // print generated PDU + srslog::fetch_basic_logger("MAC").info( + ul_action.tb.payload->msg, mac_grant.tbs, "Generated PDU (%d B)", mac_grant.tbs); +#if HAVE_PCAP + pcap_handle->write_ul_crnti_nr( + ul_action.tb.payload->msg, mac_grant.tbs, mac_grant.rnti, UE_ID, mac_grant.pid, mac_grant.tti); +#endif + + TESTASSERT(memcmp(ul_action.tb.payload->msg, tv, sizeof(tv)) == 0); + } + + // make sure MAC PDU thread picks up before stopping + stack.run_tti(0); + mac.stop(); + + return SRSRAN_SUCCESS; +} + // Basic test for periodic BSR transmission int mac_nr_ul_periodic_bsr_test() { @@ -719,6 +894,9 @@ int main() TESTASSERT(msg3_test() == SRSRAN_SUCCESS); TESTASSERT(mac_nr_ul_logical_channel_prioritization_test1() == SRSRAN_SUCCESS); TESTASSERT(mac_nr_ul_logical_channel_prioritization_test2() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_logical_channel_prioritization_test3() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_logical_channel_prioritization_test4() == SRSRAN_SUCCESS); + TESTASSERT(mac_nr_ul_periodic_bsr_test() == SRSRAN_SUCCESS); TESTASSERT(mac_nr_dl_retx_test() == SRSRAN_SUCCESS); diff --git a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc index 6b7165f0d..38615c539 100644 --- a/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc +++ b/srsue/src/stack/mac_nr/test/proc_ra_nr_test.cc @@ -82,6 +82,7 @@ public: void msga_flush(){}; // RRC RA problem void rrc_ra_problem() { logger.warning("Dummy MAC RRC ra problem"); } + void rrc_ra_completed() { logger.info("Dummy MAC RRC ra completed"); } private: uint16_t crnti = SRSRAN_INVALID_RNTI; @@ -136,10 +137,10 @@ int proc_ra_normal_test() mac_interface_phy_nr::mac_nr_grant_dl_t grant; grant.rnti = 0x16; grant.tti = rach_cfg.ra_responseWindow + tti_start + 3; - grant.pid = 0x0; + grant.pid = 0x0; uint8_t mac_dl_rar_pdu[] = {0x40, 0x06, 0x68, 0x03, 0x21, 0x46, 0x46, 0x02, 0x00, 0x00, 0x00}; - mac_interface_phy_nr::tb_action_dl_result_t result = {}; - result.payload = srsran::make_byte_buffer(); + mac_interface_phy_nr::tb_action_dl_result_t result = {}; + result.payload = srsran::make_byte_buffer(); TESTASSERT(result.payload != nullptr); result.payload.get()->append_bytes(mac_dl_rar_pdu, sizeof(mac_dl_rar_pdu)); proc_ra_nr.handle_rar_pdu(result); diff --git a/srsue/src/stack/mac_nr/ul_harq_nr.cc b/srsue/src/stack/mac_nr/ul_harq_nr.cc index 52b640d9b..8c4600cdb 100644 --- a/srsue/src/stack/mac_nr/ul_harq_nr.cc +++ b/srsue/src/stack/mac_nr/ul_harq_nr.cc @@ -115,6 +115,7 @@ int ul_harq_entity_nr::get_current_tbs(uint32_t pid) ul_harq_entity_nr::ul_harq_metrics_t ul_harq_entity_nr::get_metrics() { + std::lock_guard lock(metrics_mutex); ul_harq_entity_nr::ul_harq_metrics_t tmp = metrics; metrics = {}; return tmp; @@ -249,6 +250,7 @@ void ul_harq_entity_nr::ul_harq_process_nr::generate_new_tx(const mac_interface_ generate_tx(action); + std::lock_guard lock(harq_entity->metrics_mutex); harq_entity->metrics.tx_ok++; } @@ -264,6 +266,7 @@ void ul_harq_entity_nr::ul_harq_process_nr::generate_retx(const mac_interface_ph generate_tx(action); // increment Tx error count + std::lock_guard lock(harq_entity->metrics_mutex); harq_entity->metrics.tx_ko++; } @@ -271,7 +274,11 @@ void ul_harq_entity_nr::ul_harq_process_nr::generate_retx(const mac_interface_ph void ul_harq_entity_nr::ul_harq_process_nr::generate_tx(mac_interface_phy_nr::tb_action_ul_t* action) { nof_retx++; - harq_entity->metrics.tx_brate += current_grant.tbs * 8; + + { + std::lock_guard lock(harq_entity->metrics_mutex); + harq_entity->metrics.tx_brate += current_grant.tbs * 8; + } action->tb.rv = current_grant.rv; action->tb.enabled = true; diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index 4479827d2..2c5ce575c 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -1143,6 +1143,10 @@ void rrc::rrc_connection_release(const std::string& cause) // Save idleModeMobilityControlInfo, etc. srsran::console("Received RRC Connection Release (releaseCause: %s)\n", cause.c_str()); + if (has_nr_dc()) { + rrc_nr->rrc_release(); + } + // delay actions by 60ms as per 5.3.8.3 task_sched.defer_callback(60, [this]() { start_go_idle(); }); } @@ -2842,10 +2846,7 @@ uint32_t rrc::get_drb_id_for_eps_bearer(const uint32_t& eps_bearer_id) bool rrc::has_nr_dc() { - bool has_nr_dc = false; - if (args.release >= 15) - has_nr_dc = true; - return has_nr_dc; + return (args.release >= 15); } void rrc::add_mrb(uint32_t lcid, uint32_t port) diff --git a/srsue/src/stack/rrc/rrc_nr.cc b/srsue/src/stack/rrc/rrc_nr.cc index 1cb1658d9..fb755437b 100644 --- a/srsue/src/stack/rrc/rrc_nr.cc +++ b/srsue/src/stack/rrc/rrc_nr.cc @@ -371,6 +371,13 @@ bool rrc_nr::rrc_reconfiguration(bool endc_release_and_add_r15, return true; } +void rrc_nr::rrc_release() +{ + rlc->reset(); + pdcp->reset(); + mac->reset(); +} + int rrc_nr::get_nr_capabilities(srsran::byte_buffer_t* nr_caps_pdu) { struct ue_nr_cap_s nr_cap; @@ -667,12 +674,14 @@ bool rrc_nr::apply_sp_cell_init_dl_pdsch(const asn1::rrc_nr::pdsch_cfg_s& pdsch_ return false; } } - } else { - logger.warning("Option zp_csi_rs_res_to_add_mod_list not present"); - return false; } if (pdsch_cfg.p_zp_csi_rs_res_set_present) { + // check if resources have been processed + if (not pdsch_cfg.zp_csi_rs_res_to_add_mod_list_present) { + logger.warning("Can't build ZP-CSI config, option zp_csi_rs_res_to_add_mod_list not present"); + return false; + } if (pdsch_cfg.p_zp_csi_rs_res_set.type() == setup_release_c::types_opts::setup) { for (uint32_t i = 0; i < pdsch_cfg.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list.size(); i++) { uint8_t res = pdsch_cfg.p_zp_csi_rs_res_set.setup().zp_csi_rs_res_id_list[i]; @@ -1152,6 +1161,7 @@ bool rrc_nr::apply_sp_cell_ded_ul_pusch(const asn1::rrc_nr::pusch_cfg_s& pusch_c bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) { + srsran_csi_hl_cfg_t prev_csi = phy_cfg.csi; if (sp_cell_cfg.recfg_with_sync_present) { const recfg_with_sync_s& recfg_with_sync = sp_cell_cfg.recfg_with_sync; mac->set_crnti(recfg_with_sync.new_ue_id); @@ -1163,21 +1173,21 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) logger.warning("Option PCI not present"); return false; } - if (recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present) { - if (apply_ul_common_cfg(recfg_with_sync.sp_cell_cfg_common.ul_cfg_common) == false) { + // Read essential DL carrier settings + if (recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present) { + if (apply_dl_common_cfg(recfg_with_sync.sp_cell_cfg_common.dl_cfg_common) == false) { return false; } } else { - logger.warning("Secondary primary cell ul cfg common not present"); + logger.warning("Secondary primary cell dl cfg common not present"); return false; } - // Read essential DL carrier settings - if (recfg_with_sync.sp_cell_cfg_common.dl_cfg_common_present) { - if (apply_dl_common_cfg(recfg_with_sync.sp_cell_cfg_common.dl_cfg_common) == false) { + if (recfg_with_sync.sp_cell_cfg_common.ul_cfg_common_present) { + if (apply_ul_common_cfg(recfg_with_sync.sp_cell_cfg_common.ul_cfg_common) == false) { return false; } } else { - logger.warning("DL cfg common not present"); + logger.warning("Secondary primary cell ul cfg common not present"); return false; } // Build SSB config @@ -1227,7 +1237,10 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg_present) { if (sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg.type() == setup_release_c::types_opts::setup) { - apply_sp_cell_init_dl_pdsch(sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg.setup()); + if (apply_sp_cell_init_dl_pdsch(sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdsch_cfg.setup()) == false) { + logger.error("Couldn't apply PDSCH config for initial DL BWP in SpCell Cfg dedicated"); + return false; + }; } else { logger.warning("Option pdsch_cfg_cfg not of type setup"); return false; @@ -1314,7 +1327,14 @@ bool rrc_nr::apply_sp_cell_cfg(const sp_cell_cfg_s& sp_cell_cfg) logger.warning("Option sp_cell_cfg_ded not present"); return false; } - phy->set_config(phy_cfg); + + // Configure PHY + // Note: CSI config is deferred to when RA is complete. See TS 38.331, Section 5.3.5.3 + srsran::phy_cfg_nr_t current_phycfg = phy_cfg; + current_phycfg.csi = prev_csi; + phy->set_config(current_phycfg); + + // Start RA procedure mac->start_ra_procedure(); return true; } @@ -1510,7 +1530,11 @@ void rrc_nr::max_retx_attempted() {} void rrc_nr::protocol_failure() {} // MAC interface -void rrc_nr::ra_completed() {} +void rrc_nr::ra_completed() +{ + logger.info("RA completed. Applying remaining CSI configuration."); + phy->set_config(phy_cfg); +} void rrc_nr::ra_problem() { rrc_eutra->nr_scg_failure_information(scg_failure_cause_t::random_access_problem); diff --git a/srsue/src/stack/rrc/test/rrc_meas_test.cc b/srsue/src/stack/rrc/test/rrc_meas_test.cc index 49d43942a..d36a1cb72 100644 --- a/srsue/src/stack/rrc/test/rrc_meas_test.cc +++ b/srsue/src/stack/rrc/test/rrc_meas_test.cc @@ -189,7 +189,8 @@ public: asn1::dyn_octstring nr_radio_bearer_cfg1_r15) override { return false; - }; + } + void rrc_release() override {} bool is_config_pending() override { return false; }; }; diff --git a/srsue/src/stack/rrc/test/ue_rrc_nr_test.cc b/srsue/src/stack/rrc/test/ue_rrc_nr_test.cc index 5b57fec13..576b11354 100644 --- a/srsue/src/stack/rrc/test/ue_rrc_nr_test.cc +++ b/srsue/src/stack/rrc/test/ue_rrc_nr_test.cc @@ -36,6 +36,7 @@ class dummy_phy : public phy_interface_rrc_nr class dummy_mac : public mac_interface_rrc_nr { + void reset() {} int setup_lcid(const srsran::logical_channel_config_t& config) { return SRSRAN_SUCCESS; } int set_config(const srsran::bsr_cfg_nr_t& bsr_cfg) { return SRSRAN_SUCCESS; } int set_config(const srsran::sr_cfg_nr_t& sr_cfg) { return SRSRAN_SUCCESS; } @@ -71,7 +72,7 @@ class dummy_rlc : public rlc_interface_rrc class dummy_pdcp : public pdcp_interface_rrc { - void set_enabled(uint32_t lcid, bool enabled) {}; + void set_enabled(uint32_t lcid, bool enabled){}; void reestablish(){}; void reestablish(uint32_t lcid){}; void reset(){}; diff --git a/srsue/src/stack/ue_stack_nr.cc b/srsue/src/stack/ue_stack_nr.cc index a357e3745..9f7fa33ca 100644 --- a/srsue/src/stack/ue_stack_nr.cc +++ b/srsue/src/stack/ue_stack_nr.cc @@ -21,6 +21,7 @@ #include "srsue/hdr/stack/ue_stack_nr.h" #include "srsran/srsran.h" +#include "srsue/hdr/stack/rrc/rrc_nr.h" using namespace srsran; @@ -119,7 +120,7 @@ void ue_stack_nr::stop_impl() bool ue_stack_nr::switch_on() { // statically setup TUN (will be done through RRC later) - char* err_str = nullptr; + char* err_str = nullptr; struct in_addr in_addr; if (inet_pton(AF_INET, "192.168.1.3", &in_addr.s_addr) != 1) { perror("inet_pton"); diff --git a/srsue/src/stack/upper/nas.cc b/srsue/src/stack/upper/nas.cc index 5eb05c710..147e035ce 100644 --- a/srsue/src/stack/upper/nas.cc +++ b/srsue/src/stack/upper/nas.cc @@ -90,10 +90,11 @@ int nas::init(usim_interface_nas* usim_, rrc_interface_nas* rrc_, gw_interface_n cfg = cfg_; - if ((read_ctxt_file(&ctxt))) { - usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, ctxt.cipher_algo, ctxt.integ_algo); - logger.debug(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); - logger.debug(k_nas_int, 32, "NAS integrity key - k_nas_int"); + if ((read_ctxt_file(&ctxt, &ctxt_base))) { + usim->generate_nas_keys( + ctxt.k_asme, ctxt_base.k_nas_enc, ctxt_base.k_nas_int, ctxt_base.cipher_algo, ctxt_base.integ_algo); + logger.debug(ctxt_base.k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + logger.debug(ctxt_base.k_nas_int, 32, "NAS integrity key - k_nas_int"); } // Configure timers @@ -117,7 +118,7 @@ nas::~nas() {} void nas::stop() { running = false; - write_ctxt_file(ctxt); + write_ctxt_file(ctxt, ctxt_base); } void nas::get_metrics(nas_metrics_t* m) @@ -724,8 +725,12 @@ int nas::apply_security_config(srsran::unique_byte_buffer_t& pdu, uint8_t sec_hd cipher_encrypt(pdu.get()); } if (sec_hdr_type >= LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY) { - integrity_generate( - &k_nas_int[16], ctxt.tx_count, SECURITY_DIRECTION_UPLINK, &pdu->msg[5], pdu->N_bytes - 5, &pdu->msg[1]); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[5], + pdu->N_bytes - 5, + &pdu->msg[1]); } } else { logger.error("Invalid PDU size %d", pdu->N_bytes); @@ -1019,7 +1024,7 @@ void nas::parse_attach_accept(uint32_t lcid, unique_byte_buffer_t pdu) enter_emm_deregistered(emm_state_t::deregistered_substate_t::plmn_search); } - ctxt.rx_count++; + ctxt_base.rx_count++; } void nas::parse_attach_reject(uint32_t lcid, unique_byte_buffer_t pdu) @@ -1074,7 +1079,7 @@ void nas::parse_authentication_request(uint32_t lcid, unique_byte_buffer_t pdu, logger.info("Received Authentication Request"); liblte_mme_unpack_authentication_request_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &auth_req); - ctxt.rx_count++; + ctxt_base.rx_count++; // Generate authentication response using RAND, AUTN & KSI-ASME uint16 mcc, mnc; @@ -1130,7 +1135,7 @@ void nas::parse_identity_request(unique_byte_buffer_t pdu, const uint8_t sec_hdr liblte_mme_unpack_identity_request_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &id_req); logger.info("Received Identity Request. ID type: %d", id_req.id_type); - ctxt.rx_count++; + ctxt_base.rx_count++; // do not respond if request is not protected (TS 24.301 Sec. 4.4.4.2) if (sec_hdr_type >= LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY || @@ -1187,27 +1192,31 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu) // Reset counters (as per 24.301 5.4.3.2), only needed for initial security mode command if (auth_request) { - ctxt.rx_count = 0; - ctxt.tx_count = 0; + ctxt_base.rx_count = 0; + ctxt_base.tx_count = 0; auth_request = false; } - ctxt.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eea; - ctxt.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eia; + ctxt_base.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eea; + ctxt_base.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM)sec_mode_cmd.selected_nas_sec_algs.type_of_eia; // Check capabilities - if (!eea_caps[ctxt.cipher_algo] || !eia_caps[ctxt.integ_algo]) { + if (!eea_caps[ctxt_base.cipher_algo] || !eia_caps[ctxt_base.integ_algo]) { logger.warning("Sending Security Mode Reject due to security capabilities mismatch"); send_security_mode_reject(LIBLTE_MME_EMM_CAUSE_UE_SECURITY_CAPABILITIES_MISMATCH); return; } // Generate NAS keys - usim->generate_nas_keys(ctxt.k_asme, k_nas_enc, k_nas_int, ctxt.cipher_algo, ctxt.integ_algo); - logger.info(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); - logger.info(k_nas_int, 32, "NAS integrity key - k_nas_int"); + usim->generate_nas_keys( + ctxt.k_asme, ctxt_base.k_nas_enc, ctxt_base.k_nas_int, ctxt_base.cipher_algo, ctxt_base.integ_algo); + logger.info(ctxt_base.k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + logger.info(ctxt_base.k_nas_int, 32, "NAS integrity key - k_nas_int"); - logger.debug("Generating integrity check. integ_algo:%d, count_dl:%d, lcid:%d", ctxt.integ_algo, ctxt.rx_count, lcid); + logger.debug("Generating integrity check. integ_algo:%d, count_dl:%d, lcid:%d", + ctxt_base.integ_algo, + ctxt_base.rx_count, + lcid); if (not integrity_check(pdu.get())) { logger.warning("Sending Security Mode Reject due to integrity check failure"); @@ -1215,7 +1224,7 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu) return; } - ctxt.rx_count++; + ctxt_base.rx_count++; LIBLTE_MME_SECURITY_MODE_COMPLETE_MSG_STRUCT sec_mode_comp = {}; if (sec_mode_cmd.imeisv_req_present && LIBLTE_MME_IMEISV_REQUESTED == sec_mode_cmd.imeisv_req) { @@ -1235,7 +1244,7 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu) // Pack and send response pdu->clear(); liblte_mme_pack_security_mode_complete_msg( - &sec_mode_comp, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + &sec_mode_comp, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); } @@ -1246,9 +1255,9 @@ void nas::parse_security_mode_command(uint32_t lcid, unique_byte_buffer_t pdu) } logger.info( - "Sending Security Mode Complete nas_current_ctxt.tx_count=%d, RB=%s", ctxt.tx_count, rrc->get_rb_name(lcid)); + "Sending Security Mode Complete ctxt_base.tx_count=%d, RB=%s", ctxt_base.tx_count, rrc->get_rb_name(lcid)); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; // switch security header for all following messages current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY_AND_CIPHERED; @@ -1287,7 +1296,7 @@ void nas::parse_esm_information_request(uint32_t lcid, unique_byte_buffer_t pdu) logger.info("ESM information request received for beaser=%d, transaction_id=%d", esm_info_req.eps_bearer_id, esm_info_req.proc_transaction_id); - ctxt.rx_count++; + ctxt_base.rx_count++; // send response send_esm_information_response(esm_info_req.proc_transaction_id); @@ -1300,14 +1309,14 @@ void nas::parse_emm_information(uint32_t lcid, unique_byte_buffer_t pdu) std::string str = emm_info_str(&emm_info); logger.info("Received EMM Information: %s", str.c_str()); srsran::console("%s\n", str.c_str()); - ctxt.rx_count++; + ctxt_base.rx_count++; } void nas::parse_detach_request(uint32_t lcid, unique_byte_buffer_t pdu) { LIBLTE_MME_DETACH_REQUEST_MSG_STRUCT detach_request; liblte_mme_unpack_detach_request_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &detach_request); - ctxt.rx_count++; + ctxt_base.rx_count++; logger.info("Received detach request (type=%d). NAS State: %s", detach_request.detach_type.type_of_detach, @@ -1348,7 +1357,7 @@ void nas::parse_activate_dedicated_eps_bearer_context_request(uint32_t lcid, uni request.linked_eps_bearer_id, request.proc_transaction_id); - ctxt.rx_count++; + ctxt_base.rx_count++; LIBLTE_MME_TRAFFIC_FLOW_TEMPLATE_STRUCT* tft = &request.tft; logger.info("Traffic Flow Template: TFT OP code 0x%x, Filter list size %d, Parameter list size %d", tft->tft_op_code, @@ -1399,7 +1408,7 @@ void nas::parse_deactivate_eps_bearer_context_request(unique_byte_buffer_t pdu) request.proc_transaction_id, request.esm_cause); - ctxt.rx_count++; + ctxt_base.rx_count++; // check if bearer exists if (eps_bearer.find(request.eps_bearer_id) == eps_bearer.end()) { @@ -1430,7 +1439,7 @@ void nas::parse_modify_eps_bearer_context_request(srsran::unique_byte_buffer_t p request.eps_bearer_id, request.proc_transaction_id); - ctxt.rx_count++; + ctxt_base.rx_count++; // check if bearer exists const auto it = eps_bearer.find(request.eps_bearer_id); @@ -1461,7 +1470,7 @@ void nas::parse_activate_test_mode(uint32_t lcid, unique_byte_buffer_t pdu) { logger.info("Received Activate test mode"); - ctxt.rx_count++; + ctxt_base.rx_count++; send_activate_test_mode_complete(); } @@ -1484,7 +1493,7 @@ void nas::parse_close_ue_test_loop(uint32_t lcid, unique_byte_buffer_t pdu) break; } - ctxt.rx_count++; + ctxt_base.rx_count++; send_close_ue_test_loop_complete(); } @@ -1493,7 +1502,7 @@ void nas::parse_emm_status(uint32_t lcid, unique_byte_buffer_t pdu) { LIBLTE_MME_EMM_STATUS_MSG_STRUCT emm_status; liblte_mme_unpack_emm_status_msg((LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), &emm_status); - ctxt.rx_count++; + ctxt_base.rx_count++; switch (emm_status.emm_cause) { case LIBLTE_MME_ESM_CAUSE_INVALID_EPS_BEARER_IDENTITY: @@ -1587,7 +1596,7 @@ void nas::gen_attach_request(srsran::unique_byte_buffer_t& msg) // According to Sec 4.4.5, the attach request is always unciphered, even if a context exists liblte_mme_pack_attach_request_msg( - &attach_req, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)msg.get()); + &attach_req, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)msg.get()); if (apply_security_config(msg, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY)) { logger.error("Error applying NAS security."); @@ -1607,8 +1616,8 @@ void nas::gen_attach_request(srsran::unique_byte_buffer_t& msg) } if (have_ctxt) { - set_k_enb_count(ctxt.tx_count); - ctxt.tx_count++; + set_k_enb_count(ctxt_base.tx_count); + ctxt_base.tx_count++; } // stop T3411 and T3402 @@ -1640,11 +1649,11 @@ void nas::gen_service_request(srsran::unique_byte_buffer_t& msg) msg->msg[0] = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4u) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); msg->N_bytes++; msg->msg[1] = (ctxt.ksi & 0x07u) << 5u; - msg->msg[1] |= ctxt.tx_count & 0x1Fu; + msg->msg[1] |= ctxt_base.tx_count & 0x1Fu; msg->N_bytes++; uint8_t mac[4]; - integrity_generate(&k_nas_int[16], ctxt.tx_count, SECURITY_DIRECTION_UPLINK, &msg->msg[0], 2, &mac[0]); + integrity_generate(&ctxt_base.k_nas_int[16], ctxt_base.tx_count, SECURITY_DIRECTION_UPLINK, &msg->msg[0], 2, &mac[0]); // Set the short MAC msg->msg[2] = mac[2]; msg->N_bytes++; @@ -1654,8 +1663,8 @@ void nas::gen_service_request(srsran::unique_byte_buffer_t& msg) if (pcap != nullptr) { pcap->write_nas(msg->msg, msg->N_bytes); } - set_k_enb_count(ctxt.tx_count); - ctxt.tx_count++; + set_k_enb_count(ctxt_base.tx_count); + ctxt_base.tx_count++; } void nas::gen_pdn_connectivity_request(LIBLTE_BYTE_MSG_STRUCT* msg) @@ -1759,8 +1768,10 @@ void nas::send_detach_request(bool switch_off) detach_request.nas_ksi.tsc_flag = LIBLTE_MME_TYPE_OF_SECURITY_CONTEXT_FLAG_NATIVE; detach_request.nas_ksi.nas_ksi = ctxt.ksi; logger.info("Sending detach request with GUTI"); // If sent as an Initial UE message, it cannot be ciphered - liblte_mme_pack_detach_request_msg( - &detach_request, LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + liblte_mme_pack_detach_request_msg(&detach_request, + LIBLTE_MME_SECURITY_HDR_TYPE_INTEGRITY, + ctxt_base.tx_count, + (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -1777,7 +1788,7 @@ void nas::send_detach_request(bool switch_off) usim->get_imsi_vec(detach_request.eps_mobile_id.imsi, 15); logger.info("Sending detach request with IMSI"); liblte_mme_pack_detach_request_msg( - &detach_request, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + &detach_request, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -1803,7 +1814,7 @@ void nas::send_detach_request(bool switch_off) } } - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_attach_complete(const uint8_t& transaction_id_, const uint8_t& eps_bearer_id) @@ -1824,7 +1835,7 @@ void nas::send_attach_complete(const uint8_t& transaction_id_, const uint8_t& ep return; } liblte_mme_pack_attach_complete_msg( - &attach_complete, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + &attach_complete, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); // Write NAS pcap if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -1840,7 +1851,7 @@ void nas::send_attach_complete(const uint8_t& transaction_id_, const uint8_t& ep logger.info("Sending Attach Complete"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_detach_accept() @@ -1853,7 +1864,8 @@ void nas::send_detach_accept() LIBLTE_MME_DETACH_ACCEPT_MSG_STRUCT detach_accept; bzero(&detach_accept, sizeof(detach_accept)); - liblte_mme_pack_detach_accept_msg(&detach_accept, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + liblte_mme_pack_detach_accept_msg( + &detach_accept, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -1883,7 +1895,7 @@ void nas::send_authentication_response(const uint8_t* res, const size_t res_len) } auth_res.res_len = res_len; liblte_mme_pack_authentication_response_msg( - &auth_res, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + &auth_res, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); if (pcap != nullptr) { pcap->write_nas(pdu->msg, pdu->N_bytes); @@ -1896,7 +1908,7 @@ void nas::send_authentication_response(const uint8_t* res, const size_t res_len) logger.info("Sending Authentication Response"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_authentication_failure(const uint8_t cause, const uint8_t* auth_fail_param) @@ -1955,7 +1967,8 @@ void nas::send_identity_response(const uint8 id_type) return; } - liblte_mme_pack_identity_response_msg(&id_resp, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); + liblte_mme_pack_identity_response_msg( + &id_resp, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()); // add security if needed if (apply_security_config(pdu, current_sec_hdr)) { @@ -1968,7 +1981,7 @@ void nas::send_identity_response(const uint8 id_type) } rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_service_request() @@ -1983,11 +1996,11 @@ void nas::send_service_request() msg->msg[0] = (LIBLTE_MME_SECURITY_HDR_TYPE_SERVICE_REQUEST << 4u) | (LIBLTE_MME_PD_EPS_MOBILITY_MANAGEMENT); msg->N_bytes++; msg->msg[1] = (ctxt.ksi & 0x07u) << 5u; - msg->msg[1] |= ctxt.tx_count & 0x1Fu; + msg->msg[1] |= ctxt_base.tx_count & 0x1Fu; msg->N_bytes++; uint8_t mac[4]; - integrity_generate(&k_nas_int[16], ctxt.tx_count, SECURITY_DIRECTION_UPLINK, &msg->msg[0], 2, &mac[0]); + integrity_generate(&ctxt_base.k_nas_int[16], ctxt_base.tx_count, SECURITY_DIRECTION_UPLINK, &msg->msg[0], 2, &mac[0]); // Set the short MAC msg->msg[2] = mac[2]; msg->N_bytes++; @@ -2000,7 +2013,7 @@ void nas::send_service_request() logger.info("Sending service request"); rrc->write_sdu(std::move(msg)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_esm_information_response(const uint8 proc_transaction_id) @@ -2100,7 +2113,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) } if (liblte_mme_pack_esm_information_response_msg( - &esm_info_resp, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { + &esm_info_resp, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { logger.error("Error packing ESM information response."); return; } @@ -2117,7 +2130,7 @@ void nas::send_esm_information_response(const uint8 proc_transaction_id) logger.info(pdu->msg, pdu->N_bytes, "Sending ESM information response"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; chap_id++; } @@ -2136,7 +2149,7 @@ void nas::send_activate_dedicated_eps_bearer_context_accept(const uint8_t& proc_ accept.proc_transaction_id = proc_transaction_id; if (liblte_mme_pack_activate_dedicated_eps_bearer_context_accept_msg( - &accept, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { + &accept, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { logger.error("Error packing Activate Dedicated EPS Bearer context accept."); return; } @@ -2157,7 +2170,7 @@ void nas::send_activate_dedicated_eps_bearer_context_accept(const uint8_t& proc_ accept.proc_transaction_id); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_deactivate_eps_bearer_context_accept(const uint8_t& proc_transaction_id, const uint8_t& eps_bearer_id) @@ -2174,7 +2187,7 @@ void nas::send_deactivate_eps_bearer_context_accept(const uint8_t& proc_transact accept.proc_transaction_id = proc_transaction_id; if (liblte_mme_pack_deactivate_eps_bearer_context_accept_msg( - &accept, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { + &accept, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { logger.error("Error packing Activate EPS Bearer context accept."); return; } @@ -2195,7 +2208,7 @@ void nas::send_deactivate_eps_bearer_context_accept(const uint8_t& proc_transact accept.proc_transaction_id); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_modify_eps_bearer_context_accept(const uint8_t& proc_transaction_id, const uint8_t& eps_bearer_id) @@ -2212,7 +2225,7 @@ void nas::send_modify_eps_bearer_context_accept(const uint8_t& proc_transaction_ accept.proc_transaction_id = proc_transaction_id; if (liblte_mme_pack_modify_eps_bearer_context_accept_msg( - &accept, current_sec_hdr, ctxt.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { + &accept, current_sec_hdr, ctxt_base.tx_count, (LIBLTE_BYTE_MSG_STRUCT*)pdu.get()) != LIBLTE_SUCCESS) { logger.error("Error packing Modify EPS Bearer context accept."); return; } @@ -2233,7 +2246,7 @@ void nas::send_modify_eps_bearer_context_accept(const uint8_t& proc_transaction_ accept.proc_transaction_id); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_activate_test_mode_complete() @@ -2245,7 +2258,7 @@ void nas::send_activate_test_mode_complete() } if (liblte_mme_pack_activate_test_mode_complete_msg( - (LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), current_sec_hdr, ctxt.tx_count)) { + (LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), current_sec_hdr, ctxt_base.tx_count)) { logger.error("Error packing activate test mode complete."); return; } @@ -2262,7 +2275,7 @@ void nas::send_activate_test_mode_complete() logger.info(pdu->msg, pdu->N_bytes, "Sending Activate test mode complete"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } void nas::send_close_ue_test_loop_complete() @@ -2274,7 +2287,7 @@ void nas::send_close_ue_test_loop_complete() } if (liblte_mme_pack_close_ue_test_loop_complete_msg( - (LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), current_sec_hdr, ctxt.tx_count)) { + (LIBLTE_BYTE_MSG_STRUCT*)pdu.get(), current_sec_hdr, ctxt_base.tx_count)) { logger.error("Error packing close UE test loop complete."); return; } @@ -2291,17 +2304,17 @@ void nas::send_close_ue_test_loop_complete() logger.info(pdu->msg, pdu->N_bytes, "Sending Close UE test loop complete"); rrc->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; } /******************************************************************************* * Security context persistence file ******************************************************************************/ -bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_) +bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_, nas_sec_base_ctxt* ctxt_base_) { std::ifstream file; - if (ctxt_ == nullptr) { + if (ctxt_ == nullptr || ctxt_base_ == nullptr) { return false; } @@ -2327,16 +2340,16 @@ bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_) if (!readvar(file, "mme_code=", &ctxt_->guti.mme_code)) { return false; } - if (!readvar(file, "tx_count=", &ctxt_->tx_count)) { + if (!readvar(file, "tx_count=", &ctxt_base_->tx_count)) { return false; } - if (!readvar(file, "rx_count=", &ctxt_->rx_count)) { + if (!readvar(file, "rx_count=", &ctxt_base_->rx_count)) { return false; } - if (!readvar(file, "int_alg=", &ctxt_->integ_algo)) { + if (!readvar(file, "int_alg=", &ctxt_base_->integ_algo)) { return false; } - if (!readvar(file, "enc_alg=", &ctxt_->cipher_algo)) { + if (!readvar(file, "enc_alg=", &ctxt_base_->cipher_algo)) { return false; } if (!readvar(file, "ksi=", &ctxt_->ksi)) { @@ -2359,10 +2372,10 @@ bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_) "ksi: %x, k_asme: %s, tx_count: %x, rx_count: %x, int_alg: %d, enc_alg: %d", ctxt_->ksi, hex_to_string(ctxt_->k_asme, 32).c_str(), - ctxt_->tx_count, - ctxt_->rx_count, - ctxt_->integ_algo, - ctxt_->cipher_algo); + ctxt_base_->tx_count, + ctxt_base_->rx_count, + ctxt_base_->integ_algo, + ctxt_base_->cipher_algo); have_guti = true; have_ctxt = true; @@ -2378,7 +2391,7 @@ bool nas::read_ctxt_file(nas_sec_ctxt* ctxt_) return false; } -bool nas::write_ctxt_file(nas_sec_ctxt ctxt_) +bool nas::write_ctxt_file(nas_sec_ctxt ctxt_, nas_sec_base_ctxt ctxt_base_) { if (!have_guti || !have_ctxt) { return false; @@ -2391,10 +2404,10 @@ bool nas::write_ctxt_file(nas_sec_ctxt ctxt_) file << "mnc=" << (int)ctxt_.guti.mnc << std::endl; file << "mme_group_id=" << (int)ctxt_.guti.mme_group_id << std::endl; file << "mme_code=" << (int)ctxt_.guti.mme_code << std::endl; - file << "tx_count=" << (int)ctxt_.tx_count << std::endl; - file << "rx_count=" << (int)ctxt_.rx_count << std::endl; - file << "int_alg=" << (int)ctxt_.integ_algo << std::endl; - file << "enc_alg=" << (int)ctxt_.cipher_algo << std::endl; + file << "tx_count=" << (int)ctxt_base_.tx_count << std::endl; + file << "rx_count=" << (int)ctxt_base_.rx_count << std::endl; + file << "int_alg=" << (int)ctxt_base_.integ_algo << std::endl; + file << "enc_alg=" << (int)ctxt_base_.cipher_algo << std::endl; file << "ksi=" << (int)ctxt_.ksi << std::endl; file << "k_asme=" << hex_to_string(ctxt_.k_asme, 32) << std::endl; @@ -2410,10 +2423,10 @@ bool nas::write_ctxt_file(nas_sec_ctxt ctxt_) "ksi: %x, k_asme: %s, tx_count: %x, rx_count: %x, int_alg: %d, enc_alg: %d", ctxt_.ksi, hex_to_string(ctxt_.k_asme, 32).c_str(), - ctxt_.tx_count, - ctxt_.rx_count, - ctxt_.integ_algo, - ctxt_.cipher_algo); + ctxt_base_.tx_count, + ctxt_base_.rx_count, + ctxt_base_.integ_algo, + ctxt_base_.cipher_algo); file.close(); return true; } diff --git a/srsue/src/stack/upper/nas_5g.cc b/srsue/src/stack/upper/nas_5g.cc index 265e0dade..4cac7beed 100644 --- a/srsue/src/stack/upper/nas_5g.cc +++ b/srsue/src/stack/upper/nas_5g.cc @@ -33,7 +33,6 @@ #include #include -#include #include #define MAC_5G_OFFSET 2 @@ -79,7 +78,7 @@ void nas_5g::stop() int nas_5g::init(usim_interface_nas* usim_, rrc_nr_interface_nas_5g* rrc_nr_, gw_interface_nas* gw_, - const nas_args_t& cfg_) + const nas_5g_args_t& cfg_) { usim = usim_; rrc_nr = rrc_nr_; @@ -144,7 +143,6 @@ void nas_5g::run_tti() case mm5g_state_t::state_t::registered: break; case mm5g_state_t::state_t::deregistered_initiated: - logger.debug("UE detaching..."); break; default: break; @@ -207,6 +205,9 @@ int nas_5g::write_pdu(srsran::unique_byte_buffer_t pdu) case msg_opts::options::registration_reject: handle_registration_reject(nas_msg.registration_reject()); break; + case msg_opts::options::authentication_reject: + handle_authentication_reject(nas_msg.authentication_reject()); + break; case msg_opts::options::authentication_request: handle_authentication_request(nas_msg.authentication_request()); break; @@ -231,6 +232,12 @@ int nas_5g::write_pdu(srsran::unique_byte_buffer_t pdu) case msg_opts::options::dl_nas_transport: handle_dl_nas_transport(nas_msg.dl_nas_transport()); break; + case msg_opts::options::deregistration_accept_ue_originating: + handle_deregistration_accept_ue_originating(nas_msg.deregistration_accept_ue_originating()); + break; + case msg_opts::options::configuration_update_command: + handle_configuration_update_command(nas_msg.configuration_update_command()); + break; default: logger.error( "Not handling NAS message type: %s (0x%02x)", nas_msg.hdr.message_type.to_string(), nas_msg.hdr.message_type); @@ -309,23 +316,30 @@ int nas_5g::send_registration_complete() return SRSRAN_ERROR; } - logger.info("Generating Registration Complete"); - nas_5gs_msg nas_msg; registration_complete_t& reg_comp = nas_msg.set_registration_complete(); + nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { logger.error("Failed to pack registration complete."); return SRSRAN_ERROR; } + cipher_encrypt(pdu.get()); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[SEQ_5G_OFFSET], + pdu->N_bytes - SEQ_5G_OFFSET, + &pdu->msg[MAC_5G_OFFSET]); + if (pcap != nullptr) { pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); } - logger.info("Sending Registration Complete"); rrc_nr->write_sdu(std::move(pdu)); - + ctxt_base.tx_count++; return SRSRAN_SUCCESS; } @@ -356,6 +370,7 @@ int nas_5g::send_authentication_response(const uint8_t res[16]) logger.info("Sending Authentication Response"); rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; return SRSRAN_SUCCESS; } @@ -408,7 +423,7 @@ int nas_5g::send_security_mode_complete(const srsran::nas_5g::security_mode_comm imeisv.imeisv[14] = ue_svn_oct1; imeisv.imeisv[15] = ue_svn_oct2; } - + // TODO: Save TMSI registration_request_t& modified_registration_request = initial_registration_request_stored.registration_request(); modified_registration_request.capability_5gmm_present = true; modified_registration_request.requested_nssai_present = true; @@ -433,7 +448,7 @@ int nas_5g::send_security_mode_complete(const srsran::nas_5g::security_mode_comm nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered_with_new_5G_nas_context; - nas_msg.hdr.sequence_number = ctxt.tx_count; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { logger.error("Failed to pack security mode complete"); @@ -441,8 +456,8 @@ int nas_5g::send_security_mode_complete(const srsran::nas_5g::security_mode_comm } cipher_encrypt(pdu.get()); - integrity_generate(&k_nas_int[16], - ctxt.tx_count, + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, SECURITY_DIRECTION_UPLINK, &pdu->msg[SEQ_5G_OFFSET], pdu->N_bytes - SEQ_5G_OFFSET, @@ -454,13 +469,12 @@ int nas_5g::send_security_mode_complete(const srsran::nas_5g::security_mode_comm logger.info("Sending Security Mode Complete"); rrc_nr->write_sdu(std::move(pdu)); - ctxt.tx_count++; + ctxt_base.tx_count++; return SRSRAN_SUCCESS; } -int nas_5g::send_authentication_failure(const cause_5gmm_t::cause_5gmm_type_::options cause, - const uint8_t* auth_fail_param) +int nas_5g::send_authentication_failure(const cause_5gmm_t::cause_5gmm_type_::options cause, const uint8_t res[16]) { unique_byte_buffer_t pdu = srsran::make_byte_buffer(); if (!pdu) { @@ -470,6 +484,13 @@ int nas_5g::send_authentication_failure(const cause_5gmm_t::cause_5gmm_type_::op nas_5gs_msg nas_msg; authentication_failure_t& auth_fail = nas_msg.set_authentication_failure(); + auth_fail.cause_5gmm.cause_5gmm = cause; + + if (cause == cause_5gmm_t::cause_5gmm_type::synch_failure) { + auth_fail.authentication_failure_parameter_present = true; + auth_fail.authentication_failure_parameter.auth_failure.resize(14); + memcpy(auth_fail.authentication_failure_parameter.auth_failure.data(), res, 14); + } if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { logger.error("Failed to pack authentication failure."); @@ -480,7 +501,6 @@ int nas_5g::send_authentication_failure(const cause_5gmm_t::cause_5gmm_type_::op pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); } - logger.info("Sending Authentication Failure"); rrc_nr->write_sdu(std::move(pdu)); return SRSRAN_SUCCESS; @@ -493,6 +513,7 @@ uint32_t nas_5g::allocate_next_proc_trans_id() i++; if (pdu_trans_id == false) { pdu_trans_id = true; + break; } } // TODO if Trans ID exhausted @@ -522,11 +543,12 @@ int nas_5g::send_pdu_session_establishment_request(uint32_t tran nas_5gs_msg nas_msg; nas_msg.hdr.pdu_session_identity = pdu_session_id; nas_msg.hdr.procedure_transaction_identity = transaction_identity; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; pdu_session_establishment_request_t& pdu_ses_est_req = nas_msg.set_pdu_session_establishment_request(); - pdu_ses_est_req.integrity_protection_maximum_data_rate.max_data_rate_upip_downlink = + pdu_ses_est_req.integrity_protection_maximum_data_rate.max_data_rate_upip_downlink.value = integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_downlink_type_::options::full_data_rate; - pdu_ses_est_req.integrity_protection_maximum_data_rate.max_data_rate_upip_uplink = + pdu_ses_est_req.integrity_protection_maximum_data_rate.max_data_rate_upip_uplink.value = integrity_protection_maximum_data_rate_t::max_data_rate_UPIP_uplink_type_::options::full_data_rate; pdu_ses_est_req.pdu_session_type_present = true; @@ -545,7 +567,7 @@ int nas_5g::send_pdu_session_establishment_request(uint32_t tran env_nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered; // TODO move that seq number setting to the security part - env_nas_msg.hdr.sequence_number = ctxt.tx_count; + env_nas_msg.hdr.sequence_number = ctxt_base.tx_count; ul_nas_transport_t& ul_nas_msg = env_nas_msg.set_ul_nas_transport(); ul_nas_msg.payload_container_type.payload_container_type.value = @@ -583,8 +605,8 @@ int nas_5g::send_pdu_session_establishment_request(uint32_t tran } cipher_encrypt(pdu.get()); - integrity_generate(&k_nas_int[16], - ctxt.tx_count, + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, SECURITY_DIRECTION_UPLINK, &pdu->msg[SEQ_5G_OFFSET], pdu->N_bytes - SEQ_5G_OFFSET, @@ -592,7 +614,154 @@ int nas_5g::send_pdu_session_establishment_request(uint32_t tran logger.info("Sending PDU Session Establishment Request in UL NAS transport."); rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; + + return SRSRAN_SUCCESS; +} + +int nas_5g::send_deregistration_request_ue_originating(bool switch_off) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + logger.info("Generating Deregistration Request (UE Originating)"); + + nas_5gs_msg nas_msg; + deregistration_request_ue_originating_t& deregistration_request = nas_msg.set_deregistration_request_ue_originating(); + + // Note 5.5.2.2.2 : AMF does not send a Deregistration Accept NAS message if De-registration type IE indicates "switch + // off" + if (switch_off) { + deregistration_request.de_registration_type.switch_off.value = + de_registration_type_t::switch_off_type_::options::switch_off; + state.set_deregistered(mm5g_state_t::deregistered_substate_t::null); + } else { + deregistration_request.de_registration_type.switch_off.value = + de_registration_type_t::switch_off_type_::options::normal_de_registration; + // In this case we need to wait for the response by the core + state.set_deregistered_initiated(); + } + + mobile_identity_5gs_t::suci_s& suci = deregistration_request.mobile_identity_5gs.set_suci(); + suci.supi_format = mobile_identity_5gs_t::suci_s::supi_format_type_::options::imsi; + usim->get_home_mcc_bytes(suci.mcc.data(), suci.mcc.size()); + usim->get_home_mnc_bytes(suci.mnc.data(), suci.mnc.size()); + suci.scheme_output.resize(5); + + deregistration_request.ng_ksi.nas_key_set_identifier.value = + key_set_identifier_t::nas_key_set_identifier_type_::options::no_key_is_available_or_reserved; + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack Deregistration Request (UE Originating)."); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + logger.info("Sending Deregistration Request (UE Originating)"); + rrc_nr->write_sdu(std::move(pdu)); + + reset_pdu_sessions(); + + // TODO: Delete / Reset context (ctxt & ctxt_5g) + + return SRSASN_SUCCESS; +} + +int nas_5g::send_identity_response(srsran::nas_5g::identity_type_5gs_t::identity_types_::options identity_type) +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + logger.info("Generating Identity Response"); + + nas_5gs_msg nas_msg; + identity_response_t& identity_response = nas_msg.set_identity_response(); + + switch (identity_type) { + case (identity_type_5gs_t::identity_types_::suci): { + srsran::nas_5g::mobile_identity_5gs_t::suci_s& suci = identity_response.mobile_identity.set_suci(); + suci.supi_format = mobile_identity_5gs_t::suci_s::supi_format_type_::options::imsi; + usim->get_home_mcc_bytes(suci.mcc.data(), suci.mcc.size()); + usim->get_home_mnc_bytes(suci.mnc.data(), suci.mnc.size()); + suci.scheme_output.resize(5); + usim->get_home_msin_bcd(suci.scheme_output.data(), 5); + } break; + case (identity_type_5gs_t::identity_types_::guti_5g): { + srsran::nas_5g::mobile_identity_5gs_t::guti_5g_s& guti = identity_response.mobile_identity.set_guti_5g(); + guti = guti_5g; + } break; + case (identity_type_5gs_t::identity_types_::imei): { + srsran::nas_5g::mobile_identity_5gs_t::imei_s& imei = identity_response.mobile_identity.set_imei(); + usim->get_imei_vec(imei.imei.data(), 15); + } break; + case (identity_type_5gs_t::identity_types_::imeisv): { + srsran::nas_5g::mobile_identity_5gs_t::imeisv_s& imeisv = identity_response.mobile_identity.set_imeisv(); + usim->get_imei_vec(imeisv.imeisv.data(), 15); + imeisv.imeisv[14] = ue_svn_oct1; + imeisv.imeisv[15] = ue_svn_oct2; + } break; + default: + logger.warning("Unhandled identity type for identity response"); + return SRSRAN_ERROR; + } + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack Identity Response."); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + rrc_nr->write_sdu(std::move(pdu)); + + return SRSRAN_SUCCESS; +} + +int nas_5g::send_configuration_update_complete() +{ + unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + if (!pdu) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + + logger.info("Generating Configuration Update Complete"); + + nas_5gs_msg nas_msg; + configuration_update_complete_t& config_update_complete = nas_msg.set_configuration_update_complete(); + nas_msg.hdr.security_header_type = nas_5gs_hdr::security_header_type_opts::integrity_protected_and_ciphered; + nas_msg.hdr.sequence_number = ctxt_base.tx_count; + + if (nas_msg.pack(pdu) != SRSASN_SUCCESS) { + logger.error("Failed to pack Identity Response."); + return SRSRAN_ERROR; + } + + if (pcap != nullptr) { + pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); + } + + cipher_encrypt(pdu.get()); + integrity_generate(&ctxt_base.k_nas_int[16], + ctxt_base.tx_count, + SECURITY_DIRECTION_UPLINK, + &pdu->msg[SEQ_5G_OFFSET], + pdu->N_bytes - SEQ_5G_OFFSET, + &pdu->msg[MAC_5G_OFFSET]); + rrc_nr->write_sdu(std::move(pdu)); + ctxt_base.tx_count++; return SRSRAN_SUCCESS; } @@ -627,6 +796,29 @@ int nas_5g::handle_registration_accept(registration_accept_t& registration_accep int nas_5g::handle_registration_reject(registration_reject_t& registration_reject) { logger.info("Handling Registration Reject"); + + state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); + + switch (registration_reject.cause_5gmm.cause_5gmm.value) { + case (cause_5gmm_t::cause_5gmm_type_::options::illegal_ue): + logger.error("Registration Reject: Illegal UE"); + break; + case (cause_5gmm_t::cause_5gmm_type_::options::plmn_not_allowed): + logger.error("Registration Reject: PLMN not allowed"); + break; + case (cause_5gmm_t::cause_5gmm_type_::options::ue_security_capabilities_mismatch): + logger.error("Registration Reject: UE security capabilities mismatch"); + break; + case (cause_5gmm_t::cause_5gmm_type_::options::mac_failure): + logger.error("Registration Reject: MAC Failure"); + break; + case (cause_5gmm_t::cause_5gmm_type_::options::maximum_number_of_pdu_sessions_reached_): + logger.error("Registration Reject: Maximum number of pdu sessions reached"); + break; + default: + logger.error("Unhandled Registration Reject cause"); + } + return SRSRAN_SUCCESS; } @@ -670,27 +862,38 @@ int nas_5g::handle_authentication_request(authentication_request_t& authenticati authentication_request.abba.abba_contents.size(), res_star, ctxt_5g.k_amf); + logger.info(ctxt_5g.k_amf, 32, "Generated k_amf:"); + if (auth_result == AUTH_OK) { logger.info("Network authentication successful"); send_authentication_response(res_star); logger.info(res_star, 16, "Generated res_star (%d):", 16); + } else if (auth_result == AUTH_FAILED) { + logger.error("Network authentication failure."); + send_authentication_failure(cause_5gmm_t::cause_5gmm_type::mac_failure, res_star); } else if (auth_result == AUTH_SYNCH_FAILURE) { logger.error("Network authentication synchronization failure."); - // send_authentication_failure(LIBLTE_MME_EMM_CAUSE_SYNCH_FAILURE, res); + send_authentication_failure(cause_5gmm_t::cause_5gmm_type::synch_failure, res_star); } else { - logger.warning("Network authentication failure"); - srsran::console("Warning: Network authentication failure\n"); - // send_authentication_failure(LIBLTE_MME_EMM_CAUSE_MAC_FAILURE, nullptr); + logger.error("Unhandled authentication failure cause"); } return SRSRAN_SUCCESS; } +int nas_5g::handle_authentication_reject(srsran::nas_5g::authentication_reject_t& authentication_reject) +{ + logger.info("Handling Authentication Reject"); + state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); + return SRSRAN_SUCCESS; +} + int nas_5g::handle_identity_request(identity_request_t& identity_request) { logger.info("Handling Identity Request"); + send_identity_response(identity_request.identity_type.type_of_identity.value); return SRSRAN_SUCCESS; } @@ -710,36 +913,37 @@ int nas_5g::handle_security_mode_command(security_mode_command_t& security_m srsran::unique_byte_buffer_t pdu) { logger.info("Handling Security Mode Command"); - ctxt.cipher_algo = + ctxt_base.cipher_algo = (CIPHERING_ALGORITHM_ID_ENUM)security_mode_command.selected_nas_security_algorithms.ciphering_algorithm.value; - ctxt.integ_algo = (INTEGRITY_ALGORITHM_ID_ENUM) - security_mode_command.selected_nas_security_algorithms.integrity_protection_algorithm.value; + ctxt_base.integ_algo = + (INTEGRITY_ALGORITHM_ID_ENUM) + security_mode_command.selected_nas_security_algorithms.integrity_protection_algorithm.value; - // Check capabilities - // TODO: Check replayed sec capabilities - if (!ea5g_caps[ctxt.cipher_algo] || !ia5g_caps[ctxt.integ_algo]) { + // Check replayed ue security capabilities + if (!check_replayed_ue_security_capabilities(security_mode_command.replayed_ue_security_capabilities)) { logger.warning("Sending Security Mode Reject due to security capabilities mismatch"); - send_security_mode_reject(cause_5gmm_t::cause_5gmm_type_::options::ue_security_capabilities_mismatch); + send_security_mode_reject(cause_5gmm_t::cause_5gmm_type_::ue_security_capabilities_mismatch); return SRSRAN_ERROR; } initial_sec_command = false; // TODO if (initial_sec_command) { - ctxt.rx_count = 0; - ctxt.tx_count = 0; + ctxt_base.rx_count = 0; + ctxt_base.tx_count = 0; initial_sec_command = false; } // Generate NAS keys logger.debug(ctxt_5g.k_amf, 32, "K AMF"); - logger.debug("cipher_algo %d, integ_algo %d", ctxt.cipher_algo, ctxt.integ_algo); + logger.debug("cipher_algo %d, integ_algo %d", ctxt_base.cipher_algo, ctxt_base.integ_algo); - usim->generate_nas_keys_5g(ctxt_5g.k_amf, k_nas_enc, k_nas_int, ctxt.cipher_algo, ctxt.integ_algo); - logger.info(k_nas_enc, 32, "NAS encryption key - k_nas_enc"); - logger.info(k_nas_int, 32, "NAS integrity key - k_nas_int"); + usim->generate_nas_keys_5g( + ctxt_5g.k_amf, ctxt_base.k_nas_enc, ctxt_base.k_nas_int, ctxt_base.cipher_algo, ctxt_base.integ_algo); + logger.info(ctxt_base.k_nas_enc, 32, "NAS encryption key - k_nas_enc"); + logger.info(ctxt_base.k_nas_int, 32, "NAS integrity key - k_nas_int"); - logger.debug("Generating integrity check. integ_algo:%d, count_dl:%d", ctxt.integ_algo, ctxt.rx_count); + logger.debug("Generating integrity check. integ_algo:%d, count_dl:%d", ctxt_base.integ_algo, ctxt_base.rx_count); if (not integrity_check(pdu.get())) { logger.warning("Sending Security Mode Reject due to integrity check failure"); @@ -748,7 +952,7 @@ int nas_5g::handle_security_mode_command(security_mode_command_t& security_m } send_security_mode_complete(security_mode_command); - ctxt.rx_count++; + ctxt_base.rx_count++; return SRSRAN_SUCCESS; } @@ -806,6 +1010,26 @@ int nas_5g::handle_n1_sm_information(std::vector payload_container_cont return SRSRAN_SUCCESS; } +int nas_5g::handle_deregistration_accept_ue_originating( + srsran::nas_5g::deregistration_accept_ue_originating_t& deregistration_accept_ue_originating) +{ + logger.info("Received Deregistration Accept (UE Originating)"); + if (state.get_state() != mm5g_state_t::state_t::deregistered_initiated) { + logger.warning("Received deregistration accept while not in deregistered initiated state"); + } + + state.set_deregistered(mm5g_state_t::deregistered_substate_t::null); + return SRSASN_SUCCESS; +} + +int nas_5g::handle_configuration_update_command( + srsran::nas_5g::configuration_update_command_t& configuration_update_command) +{ + logger.info("Handling Configuration Update Command"); + send_configuration_update_complete(); + return SRSRAN_SUCCESS; +} + /******************************************************************************* * NAS Timers ******************************************************************************/ @@ -832,7 +1056,7 @@ int nas_5g::switch_on() int nas_5g::switch_off() { logger.info("Switching off"); - // TODO + send_deregistration_request_ue_originating(true); return SRSRAN_SUCCESS; } @@ -856,6 +1080,21 @@ int nas_5g::start_service_request() return SRSRAN_SUCCESS; } +int nas_5g::reset_pdu_sessions() +{ + for (auto pdu_session : pdu_sessions) { + pdu_session.established = false; + pdu_session.pdu_session_id = 0; + } + return SRSRAN_SUCCESS; +} + +void nas_5g::get_metrics(nas_5g_metrics_t& metrics) +{ + metrics.nof_active_pdu_sessions = num_of_est_pdu_sessions(); + metrics.state = state.get_state(); +} + /******************************************************************************* * Helpers ******************************************************************************/ @@ -913,8 +1152,38 @@ void nas_5g::fill_security_caps(srsran::nas_5g::ue_security_capability_t& sec_ca } } +bool nas_5g::check_replayed_ue_security_capabilities(srsran::nas_5g::ue_security_capability_t& caps) +{ + if (caps.ia0_5g_supported != ia5g_caps[0] || caps.ea0_5g_supported != ea5g_caps[0]) { + return false; + } + if (caps.ia1_128_5g_supported != ia5g_caps[1] || caps.ea1_128_5g_supported != ea5g_caps[1]) { + return false; + } + if (caps.ia2_128_5g_supported != ia5g_caps[2] || caps.ea2_128_5g_supported != ea5g_caps[2]) { + return false; + } + if (caps.ia3_128_5g_supported != ia5g_caps[3] || caps.ea3_128_5g_supported != ea5g_caps[3]) { + return false; + } + if (caps.ia4_5g_supported != ia5g_caps[4] || caps.ea4_5g_supported != ea5g_caps[4]) { + return false; + } + if (caps.ia5_5g_supported != ia5g_caps[5] || caps.ea5_5g_supported != ea5g_caps[5]) { + return false; + } + if (caps.ia6_5g_supported != ia5g_caps[6] || caps.ea6_5g_supported != ea5g_caps[6]) { + return false; + } + if (caps.ia7_5g_supported != ia5g_caps[7] || caps.ea7_5g_supported != ea5g_caps[7]) { + return false; + } + + return true; +} + /******************************************************************************* - * Helpers for Session Management + * Helpers for Session Management ******************************************************************************/ int nas_5g::trigger_pdu_session_est() @@ -939,6 +1208,17 @@ int nas_5g::init_pdu_sessions(std::vector pdu_session_cfgs) return SRSRAN_SUCCESS; } +uint32_t nas_5g::num_of_est_pdu_sessions() +{ + uint32_t i = 0; + for (auto pdu_session : pdu_sessions) { + if (pdu_session.established == true) { + i++; + } + } + return i; +} + int nas_5g::configure_pdu_session(uint16_t pdu_session_id) { for (auto pdu_session : pdu_sessions) { diff --git a/srsue/src/stack/upper/nas_base.cc b/srsue/src/stack/upper/nas_base.cc index 54bc171ad..d1ef68ac8 100644 --- a/srsue/src/stack/upper/nas_base.cc +++ b/srsue/src/stack/upper/nas_base.cc @@ -58,7 +58,7 @@ void nas_base::integrity_generate(uint8_t* key_128, uint32_t msg_len, uint8_t* mac) { - switch (ctxt.integ_algo) { + switch (ctxt_base.integ_algo) { case INTEGRITY_ALGORITHM_ID_EIA0: break; case INTEGRITY_ALGORITHM_ID_128_EIA1: @@ -89,8 +89,8 @@ bool nas_base::integrity_check(byte_buffer_t* pdu) uint8_t* mac = &pdu->msg[mac_offset]; // generate expected MAC - uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[seq_offset]; - integrity_generate(&k_nas_int[16], + uint32_t count_est = (ctxt_base.rx_count & 0x00FFFF00u) | pdu->msg[seq_offset]; + integrity_generate(&ctxt_base.k_nas_int[16], count_est, SECURITY_DIRECTION_DOWNLINK, &pdu->msg[seq_offset], @@ -124,9 +124,9 @@ bool nas_base::integrity_check(byte_buffer_t* pdu) mac[3]); // Updated local count (according to TS 24.301 Sec. 4.4.3.3) - if (count_est != ctxt.rx_count) { + if (count_est != ctxt_base.rx_count) { logger.info("Update local count to estimated count %d", count_est); - ctxt.rx_count = count_est; + ctxt_base.rx_count = count_est; } return true; } else { @@ -139,16 +139,16 @@ void nas_base::cipher_encrypt(byte_buffer_t* pdu) { byte_buffer_t pdu_tmp; - if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { - logger.debug("Encrypting PDU. count=%d", ctxt.tx_count); + if (ctxt_base.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { + logger.debug("Encrypting PDU. count=%d", ctxt_base.tx_count); } - switch (ctxt.cipher_algo) { + switch (ctxt_base.cipher_algo) { case CIPHERING_ALGORITHM_ID_EEA0: break; case CIPHERING_ALGORITHM_ID_128_EEA1: - security_128_eea1(&k_nas_enc[16], - ctxt.tx_count, + security_128_eea1(&ctxt_base.k_nas_enc[16], + ctxt_base.tx_count, bearer_id, SECURITY_DIRECTION_UPLINK, &pdu->msg[seq_offset + 1], @@ -157,8 +157,8 @@ void nas_base::cipher_encrypt(byte_buffer_t* pdu) memcpy(&pdu->msg[seq_offset + 1], &pdu_tmp.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); break; case CIPHERING_ALGORITHM_ID_128_EEA2: - security_128_eea2(&k_nas_enc[16], - ctxt.tx_count, + security_128_eea2(&ctxt_base.k_nas_enc[16], + ctxt_base.tx_count, bearer_id, SECURITY_DIRECTION_UPLINK, &pdu->msg[seq_offset + 1], @@ -167,8 +167,8 @@ void nas_base::cipher_encrypt(byte_buffer_t* pdu) memcpy(&pdu->msg[seq_offset + 1], &pdu_tmp.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); break; case CIPHERING_ALGORITHM_ID_128_EEA3: - security_128_eea3(&k_nas_enc[16], - ctxt.tx_count, + security_128_eea3(&ctxt_base.k_nas_enc[16], + ctxt_base.tx_count, bearer_id, SECURITY_DIRECTION_UPLINK, &pdu->msg[seq_offset + 1], @@ -186,16 +186,16 @@ void nas_base::cipher_decrypt(byte_buffer_t* pdu) { byte_buffer_t tmp_pdu; - uint32_t count_est = (ctxt.rx_count & 0x00FFFF00u) | pdu->msg[5]; - if (ctxt.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { - logger.debug("Decrypting PDU. Local: count=%d, Received: count=%d", ctxt.rx_count, count_est); + uint32_t count_est = (ctxt_base.rx_count & 0x00FFFF00u) | pdu->msg[5]; + if (ctxt_base.cipher_algo != CIPHERING_ALGORITHM_ID_EEA0) { + logger.debug("Decrypting PDU. Local: count=%d, Received: count=%d", ctxt_base.rx_count, count_est); } - switch (ctxt.cipher_algo) { + switch (ctxt_base.cipher_algo) { case CIPHERING_ALGORITHM_ID_EEA0: break; case CIPHERING_ALGORITHM_ID_128_EEA1: - security_128_eea1(&k_nas_enc[16], + security_128_eea1(&ctxt_base.k_nas_enc[16], count_est, bearer_id, SECURITY_DIRECTION_DOWNLINK, @@ -205,7 +205,7 @@ void nas_base::cipher_decrypt(byte_buffer_t* pdu) memcpy(&pdu->msg[seq_offset + 1], &tmp_pdu.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); break; case CIPHERING_ALGORITHM_ID_128_EEA2: - security_128_eea2(&k_nas_enc[16], + security_128_eea2(&ctxt_base.k_nas_enc[16], count_est, bearer_id, SECURITY_DIRECTION_DOWNLINK, @@ -216,7 +216,7 @@ void nas_base::cipher_decrypt(byte_buffer_t* pdu) memcpy(&pdu->msg[seq_offset + 1], &tmp_pdu.msg[seq_offset + 1], pdu->N_bytes - seq_offset + 1); break; case CIPHERING_ALGORITHM_ID_128_EEA3: - security_128_eea3(&k_nas_enc[16], + security_128_eea3(&ctxt_base.k_nas_enc[16], count_est, bearer_id, SECURITY_DIRECTION_DOWNLINK, diff --git a/srsue/src/stack/upper/pcsc_usim.cc b/srsue/src/stack/upper/pcsc_usim.cc index a8fa2fe0b..bb132011b 100644 --- a/srsue/src/stack/upper/pcsc_usim.cc +++ b/srsue/src/stack/upper/pcsc_usim.cc @@ -190,7 +190,7 @@ auth_result_t pcsc_usim::generate_authentication_response_5g(uint8_t* rand, case -2: logger.info("SCARD: USIM synchronization failure, AUTS generated"); logger.debug(auts, AKA_AUTS_LEN, "AUTS"); - memcpy(res, auts, AKA_AUTS_LEN); + memcpy(res_star, auts, AKA_AUTS_LEN); res_len = AKA_AUTS_LEN; return AUTH_SYNCH_FAILURE; default: @@ -222,7 +222,7 @@ auth_result_t pcsc_usim::generate_authentication_response_5g(uint8_t* rand, // Generate K_seaf security_generate_k_seaf(k_ausf, serving_network_name, k_seaf); logger.debug(k_seaf, 32, "K SEAF"); - // Generate K_seaf + // Generate K_amf security_generate_k_amf(k_ausf, imsi_str.c_str(), abba, abba_len, k_amf); logger.debug(k_amf, 32, "K AMF"); diff --git a/srsue/src/stack/upper/test/nas_5g_test.cc b/srsue/src/stack/upper/test/nas_5g_test.cc index 7b3e208b3..0c62f1b16 100644 --- a/srsue/src/stack/upper/test/nas_5g_test.cc +++ b/srsue/src/stack/upper/test/nas_5g_test.cc @@ -51,11 +51,18 @@ int amf_attach_request_test(srsran::nas_pcap* pcap) args.op = "63BFA50EE6523365FF14C1F45F88737D"; usim.init(&args); - nas_args_t nas_cfg; - nas_cfg.force_imsi_attach = true; - nas_cfg.apn_name = "test123"; - nas_cfg.ia5g = "0,1,2,3"; - nas_cfg.ea5g = "0,1,2,3"; + nas_5g_args_t nas_5g_cfg; + nas_5g_cfg.force_imsi_attach = true; + pdu_session_cfg_t pdu_session; + pdu_session.apn_name = "test123"; + + nas_5g_cfg.pdu_session_cfgs.push_back(pdu_session); + + nas_5g_cfg.eia = "0,1,2,3"; + nas_5g_cfg.eea = "0,1,2,3"; + + nas_5g_cfg.ia5g = "0,1,2,3"; + nas_5g_cfg.ea5g = "0,1,2,3"; test_stack_dummy stack(&pdcp_dummy); srsue::nas_5g nas_5g(srslog::fetch_basic_logger("NAS-5G"), &stack.task_sched); @@ -73,7 +80,7 @@ int amf_attach_request_test(srsran::nas_pcap* pcap) gw.init(gw_args, &stack); stack.init(&nas_5g); - nas_5g.init(&usim, &rrc_nr_dummy, &gw, nas_cfg); + nas_5g.init(&usim, &rrc_nr_dummy, &gw, nas_5g_cfg); rrc_nr_dummy.init(&nas_5g); // trigger test diff --git a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h index 0d248103f..729e62e98 100644 --- a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h +++ b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h @@ -22,6 +22,7 @@ #ifndef SRSUE_TTCN3_LTE_PHY_H #define SRSUE_TTCN3_LTE_PHY_H +#include "srsran/common/task_scheduler.h" #include "srsran/interfaces/ue_interfaces.h" #include "srsran/interfaces/ue_phy_interfaces.h" #include "srsue/hdr/phy/ue_lte_phy_base.h" diff --git a/test/phy/CMakeLists.txt b/test/phy/CMakeLists.txt index 4153f1df9..4869d31b2 100644 --- a/test/phy/CMakeLists.txt +++ b/test/phy/CMakeLists.txt @@ -22,6 +22,7 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) # gNb options set(NR_PHY_TEST_GNB_NOF_THREADS 1) set(NR_PHY_TEST_GNB_PHY_LOG_LEVEL "error") + set(NR_PHY_TEST_GNB_STACK_LOG_LEVEL "error") # UE options set(NR_PHY_TEST_UE_NOF_THREADS 1) @@ -29,9 +30,11 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) # Build common arguments set(NR_PHY_TEST_COMMON_ARGS - --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} - --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} - --ue.phy.log.level=${NR_PHY_TEST_UE_PHY_LOG_LEVEL}) + --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} + --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} + --ue.phy.log.level=${NR_PHY_TEST_UE_PHY_LOG_LEVEL} + --gnb.phy.log.level=${NR_PHY_TEST_GNB_PHY_LOG_LEVEL} + --gnb.stack.log.level=${NR_PHY_TEST_GNB_STACK_LOG_LEVEL}) add_executable(nr_phy_test nr_phy_test.cc) target_link_libraries(nr_phy_test @@ -47,71 +50,60 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) ${CMAKE_THREAD_LIBS_INIT} ${Boost_LIBRARIES} ${ATOMIC_LIBS}) - # For each supported bandwidth foreach (NR_PHY_TEST_BW "10MHz" "20MHz") - # For each supported frame structure - foreach (NR_PHY_TEST_DUPLEX "FDD" "6D+4U" "FR1.15-1") - set(NR_PHY_TEST_DURATION_MS 20) + # For dummy and real scheduler + foreach (NR_PHY_TEST_MAC_DUMMY "dummymac" "realmac") + # For each supported frame structure + foreach (NR_PHY_TEST_DUPLEX "FDD" "6D+4U" "FR1.15-1") + set(NR_PHY_TEST_DURATION_MS 50) + + # DL flooding only + foreach (NR_PHY_TEST_PDSCH "default" "ts38101/5.2-1") + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_MAC_DUMMY}_${NR_PHY_TEST_DUPLEX}_dl_${NR_PHY_TEST_PDSCH} nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX},pdsch=${NR_PHY_TEST_PDSCH} + --duration=${NR_PHY_TEST_DURATION_MS} + --gnb.stack.pdsch.slots=all + --gnb.stack.pdsch.start=0 # Start at RB 0 + --gnb.stack.pdsch.length=52 # Full 10 MHz BW + --gnb.stack.pdsch.mcs=27 # Maximum MCS + --gnb.stack.pusch.slots=none + --gnb.stack.use_dummy_mac=${NR_PHY_TEST_MAC_DUMMY} # Use real/dummy NR MAC + ${NR_PHY_TEST_COMMON_ARGS} + ) + endforeach () + + # UL flooding + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_MAC_DUMMY}_${NR_PHY_TEST_DUPLEX}_ul_only nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX} + --duration=${NR_PHY_TEST_DURATION_MS} + --gnb.stack.pdsch.slots=none + --gnb.stack.pusch.slots=all + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + --gnb.stack.use_dummy_mac=${NR_PHY_TEST_MAC_DUMMY} # Use real/dummy NR MAC + ${NR_PHY_TEST_COMMON_ARGS} + ) - # DL flooding only - foreach (NR_PHY_TEST_PDSCH "default" "ts38101/5.2-1") - add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_DUPLEX}_dl_${NR_PHY_TEST_PDSCH} nr_phy_test - --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX},pdsch=${NR_PHY_TEST_PDSCH} + # DL and UL flooding + add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_MAC_DUMMY}_${NR_PHY_TEST_DUPLEX}_bidir nr_phy_test + --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX} --duration=${NR_PHY_TEST_DURATION_MS} --gnb.stack.pdsch.slots=all --gnb.stack.pdsch.start=0 # Start at RB 0 --gnb.stack.pdsch.length=52 # Full 10 MHz BW - --gnb.stack.pdsch.mcs=27 # Maximum MCS - --gnb.stack.pusch.slots=none + --gnb.stack.pdsch.mcs=28 # Maximum MCS + --gnb.stack.pusch.slots=all + --gnb.stack.pusch.start=0 # Start at RB 0 + --gnb.stack.pusch.length=52 # Full 10 MHz BW + --gnb.stack.pusch.mcs=28 # Maximum MCS + --gnb.stack.use_dummy_mac=${NR_PHY_TEST_MAC_DUMMY} # Use real/dummy NR MAC ${NR_PHY_TEST_COMMON_ARGS} ) endforeach () - - # UL flooding - add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_DUPLEX}_ul_only nr_phy_test - --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX} - --duration=${NR_PHY_TEST_DURATION_MS} - --gnb.stack.pdsch.slots=none - --gnb.stack.pusch.slots=all - --gnb.stack.pusch.start=0 # Start at RB 0 - --gnb.stack.pusch.length=52 # Full 10 MHz BW - --gnb.stack.pusch.mcs=28 # Maximum MCS - ${NR_PHY_TEST_COMMON_ARGS} - ) - - # DL and UL flooding - add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_${NR_PHY_TEST_DUPLEX}_bidir nr_phy_test - --reference=carrier=${NR_PHY_TEST_BW},duplex=${NR_PHY_TEST_DUPLEX} - --duration=${NR_PHY_TEST_DURATION_MS} - --gnb.stack.pdsch.slots=all - --gnb.stack.pdsch.start=0 # Start at RB 0 - --gnb.stack.pdsch.length=52 # Full 10 MHz BW - --gnb.stack.pdsch.mcs=28 # Maximum MCS - --gnb.stack.pusch.slots=all - --gnb.stack.pusch.start=0 # Start at RB 0 - --gnb.stack.pusch.length=52 # Full 10 MHz BW - --gnb.stack.pusch.mcs=28 # Maximum MCS - ${NR_PHY_TEST_COMMON_ARGS} - ) endforeach () - add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_bidir_sched nr_phy_test - --reference=carrier=${NR_PHY_TEST_BW} - --duration=100 # 100 slots - --rnti=17921 # 0x4601 - --gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots - --gnb.stack.pdsch.start=0 # Start at RB 0 - --gnb.stack.pdsch.length=52 # Full 10 MHz BW - --gnb.stack.pdsch.mcs=28 # Maximum MCS - --gnb.stack.pusch.slots=6,7,8,9 # All possible UL slots - --gnb.stack.pusch.start=0 # Start at RB 0 - --gnb.stack.pusch.length=52 # Full 10 MHz BW - --gnb.stack.pusch.mcs=28 # Maximum MCS - --gnb.stack.use_dummy_sched=false # Use real NR scheduler - ${NR_PHY_TEST_COMMON_ARGS} - ) - # Test PRACH transmission and detection add_nr_test(nr_phy_test_${NR_PHY_TEST_BW}_prach_fdd nr_phy_test --reference=carrier=${NR_PHY_TEST_BW},duplex=FDD @@ -120,7 +112,6 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) --gnb.stack.pusch.slots=none # No PUSCH --gnb.phy.nof_threads=${NR_PHY_TEST_GNB_NOF_THREADS} --ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames - --ue.stack.prach.preamble=10 # Use preamble 10 --ue.phy.nof_threads=${NR_PHY_TEST_UE_NOF_THREADS} ) @@ -130,7 +121,6 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB) --gnb.stack.pdsch.slots=none # No PDSCH --gnb.stack.pusch.slots=none # No PUSCH --ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames - --ue.stack.prach.preamble=10 # Use preamble 10 ${NR_PHY_TEST_COMMON_ARGS} ) diff --git a/test/phy/dummy_gnb_stack.h b/test/phy/dummy_gnb_stack.h index 0f9b7e908..0aee76ef2 100644 --- a/test/phy/dummy_gnb_stack.h +++ b/test/phy/dummy_gnb_stack.h @@ -45,7 +45,9 @@ class gnb_dummy_stack : public srsenb::stack_interface_phy_nr public: struct prach_metrics_t { uint32_t count; - float avg_ta; + float avg_ta = 0.0f; + float min_ta = +INFINITY; + float max_ta = -INFINITY; }; struct pucch_metrics_t { float epre_db_avg = 0.0f; @@ -64,16 +66,19 @@ public: }; struct metrics_t { - std::map prach = {}; ///< PRACH metrics indexed with premable index - srsenb::mac_ue_metrics_t mac = {}; ///< MAC metrics - uint32_t sr_count = 0; ///< SR counter - pucch_metrics_t pucch = {}; + std::map prach = {}; ///< PRACH metrics indexed with premable index + srsenb::mac_ue_metrics_t mac = {}; ///< MAC metrics + uint32_t sr_count = 0; ///< SR counter + uint32_t cqi_count = 0; ///< CQI opportunity counter + uint32_t cqi_valid_count = 0; ///< Valid CQI counter + pucch_metrics_t pucch = {}; + pucch_metrics_t pusch = {}; }; private: - srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK"); - bool use_dummy_sched = true; - const uint16_t rnti = 0x1234; + srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK"); + bool use_dummy_mac = true; + const uint16_t rnti = 0x1234; struct { srsran::circular_array dci_location = {}; uint32_t mcs = 0; @@ -313,13 +318,30 @@ private: metrics.sr_count++; } + // Process CQI + for (uint32_t i = 0; i < cfg.nof_csi; i++) { + // Increment CQI opportunity + metrics.cqi_count++; + + // Skip if invalid or not supported CSI report + if (not value.valid or cfg.csi[i].cfg.quantity != SRSRAN_CSI_REPORT_QUANTITY_CRI_RI_PMI_CQI or + cfg.csi[i].cfg.freq_cfg != SRSRAN_CSI_REPORT_FREQ_WIDEBAND) { + continue; + } + + // Add statistics + metrics.mac.dl_cqi = + SRSRAN_VEC_SAFE_CMA(value.csi->wideband_cri_ri_pmi_cqi.cqi, metrics.mac.dl_cqi, metrics.cqi_count); + metrics.cqi_valid_count++; + } + return true; } public: struct args_t { - srsran::phy_cfg_nr_t phy_cfg; ///< Physical layer configuration - bool use_dummy_sched = true; ///< Use dummy or real NR scheduler + srsran::phy_cfg_nr_t phy_cfg; ///< Physical layer configuration + std::string use_dummy_mac = "dummymac"; ///< Use dummy or real NR scheduler bool wait_preamble = false; ///< Whether a UE is created automatically or the stack waits for a PRACH uint16_t rnti = 0x1234; ///< C-RNTI uint32_t ss_id = 1; ///< Search Space identifier @@ -339,7 +361,7 @@ public: rnti(args.rnti), phy_cfg(args.phy_cfg), ss_id(args.ss_id), - use_dummy_sched(args.use_dummy_sched), + use_dummy_mac(args.use_dummy_mac == "dummymac"), sched_logger(srslog::fetch_basic_logger("MAC")) { logger.set_level(srslog::str_to_basic_level(args.log_level)); @@ -353,19 +375,18 @@ public: srsenb::mac_nr_args_t mac_args{}; mac_args.sched_cfg.pdsch_enabled = args.pdsch.slots != "" and args.pdsch.slots != "none"; mac_args.sched_cfg.pusch_enabled = args.pusch.slots != "" and args.pusch.slots != "none"; + mac_args.sched_cfg.fixed_dl_mcs = args.pdsch.mcs; + mac_args.sched_cfg.fixed_ul_mcs = args.pusch.mcs; mac->init(mac_args, nullptr, nullptr, &rlc_obj, &rrc_obj); std::vector cells_cfg = srsenb::get_default_cells_cfg(1, phy_cfg); mac->cell_cfg(cells_cfg); // add UE to scheduler - if (not use_dummy_sched and not args.wait_preamble) { - mac->reserve_rnti(0); - + if (not use_dummy_mac and not args.wait_preamble) { srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1, phy_cfg); - ue_cfg.fixed_dl_mcs = args.pdsch.mcs; - ue_cfg.fixed_ul_mcs = args.pusch.mcs; ue_cfg.ue_bearers[4].direction = srsenb::mac_lc_ch_cfg_t::BOTH; - mac->ue_cfg(args.rnti, ue_cfg); + + mac->reserve_rnti(0, ue_cfg); } dl.mcs = args.pdsch.mcs; @@ -440,7 +461,8 @@ public: valid = true; } - ~gnb_dummy_stack() {} + ~gnb_dummy_stack() = default; + bool is_valid() const { return valid; } int slot_indication(const srsran_slot_cfg_t& slot_cfg) override { return 0; } @@ -450,7 +472,7 @@ public: logger.set_context(slot_cfg.idx); sched_logger.set_context(slot_cfg.idx); - if (not use_dummy_sched) { + if (not use_dummy_mac) { if (autofill_pdsch_bsr) { mac->rlc_buffer_state(rnti, 0, 10000, 0); } @@ -504,8 +526,18 @@ public: // Schedule SSB for (uint32_t ssb_idx = 0; ssb_idx < SRSRAN_SSB_NOF_CANDIDATES; ssb_idx++) { if (phy_cfg.ssb.position_in_burst[ssb_idx]) { + srsran_mib_nr_t mib = {}; + mib.ssb_idx = ssb_idx; + mib.sfn = slot_cfg.idx / SRSRAN_NSLOTS_PER_FRAME_NR(phy_cfg.carrier.scs); + mib.hrf = (slot_cfg.idx % SRSRAN_NSLOTS_PER_FRAME_NR(phy_cfg.carrier.scs)) >= + SRSRAN_NSLOTS_PER_FRAME_NR(phy_cfg.carrier.scs) / 2; + mac_interface_phy_nr::ssb_t ssb = {}; - ssb.pbch_msg.ssb_idx = (uint32_t)ssb_idx; + if (srsran_pbch_msg_nr_mib_pack(&mib, &ssb.pbch_msg) < SRSRAN_SUCCESS) { + logger.error("Error Packing MIB in slot %d", slot_cfg.idx); + continue; + } + ssb.pbch_msg.ssb_idx = (uint32_t)ssb_idx; dl_sched.ssb.push_back(ssb); } } @@ -518,7 +550,7 @@ public: logger.set_context(slot_cfg.idx); sched_logger.set_context(slot_cfg.idx); - if (not use_dummy_sched) { + if (not use_dummy_mac) { int ret = mac->get_ul_sched(slot_cfg, ul_sched); return ret; @@ -547,6 +579,9 @@ public: // Schedule PUSCH if (has_pusch) { + // If has PUSCH, no SR shall be received + uci_cfg.o_sr = 0; + // Put UCI configuration in PUSCH config if (not phy_cfg.get_pusch_uci_cfg(slot_cfg, uci_cfg, pusch.sch)) { logger.error("Error setting UCI configuration in PUSCH"); @@ -597,15 +632,14 @@ public: int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override { - if (not use_dummy_sched) { + if (not use_dummy_mac) { mac->pucch_info(slot_cfg, pucch_info); - return SRSRAN_SUCCESS; - } - - // Handle UCI data - if (not handle_uci_data(pucch_info.uci_data.cfg, pucch_info.uci_data.value)) { - logger.error("Error handling UCI data from PUCCH reception"); - return SRSRAN_ERROR; + } else { + // Handle UCI data + if (not handle_uci_data(pucch_info.uci_data.cfg, pucch_info.uci_data.value)) { + logger.error("Error handling UCI data from PUCCH reception"); + return SRSRAN_ERROR; + } } // Skip next steps if uci data is invalid @@ -634,49 +668,65 @@ public: int pusch_info(const srsran_slot_cfg_t& slot_cfg, pusch_info_t& pusch_info) override { - if (not use_dummy_sched) { + if (not use_dummy_mac) { mac->pusch_info(slot_cfg, pusch_info); - } + } else { + // Handle UCI data + if (not handle_uci_data(pusch_info.uci_cfg, pusch_info.pusch_data.uci)) { + logger.error("Error handling UCI data from PUCCH reception"); + return SRSRAN_ERROR; + } - // Handle UCI data - if (not handle_uci_data(pusch_info.uci_cfg, pusch_info.pusch_data.uci)) { - logger.error("Error handling UCI data from PUCCH reception"); - return SRSRAN_ERROR; + // Handle UL-SCH metrics + std::unique_lock lock(metrics_mutex); + if (not pusch_info.pusch_data.tb[0].crc) { + metrics.mac.rx_errors++; + } + metrics.mac.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs(); + metrics.mac.rx_pkts++; } - // Handle UL-SCH metrics - std::unique_lock lock(metrics_mutex); - if (not pusch_info.pusch_data.tb[0].crc) { - metrics.mac.rx_errors++; - } - metrics.mac.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs(); - metrics.mac.rx_pkts++; + // Handle PHY metrics + metrics.pusch.epre_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.epre_dB, metrics.pusch.epre_db_avg, metrics.pusch.count); + metrics.pusch.epre_db_min = SRSRAN_MIN(metrics.pusch.epre_db_min, pusch_info.csi.epre_dB); + metrics.pusch.epre_db_max = SRSRAN_MAX(metrics.pusch.epre_db_max, pusch_info.csi.epre_dB); + metrics.pusch.rsrp_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.rsrp_dB, metrics.pusch.rsrp_db_avg, metrics.pusch.count); + metrics.pusch.rsrp_db_min = SRSRAN_MIN(metrics.pusch.rsrp_db_min, pusch_info.csi.rsrp_dB); + metrics.pusch.rsrp_db_max = SRSRAN_MAX(metrics.pusch.rsrp_db_max, pusch_info.csi.rsrp_dB); + metrics.pusch.snr_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.snr_dB, metrics.pusch.snr_db_avg, metrics.pusch.count); + metrics.pusch.snr_db_min = SRSRAN_MIN(metrics.pusch.snr_db_min, pusch_info.csi.snr_dB); + metrics.pusch.snr_db_max = SRSRAN_MAX(metrics.pusch.snr_db_max, pusch_info.csi.snr_dB); + metrics.pusch.ta_us_avg = SRSRAN_VEC_CMA(pusch_info.csi.delay_us, metrics.pusch.ta_us_avg, metrics.pusch.count); + metrics.pusch.ta_us_min = SRSRAN_MIN(metrics.pusch.ta_us_min, pusch_info.csi.delay_us); + metrics.pusch.ta_us_max = SRSRAN_MAX(metrics.pusch.ta_us_max, pusch_info.csi.delay_us); + metrics.pusch.count++; return SRSRAN_SUCCESS; } void rach_detected(const rach_info_t& rach_info) override { - if (not use_dummy_sched) { + if (not use_dummy_mac) { mac->rach_detected(rach_info); task_sched.run_pending_tasks(); - - srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1, phy_cfg); - ue_cfg.fixed_dl_mcs = ue_cfg.fixed_dl_mcs; - ue_cfg.fixed_ul_mcs = ue_cfg.fixed_ul_mcs; - mac->ue_cfg(rnti, ue_cfg); } std::unique_lock lock(metrics_mutex); prach_metrics_t& prach_metrics = metrics.prach[rach_info.preamble]; prach_metrics.avg_ta = SRSRAN_VEC_SAFE_CMA((float)rach_info.time_adv, prach_metrics.avg_ta, prach_metrics.count); + prach_metrics.min_ta = SRSRAN_MIN((float)rach_info.time_adv, prach_metrics.min_ta); + prach_metrics.max_ta = SRSRAN_MAX((float)rach_info.time_adv, prach_metrics.max_ta); prach_metrics.count++; } metrics_t get_metrics() { std::unique_lock lock(metrics_mutex); - + if (not use_dummy_mac) { + srsenb::mac_metrics_t mac_metrics; + mac->get_metrics(mac_metrics); + metrics.mac = mac_metrics.ues[0]; + } return metrics; } }; diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h index 3cd10cd3b..dc648f692 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -38,6 +38,7 @@ public: private: std::mutex rnti_mutex; + srsran_random_t random_gen = srsran_random_init(0x1323); srsran_rnti_type_t dl_rnti_type = srsran_rnti_type_c; uint16_t rnti = 0; bool valid = false; @@ -45,6 +46,7 @@ private: uint32_t sr_count = 0; uint32_t prach_period = 0; uint32_t prach_preamble = 0; + bool prach_pending = false; metrics_t metrics = {}; srsue::phy_interface_stack_nr& phy; @@ -53,21 +55,16 @@ private: public: struct args_t { - uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions - uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable. - uint32_t prach_period = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable. - uint32_t prach_preamble = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable. + uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions + uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable. + uint32_t prach_period = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable. }; ue_dummy_stack(const args_t& args, srsue::phy_interface_stack_nr& phy_) : - rnti(args.rnti), - sr_period(args.sr_period), - prach_period(args.prach_period), - prach_preamble(args.prach_preamble), - phy(phy_) + rnti(args.rnti), sr_period(args.sr_period), prach_period(args.prach_period), phy(phy_) { valid = true; } - ~ue_dummy_stack() = default; + ~ue_dummy_stack() { srsran_random_free(random_gen); } void in_sync() override {} void out_of_sync() override {} void run_tti(const uint32_t tti) override @@ -76,9 +73,10 @@ public: if (prach_period != 0) { uint32_t slot_idx = tti % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz); uint32_t sfn = tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz); - if (slot_idx == 0 and sfn % prach_period == 0) { + if (not prach_pending and slot_idx == 0 and sfn % prach_period == 0) { + prach_preamble = srsran_random_uniform_int_dist(random_gen, 0, 63); phy.send_prach(0, prach_preamble, 0.0f, 0.0f); - metrics.prach[prach_preamble].count++; + prach_pending = true; } } } @@ -113,6 +111,8 @@ public: std::unique_lock lock(rnti_mutex); dl_rnti_type = srsran_rnti_type_ra; rnti = 1 + s_id + 14 * t_id + 14 * 80 * f_id + 14 * 80 * 8 * ul_carrier_id; + metrics.prach[prach_preamble].count++; + prach_pending = false; } bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) override { diff --git a/test/phy/nr_phy_test.cc b/test/phy/nr_phy_test.cc index dce1630d7..49f03750e 100644 --- a/test/phy/nr_phy_test.cc +++ b/test/phy/nr_phy_test.cc @@ -30,6 +30,15 @@ // shorten boost program options namespace namespace bpo = boost::program_options; +static double assert_sr_detection_min = 1.000; +static double assert_cqi_detection_min = 1.000; +static double assert_pusch_bler_max = 0.000; +static double assert_pdsch_bler_max = 0.000; +static double assert_prach_detection_min = 1.000; +static double assert_prach_ta_min = 0.000; +static double assert_prach_ta_max = 0.000; +static double assert_pucch_snr_min = 0.000; + test_bench::args_t::args_t(int argc, char** argv) { std::string reference_cfg_str = ""; @@ -38,6 +47,7 @@ test_bench::args_t::args_t(int argc, char** argv) bpo::options_description options_gnb_phy("gNb PHY related options"); bpo::options_description options_ue_stack("UE stack options"); bpo::options_description options_ue_phy("UE stack options"); + bpo::options_description options_assertion("Test assertions"); uint16_t rnti = 17921; @@ -46,11 +56,17 @@ test_bench::args_t::args_t(int argc, char** argv) // clang-format off options.add_options() - ("rnti", bpo::value(&rnti)->default_value(rnti), "UE RNTI") - ("duration", bpo::value(&durations_slots)->default_value(durations_slots), "Test duration in slots") - ("lib.log.level", bpo::value(&phy_lib_log_level)->default_value(phy_lib_log_level), "PHY librray log level") - ("reference", bpo::value(&reference_cfg_str)->default_value(reference_cfg_str), "Reference PHY configuration arguments") - ; + ("rnti", bpo::value(&rnti)->default_value(rnti), "UE RNTI") + ("duration", bpo::value(&durations_slots)->default_value(durations_slots), "Test duration in slots") + ("lib.log.level", bpo::value(&phy_lib_log_level)->default_value(phy_lib_log_level), "PHY librray log level") + ("reference", bpo::value(&reference_cfg_str)->default_value(reference_cfg_str), "Reference PHY configuration arguments") + ("dl_channel.awgn_enable", bpo::value(&dl_channel.awgn_enable)->default_value(dl_channel.awgn_enable), "DL Channel AWGN enable / disable") + ("dl_channel.awgn_snr", bpo::value(&dl_channel.awgn_snr_dB)->default_value(dl_channel.awgn_snr_dB), "DL Channel AWGN SNR in dB") + ("ul_channel.awgn_enable", bpo::value(&ul_channel.awgn_enable)->default_value(ul_channel.awgn_enable), "UL Channel AWGN enable / disable") + ("ul_channel.awgn_snr", bpo::value(&ul_channel.awgn_snr_dB)->default_value(ul_channel.awgn_snr_dB), "UL Channel AWGN SNR in dB") + ("ul_channel.signal_power_dBfs", bpo::value(&ul_channel.awgn_signal_power_dBfs)->default_value(ul_channel.awgn_signal_power_dBfs), "UL Channel expected signal power") + ("channel.cfo", bpo::value(&ul_channel.hst_fd_hz)->default_value(0), "Channel HST Doppler frequency") +; options_gnb_stack.add_options() ("gnb.stack.pdcch.aggregation_level", bpo::value(&gnb_stack.pdcch_aggregation_level)->default_value(gnb_stack.pdcch_aggregation_level), "PDCCH aggregation level") @@ -65,7 +81,7 @@ test_bench::args_t::args_t(int argc, char** argv) ("gnb.stack.pusch.slots", bpo::value(&gnb_stack.pusch.slots)->default_value(gnb_stack.pusch.slots), "Slots enabled for PUSCH") ("gnb.stack.pusch.mcs", bpo::value(&gnb_stack.pusch.mcs)->default_value(gnb_stack.pusch.mcs), "PUSCH scheduling modulation code scheme") ("gnb.stack.log.level", bpo::value(&gnb_stack.log_level)->default_value(gnb_stack.log_level), "Stack log level") - ("gnb.stack.use_dummy_sched", bpo::value(&gnb_stack.use_dummy_sched)->default_value(true), "Use dummy or real NR scheduler") + ("gnb.stack.use_dummy_mac", bpo::value(&gnb_stack.use_dummy_mac)->default_value("dummymac"), "Use dummy or real NR scheduler (dummymac or realmac)") ; options_gnb_phy.add_options() @@ -73,7 +89,7 @@ test_bench::args_t::args_t(int argc, char** argv) ("gnb.phy.log.level", bpo::value(&gnb_phy.log.phy_level)->default_value("warning"), "gNb PHY log level") ("gnb.phy.log.hex_limit", bpo::value(&gnb_phy.log.phy_hex_limit)->default_value(0), "gNb PHY log hex limit") ("gnb.phy.log.id_preamble", bpo::value(&gnb_phy.log.id_preamble)->default_value("GNB/"), "gNb PHY log ID preamble") - ("gnb.phy.pusch.max_iter", bpo::value(&gnb_phy.pusch_max_nof_iter)->default_value(10), "PUSCH LDPC max number of iterations") + ("gnb.phy.pusch.max_iter", bpo::value(&gnb_phy.pusch_max_its)->default_value(10), "PUSCH LDPC max number of iterations") ; options_ue_phy.add_options() @@ -86,9 +102,18 @@ test_bench::args_t::args_t(int argc, char** argv) options_ue_stack.add_options() ("ue.stack.sr.period", bpo::value(&ue_stack.sr_period)->default_value(ue_stack.sr_period), "SR period in number of opportunities. Set 0 to disable and 1 for all.") ("ue.stack.prach.period", bpo::value(&ue_stack.prach_period)->default_value(ue_stack.prach_period), "PRACH period in SFN. Set 0 to disable and 1 for all.") - ("ue.stack.prach.preamble", bpo::value(&ue_stack.prach_preamble)->default_value(ue_stack.prach_preamble), "PRACH preamble. Set 0 to disable and 1 for all.") ; + options_assertion.add_options() + ("assert.sr.detection.min", bpo::value(&assert_sr_detection_min)->default_value(assert_sr_detection_min), "Scheduling request minimum detection threshold") + ("assert.cqi.detection.min", bpo::value(&assert_cqi_detection_min)->default_value(assert_cqi_detection_min), "CQI report minimum detection threshold") + ("assert.pusch.bler.max", bpo::value(&assert_pusch_bler_max)->default_value(assert_pusch_bler_max), "PUSCH maximum BLER threshold") + ("assert.pdsch.bler.max", bpo::value(&assert_pdsch_bler_max)->default_value(assert_pdsch_bler_max), "PDSCH maximum BLER threshold") + ("assert.prach.ta.min", bpo::value(&assert_prach_ta_min)->default_value(assert_prach_ta_min), "PRACH estimated TA minimum value threshold") + ("assert.prach.ta.max", bpo::value(&assert_prach_ta_max)->default_value(assert_prach_ta_max), "PRACH estimated TA maximum value threshold") + ("assert.pucch.snr.min", bpo::value(&assert_pucch_snr_min)->default_value(assert_pucch_snr_min), "PUCCH DMRS minimum SNR allowed threshold") + ; + options.add(options_gnb_stack).add(options_gnb_phy).add(options_ue_stack).add(options_ue_phy).add_options() ("help", "Show this message") ; @@ -98,6 +123,11 @@ test_bench::args_t::args_t(int argc, char** argv) try { bpo::store(bpo::command_line_parser(argc, argv).options(options).run(), vm); bpo::notify(vm); + + // Apply the High Speed Train args to the DL channel as well + ul_channel.hst_enable = std::isnormal(ul_channel.hst_fd_hz); + dl_channel.hst_enable = ul_channel.hst_enable; + dl_channel.hst_fd_hz = ul_channel.hst_fd_hz; } catch (bpo::error& e) { std::cerr << e.what() << std::endl; return; @@ -113,6 +143,12 @@ test_bench::args_t::args_t(int argc, char** argv) // Load default reference configuration phy_cfg = srsran::phy_cfg_nr_default_t(srsran::phy_cfg_nr_default_t::reference_cfg_t(reference_cfg_str)); + // Calulate the DL signal power from the number of PRBs + dl_channel.awgn_signal_power_dBfs = srsran_gnb_dl_get_maximum_signal_power_dBfs(phy_cfg.carrier.nof_prb); + + // Reverses the Doppler shift for the UL + ul_channel.hst_init_time_s = 0.5 * dl_channel.hst_period_s; + // Calculate sampling rate in Hz srate_hz = (double)(srsran_min_symbol_sz_rb(phy_cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs)); @@ -126,7 +162,7 @@ test_bench::args_t::args_t(int argc, char** argv) gnb_stack.rnti = rnti; gnb_stack.phy_cfg = phy_cfg; - gnb_stack.wait_preamble = ue_stack.prach_preamble > 0; + gnb_stack.wait_preamble = ue_stack.prach_period > 0; if (gnb_stack.pdsch.rb_length == 0) { gnb_stack.pdsch.rb_length = phy_cfg.carrier.nof_prb; @@ -170,12 +206,97 @@ int main(int argc, char** argv) srslog::flush(); // Retrieve MAC metrics - test_bench::metrics_t metrics = tb.get_gnb_metrics(); + test_bench::metrics_t metrics = tb.get_metrics(); + + // Print PRACH + double prach_detection = 0.0; + double prach_ta = 0.0; + uint32_t prach_tx_count = 0; + uint32_t prach_rx_count = 0; + if (metrics.ue_stack.prach.size() > 0) { + srsran::console("PRACH:\n"); + srsran::console( + " +------------+------------+------------+------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %10s | %10s | %10s | %10s | %10s | %10s |\n", + "Preamble", + "Transmit'd", + "Received", + "Detection", + "Avg TA", + "Min TA", + "Max TA"); + srsran::console( + " +------------+------------+------------+------------+------------+------------+------------+\n"); + + for (const auto& p : metrics.ue_stack.prach) { + // Ensure the detected count matches with transmission + // TESTASSERT(metrics.gnb_stack.prach.count(p.first)); + // TESTASSERT(metrics.gnb_stack.prach[p.first].count == p.second.count); + TESTASSERT(p.second.count != 0); + prach_tx_count += p.second.count; + + gnb_dummy_stack::prach_metrics_t gnb_prach = {}; + if (metrics.gnb_stack.prach.count(p.first) > 0) { + gnb_prach = metrics.gnb_stack.prach[p.first]; + prach_ta = SRSRAN_VEC_SAFE_CMA(gnb_prach.avg_ta, prach_ta, prach_rx_count); + } else { + gnb_prach.avg_ta = NAN; + gnb_prach.min_ta = NAN; + gnb_prach.max_ta = NAN; + } + prach_rx_count += gnb_prach.count; + + double detection = (double)gnb_prach.count / (double)p.second.count; + + srsran::console(" | %10d | %10d | %10d | %10.3f | %10.1f | %10.1f | %10.1f |\n", + p.first, + p.second.count, + gnb_prach.count, + detection, + gnb_prach.avg_ta, + gnb_prach.min_ta, + gnb_prach.max_ta); + } + srsran::console( + " +------------+------------+------------+------------+------------+------------+------------+\n"); + } + if (prach_tx_count > 0) { + prach_detection = (double)prach_rx_count / (double)prach_tx_count; + } + + // Print PUCCH + if (metrics.gnb_stack.pucch.count > 0) { + srsran::console("PUCCH DMRS Receiver metrics:\n"); + srsran::console(" +------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %10s | %10s | %10s |\n", "Measure", "Average", "Min", "Max"); + srsran::console(" +------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "EPRE (dB)", + metrics.gnb_stack.pucch.epre_db_avg, + metrics.gnb_stack.pucch.epre_db_min, + metrics.gnb_stack.pucch.epre_db_min); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "RSRP (dB)", + metrics.gnb_stack.pucch.rsrp_db_avg, + metrics.gnb_stack.pucch.rsrp_db_min, + metrics.gnb_stack.pucch.rsrp_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "SINR (dB)", + metrics.gnb_stack.pucch.snr_db_avg, + metrics.gnb_stack.pucch.snr_db_min, + metrics.gnb_stack.pucch.snr_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "TA (us)", + metrics.gnb_stack.pucch.ta_us_avg, + metrics.gnb_stack.pucch.ta_us_min, + metrics.gnb_stack.pucch.ta_us_max); + srsran::console(" +------------+------------+------------+------------+\n"); + } // Print PDSCH metrics if scheduled + double pdsch_bler = 0.0; if (metrics.gnb_stack.mac.tx_pkts > 0) { - float pdsch_bler = 0.0f; - pdsch_bler = (float)metrics.gnb_stack.mac.tx_errors / (float)metrics.gnb_stack.mac.tx_pkts; + pdsch_bler = (double)metrics.gnb_stack.mac.tx_errors / (double)metrics.gnb_stack.mac.tx_pkts; float pdsch_shed_rate = 0.0f; pdsch_shed_rate = (float)metrics.gnb_stack.mac.tx_brate / (float)metrics.gnb_stack.mac.tx_pkts / 1000.0f; @@ -190,10 +311,10 @@ int main(int argc, char** argv) } // Print PUSCH metrics if scheduled + double pusch_bler = 0.0; if (metrics.gnb_stack.mac.rx_pkts > 0) { - float pusch_bler = 0.0f; if (metrics.gnb_stack.mac.rx_pkts != 0) { - pusch_bler = (float)metrics.gnb_stack.mac.rx_errors / (float)metrics.gnb_stack.mac.rx_pkts; + pusch_bler = (double)metrics.gnb_stack.mac.rx_errors / (double)metrics.gnb_stack.mac.rx_pkts; } float pusch_shed_rate = 0.0f; @@ -210,83 +331,96 @@ int main(int argc, char** argv) srsran::console("\n"); } - // Print PRACH - if (metrics.ue_stack.prach.size() > 0) { - srsran::console("PRACH:\n"); - srsran::console(" UE transmitted:\n"); - srsran::console(" +------------+------------+\n"); - srsran::console(" | %10s | %10s |\n", "preamble", "count"); - srsran::console(" +------------+------------+\n"); - - for (const auto& p : metrics.ue_stack.prach) { - srsran::console(" | %10d | %10d |\n", p.first, p.second.count); - - // Ensure the detected count matches with transmission - TESTASSERT(metrics.gnb_stack.prach.count(p.first)); - TESTASSERT(metrics.gnb_stack.prach[p.first].count == p.second.count); - } - srsran::console(" +------------+------------+\n\n"); - - srsran::console(" GNB detected:\n"); - srsran::console(" +------------+------------+------------+\n"); - srsran::console(" | %10s | %10s | %10s |\n", "preamble", "count", "avg TA"); - srsran::console(" +------------+------------+------------+\n"); - - for (const auto& p : metrics.gnb_stack.prach) { - srsran::console(" | %10d | %10d | %10.1f |\n", p.first, p.second.count, p.second.avg_ta); - - // Ensure all detected preambles were transmitted - TESTASSERT(metrics.ue_stack.prach.count(p.first) > 0); - } - srsran::console(" +------------+------------+------------+\n\n"); - } - - // Print PUCCH - if (metrics.gnb_stack.pucch.count > 0) { - srsran::console("PUCCH DMRS Receiver metrics:\n"); + // Print PUSCH + if (metrics.gnb_stack.pusch.count > 0) { + srsran::console("PUSCH DMRS Receiver metrics:\n"); srsran::console(" +------------+------------+------------+------------+\n"); srsran::console(" | %10s | %10s | %10s | %10s |\n", "Measure", "Average", "Min", "Max"); srsran::console(" +------------+------------+------------+------------+\n"); srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", "EPRE (dB)", - metrics.gnb_stack.pucch.epre_db_avg, - metrics.gnb_stack.pucch.epre_db_min, - metrics.gnb_stack.pucch.epre_db_min); + metrics.gnb_stack.pusch.epre_db_avg, + metrics.gnb_stack.pusch.epre_db_min, + metrics.gnb_stack.pusch.epre_db_min); srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", "RSRP (dB)", - metrics.gnb_stack.pucch.rsrp_db_avg, - metrics.gnb_stack.pucch.rsrp_db_min, - metrics.gnb_stack.pucch.rsrp_db_max); + metrics.gnb_stack.pusch.rsrp_db_avg, + metrics.gnb_stack.pusch.rsrp_db_min, + metrics.gnb_stack.pusch.rsrp_db_max); srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", "SINR (dB)", - metrics.gnb_stack.pucch.snr_db_avg, - metrics.gnb_stack.pucch.snr_db_min, - metrics.gnb_stack.pucch.snr_db_max); + metrics.gnb_stack.pusch.snr_db_avg, + metrics.gnb_stack.pusch.snr_db_min, + metrics.gnb_stack.pusch.snr_db_max); srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", "TA (us)", - metrics.gnb_stack.pucch.ta_us_avg, - metrics.gnb_stack.pucch.ta_us_min, - metrics.gnb_stack.pucch.ta_us_max); + metrics.gnb_stack.pusch.ta_us_avg, + metrics.gnb_stack.pusch.ta_us_min, + metrics.gnb_stack.pusch.ta_us_max); srsran::console(" +------------+------------+------------+------------+\n"); } + srsran::console("UCI stats:\n"); + srsran::console(" +------------+------------+------------+------------+------------+\n"); + srsran::console( + " | %10s | %10s | %10s | %10s | %10s |\n", "Field", "Transmit'd", "Received", "Detection", "Avg. Val."); + srsran::console(" +------------+------------+------------+------------+------------+\n"); + // Print SR + double sr_detection = 0.0; if (metrics.ue_stack.sr_count > 0) { - srsran::console("SR:\n"); - srsran::console(" +------------+------------+------------+\n"); - srsran::console(" | %10s | %10s | %10s |\n", "Transmit'd", "Received", "Detection"); - srsran::console(" +------------+------------+------------+\n"); - srsran::console(" | %10d | %10d | %10.5f |\n", + sr_detection = (double)metrics.gnb_stack.sr_count / (double)metrics.ue_stack.sr_count; + srsran::console(" | %10s | %10d | %10d | %10.5f | %10s |\n", + "SR", metrics.ue_stack.sr_count, metrics.gnb_stack.sr_count, - (double)metrics.gnb_stack.sr_count / (double)metrics.ue_stack.sr_count); - srsran::console(" +------------+------------+------------+\n"); + sr_detection, + "-"); + } + + // Print SR + double cqi_detection = 0.0; + if (metrics.gnb_stack.cqi_count > 0) { + cqi_detection = (double)metrics.gnb_stack.cqi_valid_count / (double)metrics.gnb_stack.cqi_count; + srsran::console(" | %10s | %10d | %10d | %10.5f | %10.5f |\n", + "CQI", + metrics.gnb_stack.cqi_count, + metrics.gnb_stack.cqi_valid_count, + cqi_detection, + metrics.gnb_stack.mac.dl_cqi); } + srsran::console(" +------------+------------+------------+------------+------------+\n"); // Assert metrics - TESTASSERT_EQ(0, metrics.gnb_stack.mac.tx_errors); - TESTASSERT_EQ(0, metrics.gnb_stack.mac.rx_errors); - TESTASSERT(metrics.ue_stack.sr_count == metrics.gnb_stack.sr_count); + srsran_assert(metrics.gnb_stack.mac.tx_pkts == 0 or pdsch_bler <= assert_pdsch_bler_max, + "PDSCH BLER (%f) exceeds the assertion maximum (%f)", + pdsch_bler, + assert_pusch_bler_max); + srsran_assert(metrics.gnb_stack.mac.rx_pkts == 0 or pusch_bler <= assert_pusch_bler_max, + "PUSCH BLER (%f) exceeds the assertion maximum (%f)", + pusch_bler, + assert_pusch_bler_max); + srsran_assert(metrics.ue_stack.sr_count == 0 or sr_detection >= assert_sr_detection_min, + "SR detection probability (%f) did not reach the assertion minimum (%f)", + sr_detection, + assert_sr_detection_min); + srsran_assert(metrics.gnb_stack.cqi_count == 0 or cqi_detection >= assert_cqi_detection_min, + "CQI report detection probability (%f) did not reach the assertion minimum (%f)", + cqi_detection, + assert_sr_detection_min); + srsran_assert(prach_tx_count == 0 or prach_detection >= assert_prach_detection_min, + "PRACH detection probability (%f) did not reach the assertion minimum (%f)", + prach_detection, + assert_prach_detection_min); + srsran_assert(prach_tx_count == 0 or (prach_ta >= assert_prach_ta_min and prach_ta <= assert_prach_ta_max), + "PRACH TA average measurement %f is higher than minimum (%d) or above maximum (%f)", + prach_ta, + assert_prach_ta_min, + assert_prach_ta_max); + srsran_assert(metrics.gnb_stack.pucch.count == 0 or (metrics.gnb_stack.pucch.snr_db_min >= assert_pucch_snr_min), + "Minimum PUCCH DMRS SNR %f is below the minimum (%d)", + metrics.gnb_stack.pucch.snr_db_min, + assert_pucch_snr_min); // If reached here, the test is successful return SRSRAN_SUCCESS; diff --git a/test/phy/test_bench.h b/test/phy/test_bench.h index 0b6fab97f..1f158be83 100644 --- a/test/phy/test_bench.h +++ b/test/phy/test_bench.h @@ -31,6 +31,7 @@ class test_bench private: const std::string UE_PHY_COM_LOG_NAME = "UE /PHY/COM"; const std::string GNB_PHY_COM_LOG_NAME = "GNB/PHY/COM"; + const std::string CHANNEL_LOG_NAME = "CHANNEL"; uint32_t slot_idx = 0; uint64_t slot_count = 0; uint64_t duration_slots = 0; @@ -42,6 +43,9 @@ private: phy_common ue_phy_com; bool initialised = false; uint32_t sf_sz = 0; + // Channel simulator + srsran::channel dl_channel; + srsran::channel ul_channel; public: struct args_t { @@ -59,12 +63,16 @@ public: std::string phy_lib_log_level = "none"; uint64_t durations_slots = 100; + // channel simulator args + srsran::channel::args_t dl_channel; + srsran::channel::args_t ul_channel; args_t(int argc, char** argv); }; struct metrics_t { gnb_dummy_stack::metrics_t gnb_stack = {}; ue_dummy_stack::metrics_t ue_stack = {}; + srsue::phy_metrics_t ue_phy = {}; }; test_bench(const args_t& args) : @@ -78,10 +86,13 @@ public: gnb_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)), sf_sz((uint32_t)std::round(args.srate_hz * 1e-3)), - duration_slots(args.durations_slots) + duration_slots(args.durations_slots), + dl_channel(args.dl_channel, 1, srslog::fetch_basic_logger(CHANNEL_LOG_NAME, srslog::get_default_sink(), false)), + ul_channel(args.ul_channel, 1, srslog::fetch_basic_logger(CHANNEL_LOG_NAME, srslog::get_default_sink(), false)) { srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level)); srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level)); + srslog::fetch_basic_logger(CHANNEL_LOG_NAME).set_level(srslog::basic_levels::error); if (not gnb_phy.init(args.gnb_phy, args.cell_list)) { return; @@ -118,6 +129,9 @@ public: srsran_verbose = SRSRAN_VERBOSE_NONE; } + // Configure channel + dl_channel.set_srate((uint32_t)args.srate_hz); + ul_channel.set_srate((uint32_t)args.srate_hz); initialised = true; } @@ -150,6 +164,9 @@ public: // Set gNb time gnb_time.add(TX_ENB_DELAY * 1e-3); + // Run the UL channel simulator + ul_channel.run(gnb_rx_buffers.data(), gnb_rx_buffers.data(), (uint32_t)sf_sz, gnb_time.get(0)); + // Set gnb context srsran::phy_common_interface::worker_context_t gnb_context; gnb_context.sf_idx = slot_idx; @@ -177,6 +194,9 @@ public: // Set UE time ue_time.add(TX_ENB_DELAY * 1e-3); + // Run the DL channel simulator + dl_channel.run(ue_rx_buffers.data(), ue_rx_buffers.data(), (uint32_t)sf_sz, ue_time.get(0)); + // Set gnb context srsran::phy_common_interface::worker_context_t ue_context; ue_context.sf_idx = slot_idx; @@ -197,11 +217,12 @@ public: return slot_count <= duration_slots; } - metrics_t get_gnb_metrics() + metrics_t get_metrics() { metrics_t metrics = {}; metrics.gnb_stack = gnb_stack.get_metrics(); metrics.ue_stack = ue_stack.get_metrics(); + ue_phy.get_metrics(metrics.ue_phy); // get the metrics from the ue_phy return metrics; } };