diff --git a/lib/include/srsran/asn1/rrc_nr_utils.h b/lib/include/srsran/asn1/rrc_nr_utils.h index 8824954f2..1dbd90ae4 100644 --- a/lib/include/srsran/asn1/rrc_nr_utils.h +++ b/lib/include/srsran/asn1/rrc_nr_utils.h @@ -70,6 +70,7 @@ struct pdcch_cfg_common_s; struct pdcch_cfg_s; struct pdsch_cfg_common_s; struct pucch_cfg_common_s; +struct pucch_cfg_s; struct pusch_cfg_common_s; struct mib_s; @@ -144,6 +145,7 @@ void fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch); bool fill_phy_pdsch_cfg_common(const asn1::rrc_nr::pdsch_cfg_common_s& pdsch_cfg, srsran_sch_hl_cfg_nr_t* pdsch); void fill_phy_pucch_cfg_common(const asn1::rrc_nr::pucch_cfg_common_s& pucch_cfg, srsran_pucch_nr_common_cfg_t* pucch); +bool fill_phy_pucch_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch_nr_hl_cfg_t* pucch); bool fill_phy_pusch_cfg_common(const asn1::rrc_nr::pusch_cfg_common_s& pusch_cfg, srsran_sch_hl_cfg_nr_t* pusch); void fill_phy_carrier_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg, srsran_carrier_nr_t* carrier_nr); diff --git a/lib/include/srsran/common/security.h b/lib/include/srsran/common/security.h index 1b5b77083..1e81270ca 100644 --- a/lib/include/srsran/common/security.h +++ b/lib/include/srsran/common/security.h @@ -106,6 +106,7 @@ struct k_enb_context_t { }; struct k_gnb_context_t { + as_key_t k_gnb; as_key_t sk_gnb; }; @@ -189,6 +190,8 @@ uint8_t security_generate_k_amf(const uint8_t* k_seaf, uint8_t security_generate_k_seaf(const uint8_t* k_ausf, const char* serving_network_name, uint8_t* k_seaf); +uint8_t security_generate_k_gnb(const as_key_t& k_amf, const uint32_t nas_count, as_key_t& k_gnb); + uint8_t security_generate_k_enb(const uint8_t* k_asme, const uint32_t nas_count, uint8_t* k_enb); uint8_t security_generate_k_nb_star_common(uint8_t fc, diff --git a/lib/include/srsran/common/tti_sempahore.h b/lib/include/srsran/common/tti_sempahore.h index 0901c4d6f..508f74c2d 100644 --- a/lib/include/srsran/common/tti_sempahore.h +++ b/lib/include/srsran/common/tti_sempahore.h @@ -84,8 +84,10 @@ public: { std::unique_lock lock(mutex); - // Pop first element - fifo.pop_front(); + // If the FIFO is not empty pop first element + if (not fifo.empty()) { + fifo.pop_front(); + } // Notify release cvar.notify_all(); diff --git a/lib/include/srsran/interfaces/ue_nas_interfaces.h b/lib/include/srsran/interfaces/ue_nas_interfaces.h index dc16ea3d9..1faa18cae 100644 --- a/lib/include/srsran/interfaces/ue_nas_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nas_interfaces.h @@ -38,7 +38,7 @@ class pdu_session_cfg_t { public: std::string apn_name; - apn_types apn_type; + apn_types apn_type = ipv4; std::string apn_user; std::string apn_pass; }; @@ -67,13 +67,15 @@ public: class nas_5g_interface_rrc_nr { public: - virtual int write_pdu(srsran::unique_byte_buffer_t pdu) = 0; + virtual int write_pdu(srsran::unique_byte_buffer_t pdu) = 0; + virtual int get_k_amf(srsran::as_key_t& k_amf) = 0; + virtual uint32_t get_ul_nas_count() = 0; }; class nas_5g_interface_procedures { public: - virtual int send_registration_request() = 0; + virtual int send_registration_request() = 0; virtual int send_pdu_session_establishment_request(uint32_t transaction_identity, uint16_t pdu_session_id, const pdu_session_cfg_t& pdu_session) = 0; diff --git a/lib/include/srsran/interfaces/ue_phy_interfaces.h b/lib/include/srsran/interfaces/ue_phy_interfaces.h index 6cb3a6463..ccbd18126 100644 --- a/lib/include/srsran/interfaces/ue_phy_interfaces.h +++ b/lib/include/srsran/interfaces/ue_phy_interfaces.h @@ -105,7 +105,6 @@ struct phy_args_t { srsran::channel::args_t dl_channel_args; srsran::channel::args_t ul_channel_args; - }; /* RAT agnostic Interface MAC -> PHY */ @@ -167,7 +166,7 @@ public: virtual void meas_stop() = 0; /* Cell search and selection procedures */ - virtual bool cell_search() = 0; + virtual bool cell_search(int earfcn) = 0; virtual bool cell_select(phy_cell_t cell) = 0; virtual bool cell_is_camping() = 0; }; diff --git a/lib/include/srsran/interfaces/ue_usim_interfaces.h b/lib/include/srsran/interfaces/ue_usim_interfaces.h index 8c4e01a95..a6817b0c3 100644 --- a/lib/include/srsran/interfaces/ue_usim_interfaces.h +++ b/lib/include/srsran/interfaces/ue_usim_interfaces.h @@ -33,24 +33,24 @@ enum auth_result_t { AUTH_OK, AUTH_FAILED, AUTH_SYNCH_FAILURE }; class usim_interface_nas { public: - virtual std::string get_imsi_str() = 0; - virtual std::string get_imei_str() = 0; - virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0; - virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0; - virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0; + virtual std::string get_imsi_str() = 0; + virtual std::string get_imei_str() = 0; + virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 0; + virtual bool get_imei_vec(uint8_t* imei_, uint32_t n) = 0; + virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0; // Get the home mcc as bytes array - virtual bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) = 0; + virtual bool get_home_mcc_bytes(uint8_t* mcc_, uint32_t n) = 0; // Get the home mnc as byte array - virtual bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) = 0; + virtual bool get_home_mnc_bytes(uint8_t* mnc_, uint32_t n) = 0; // Get the home msin in bytes array encoded as bcd - virtual bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) = 0; + virtual bool get_home_msin_bcd(uint8_t* msin_, uint32_t n) = 0; virtual auth_result_t generate_authentication_response(uint8_t* rand, uint8_t* autn_enb, uint16_t mcc, uint16_t mnc, uint8_t* res, int* res_len, - uint8_t* k_asme) = 0; + uint8_t* k_asme) = 0; virtual auth_result_t generate_authentication_response_5g(uint8_t* rand, uint8_t* autn_enb, @@ -86,8 +86,10 @@ public: class usim_interface_rrc_nr { public: - virtual bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) = 0; - virtual bool update_nr_context(srsran::as_security_config_t* sec_cfg) = 0; + virtual void + generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) = 0; + virtual bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) = 0; + virtual bool update_nr_context(srsran::as_security_config_t* sec_cfg) = 0; }; } // namespace srsue diff --git a/lib/include/srsran/test/ue_test_interfaces.h b/lib/include/srsran/test/ue_test_interfaces.h index 63811ed26..d325f9518 100644 --- a/lib/include/srsran/test/ue_test_interfaces.h +++ b/lib/include/srsran/test/ue_test_interfaces.h @@ -85,7 +85,7 @@ class phy_dummy_interface : public phy_interface_rrc_lte void meas_stop() override {} /* Cell search and selection procedures */ - bool cell_search() override { return true; } + bool cell_search(int earfcn) override { return true; } bool cell_select(phy_cell_t cell) override { return true; } bool cell_is_camping() override { return false; } }; diff --git a/lib/include/srsran/upper/pdcp_entity_base.h b/lib/include/srsran/upper/pdcp_entity_base.h index 6c8f0428a..42b509926 100644 --- a/lib/include/srsran/upper/pdcp_entity_base.h +++ b/lib/include/srsran/upper/pdcp_entity_base.h @@ -98,7 +98,7 @@ public: } else { encryption_direction = direction; } - logger.debug("LCID=%d encryption=%s", lcid, srsran_direction_text[integrity_direction]); + logger.debug("LCID=%d, encryption=%s", lcid, srsran_direction_text[integrity_direction]); } void enable_security_timed(srsran_direction_t direction, uint32_t sn) diff --git a/lib/src/asn1/rrc_nr_utils.cc b/lib/src/asn1/rrc_nr_utils.cc index 8c00f7ad9..880e2393a 100644 --- a/lib/src/asn1/rrc_nr_utils.cc +++ b/lib/src/asn1/rrc_nr_utils.cc @@ -580,42 +580,41 @@ bool make_phy_csi_report(const csi_report_cfg_s& csi_report_cfg, } if (srsran_csi_hl_report_cfg.type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) { - srsran_csi_hl_report_cfg.periodic.period = - csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type().to_number(); - switch (csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type()) { + const auto& csi_periodic = csi_report_cfg.report_cfg_type.periodic(); + srsran_csi_hl_report_cfg.periodic.period = csi_periodic.report_slot_cfg.type().to_number(); + switch (csi_periodic.report_slot_cfg.type()) { case csi_report_periodicity_and_offset_c::types_opts::slots4: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots4(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots4(); break; case csi_report_periodicity_and_offset_c::types_opts::slots5: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots5(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots5(); break; case csi_report_periodicity_and_offset_c::types_opts::slots8: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots8(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots8(); break; case csi_report_periodicity_and_offset_c::types_opts::slots10: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots10(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots10(); break; case csi_report_periodicity_and_offset_c::types_opts::slots16: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots16(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots16(); break; case csi_report_periodicity_and_offset_c::types_opts::slots20: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots20(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots20(); break; case csi_report_periodicity_and_offset_c::types_opts::slots40: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots40(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots40(); break; case csi_report_periodicity_and_offset_c::types_opts::slots80: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots80(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots80(); break; case csi_report_periodicity_and_offset_c::types_opts::slots160: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots160(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots160(); break; case csi_report_periodicity_and_offset_c::types_opts::slots320: - srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots320(); + srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots320(); break; default: - asn1::log_warning("Invalid option for report_slot_cfg %s", - csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type().to_string()); + asn1::log_warning("Invalid option for report_slot_cfg %s", csi_periodic.report_slot_cfg.type().to_string()); return false; } } @@ -699,6 +698,7 @@ bool make_phy_csi_report(const csi_report_cfg_s& csi_report_cfg, asn1::log_warning("Invalid option for cqi_table %s", csi_report_cfg.cqi_table.to_string()); return false; } + *in_srsran_csi_hl_report_cfg = srsran_csi_hl_report_cfg; return true; } @@ -805,6 +805,10 @@ bool make_phy_res_config(const pucch_res_s& pucch_res, { srsran_pucch_nr_resource_t srsran_pucch_nr_resource = {}; srsran_pucch_nr_resource.starting_prb = pucch_res.start_prb; + srsran_pucch_nr_resource.intra_slot_hopping = pucch_res.intra_slot_freq_hop_present; + if (pucch_res.second_hop_prb_present) { + srsran_pucch_nr_resource.second_hop_prb = pucch_res.second_hop_prb; + } switch (pucch_res.format.type()) { case pucch_res_s::format_c_::types_opts::format0: srsran_pucch_nr_resource.format = SRSRAN_PUCCH_NR_FORMAT_0; @@ -1646,6 +1650,21 @@ bool make_csi_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_ce if (not make_phy_csi_report(csi_rep, &csi_hl->reports[i])) { return false; } + if (csi_rep.report_cfg_type.type().value == csi_report_cfg_s::report_cfg_type_c_::types_opts::periodic) { + const auto& pucch_setup = serv_cell.ul_cfg.init_ul_bwp.pucch_cfg.setup(); + srsran_pucch_nr_resource_t& resource = csi_hl->reports[i].periodic.resource; + uint32_t pucch_resource_id = csi_rep.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res; + const auto& asn1_resource = pucch_setup.res_to_add_mod_list[pucch_resource_id]; + uint32_t format2_rate = 0; + if (pucch_setup.format2_present and + pucch_setup.format2.type().value == asn1::setup_release_c::types_opts::setup and + pucch_setup.format2.setup().max_code_rate_present) { + format2_rate = pucch_setup.format2.setup().max_code_rate.to_number(); + } + if (not make_phy_res_config(asn1_resource, format2_rate, &resource)) { + return false; + } + } } } @@ -1720,6 +1739,67 @@ void fill_phy_pucch_cfg_common(const asn1::rrc_nr::pucch_cfg_common_s& pucch_cfg } } +bool fill_phy_pucch_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch_nr_hl_cfg_t* pucch) +{ + // sanity check to avoid pucch->sets[n] goes out of bound + if (pucch_cfg.res_set_to_add_mod_list.size() > SRSRAN_PUCCH_NR_MAX_NOF_SETS) { + return false; + } + + // iterate over the sets of resourceSetToAddModList + for (size_t n = 0; n < pucch_cfg.res_set_to_add_mod_list.size() and + pucch_cfg.res_set_to_add_mod_list.size() <= SRSRAN_PUCCH_NR_MAX_NOF_SETS; + n++) { + auto& res_set = pucch_cfg.res_set_to_add_mod_list[n]; + pucch->sets[n].nof_resources = res_set.res_list.size(); + if (res_set.max_payload_size_present) { + pucch->sets[n].max_payload_size = res_set.max_payload_size; + } + // NOTE: res_set.pucch_res_set_id does not have a corresponding field in the PHY struct + + // for each set, iterate over the elements (an element is an index). For each of the element or index, find the + // corresponding pucch_res_s object in the pucch_cfg.res_to_add_mod_list + for (size_t res_idx = 0; res_idx < res_set.res_list.size(); res_idx++) { + size_t pucch_resource_id = res_set.res_list[res_idx]; + + // Find the pucch_res_s object corresponding to pucch_resource_id in the pucch_cfg.res_to_add_mod_list + size_t m = 0; + while (m <= pucch_cfg.res_to_add_mod_list.size()) { + if (m == pucch_cfg.res_to_add_mod_list.size()) { + // if we get here, the list pucch_cfg.res_to_add_mod_list does not contain any object corresponding to + // pucch_resource_id + return false; + } + if (pucch_cfg.res_to_add_mod_list[m].pucch_res_id == pucch_resource_id) { + break; // item found, exit the loop + } + m++; + } + + // Below is the object corresponding to pucch_resource_id in the pucch_cfg.res_to_add_mod_list + const auto& asn1_resource = pucch_cfg.res_to_add_mod_list[m]; + + // sanity check to avoid pucch->sets[n].resources[res_idx] goes out of bound; + if (res_idx >= SRSRAN_PUCCH_NR_MAX_NOF_RESOURCES_PER_SET) { + return false; + } + + auto& resource = pucch->sets[n].resources[res_idx]; + uint32_t format2_rate = 0; + if (pucch_cfg.format2_present and + pucch_cfg.format2.type().value == asn1::setup_release_c::types_opts::setup and + pucch_cfg.format2.setup().max_code_rate_present) { + format2_rate = pucch_cfg.format2.setup().max_code_rate.to_number(); + } + if (not make_phy_res_config(asn1_resource, format2_rate, &resource)) { + return false; + } + } + } + + return true; +} + bool fill_phy_pdsch_cfg_common(const asn1::rrc_nr::pdsch_cfg_common_s& pdsch_cfg, srsran_sch_hl_cfg_nr_t* pdsch) { for (uint32_t i = 0; i < pdsch_cfg.pdsch_time_domain_alloc_list.size(); i++) { diff --git a/lib/src/common/security.cc b/lib/src/common/security.cc index f27ca1396..74bcfa554 100644 --- a/lib/src/common/security.cc +++ b/lib/src/common/security.cc @@ -24,7 +24,6 @@ #include "srsran/common/s3g.h" #include "srsran/common/ssl.h" #include "srsran/config.h" - #include #ifdef HAVE_MBEDTLS @@ -204,6 +203,32 @@ uint8_t security_generate_k_amf(const uint8_t* k_seaf, return SRSRAN_SUCCESS; } +uint8_t security_generate_k_gnb(const as_key_t& k_amf, const uint32_t nas_count_, as_key_t& k_gnb) +{ + if (k_amf.empty()) { + log_error("Invalid inputs"); + return SRSRAN_ERROR; + } + + // NAS Count + std::vector nas_count; + nas_count.resize(4); + nas_count[0] = (nas_count_ >> 24) & 0xFF; + nas_count[1] = (nas_count_ >> 16) & 0xFF; + nas_count[2] = (nas_count_ >> 8) & 0xFF; + nas_count[3] = nas_count_ & 0xFF; + + // Access Type Distinguisher 3GPP access = 0x01 (TS 33501 Annex A.9) + std::vector access_type_distinguisher = {1}; + + if (kdf_common(FC_5G_KGNB_KN3IWF_DERIVATION, k_amf, nas_count, access_type_distinguisher, k_gnb.data()) != + SRSRAN_SUCCESS) { + log_error("Failed to run kdf_common"); + return SRSRAN_ERROR; + } + return SRSRAN_SUCCESS; +} + uint8_t security_generate_k_enb(const uint8_t* k_asme, const uint32_t nas_count_, uint8_t* k_enb) { if (k_asme == NULL || k_enb == NULL) { diff --git a/lib/src/phy/phch/pbch_nr.c b/lib/src/phy/phch/pbch_nr.c index 4aaf2b294..94cc016a0 100644 --- a/lib/src/phy/phch/pbch_nr.c +++ b/lib/src/phy/phch/pbch_nr.c @@ -637,10 +637,23 @@ int srsran_pbch_nr_decode(srsran_pbch_nr_t* q, return SRSRAN_ERROR; } + // Avoid NAN getting into the demodulator + for (uint32_t i = 0; i < PBCH_NR_M; i++) { + if (isnan(__real__ symbols[i]) || isnan(__imag__ symbols[i])) { + symbols[i] = 0.0f; + } + } + // 7.3.3.2 Modulation int8_t llr[PBCH_NR_E]; srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, symbols, llr, PBCH_NR_M); + // If all LLR are zero, no message could be received + if (srsran_vec_avg_power_bf(llr, PBCH_NR_E) == 0) { + SRSRAN_MEM_ZERO(msg, srsran_pbch_msg_nr_t, 1); + return SRSRAN_SUCCESS; + } + // TS 38.211 7.3.3 Physical broadcast channel // 7.3.3.1 Scrambling pbch_nr_scramble_rx(cfg, cfg->ssb_idx, llr, llr); diff --git a/lib/src/phy/phch/test/CMakeLists.txt b/lib/src/phy/phch/test/CMakeLists.txt index 3e62ccfef..b61ef3c69 100644 --- a/lib/src/phy/phch/test/CMakeLists.txt +++ b/lib/src/phy/phch/test/CMakeLists.txt @@ -641,6 +641,10 @@ if(RF_FOUND) target_link_libraries(prach_test_usrp srsran_rf srsran_phy pthread) endif(RF_FOUND) +add_executable(prach_nr_test_perf EXCLUDE_FROM_ALL prach_nr_test_perf.c) +target_link_libraries(prach_nr_test_perf srsran_phy) +# this is just for performance evaluation, not for unit testing + ######################################################################## # NR ######################################################################## diff --git a/lib/src/phy/phch/test/prach_nr_test_perf.c b/lib/src/phy/phch/test/prach_nr_test_perf.c new file mode 100644 index 000000000..e1a6ccdf6 --- /dev/null +++ b/lib/src/phy/phch/test/prach_nr_test_perf.c @@ -0,0 +1,251 @@ +/** + * \copyright Copyright 2013-2021 Software Radio Systems Limited + * + * \copyright 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 prach_nr_test_perf.c + * \brief Performance test for PRACH NR. + * + * This program simulates several PRACH preamble transmissions (so far, burst format 0 only) + * to estimate the probability of detection and of false alarm. The probability of detection + * is the conditional probability of detecting the preamble when the preamble is present. + * An error consists in detecting no preambles, detecting only preambles different from the + * reference one, or detecting the correct preamble with a timing error beyond tolerance. + * The probability of false alarm is the probability of detecting any preamble when input + * is only noise. + * + * The simulation setup can be controlled by means of the following arguments. + * - -N num: sets the number of experiments to \c num. + * - -n num: sets the total number of UL PRBs to \c num. + * - -f num: sets the preamble format to \c num (for now, format 0 only). + * - -s val: sets the nominal SNR to \c val dB. + * - -v : activates verbose output. + * + * Example: + * \code{.cpp} + * prach_nr_test_perf -n 52 -s -14.6 + * \endcode + * + * \todo Restricted preamble formats not implemented yet. Fading channel and SIMO. + */ + +#include +#include +#include +#include +#include + +#include "srsran/srsran.h" + +#define MAX_LEN 70176 + +static uint32_t nof_prb = 52; +static uint32_t config_idx = 0; +static int nof_runs = 100; +static float snr_dB = -14.5F; +static bool is_verbose = false; + +static void usage(char* prog) +{ + printf("Usage: %s\n", prog); + printf("\t-N Number of experiments [Default %d]\n", nof_runs); + printf("\t-n Uplink number of PRB [Default %d]\n", nof_prb); + printf("\t-f Preamble format [Default %d]\n", config_idx); + printf("\t-s SNR in dB [Default %.2f]\n", snr_dB); + printf("\t-v Activate verbose output [Default %s]\n", is_verbose ? "true" : "false"); +} + +static void parse_args(int argc, char** argv) +{ + int opt = 0; + while ((opt = getopt(argc, argv, "N:n:f:s:v")) != -1) { + switch (opt) { + case 'N': + nof_runs = (int)strtol(optarg, NULL, 10); + break; + case 'n': + nof_prb = (uint32_t)strtol(optarg, NULL, 10); + break; + case 'f': + config_idx = (uint32_t)strtol(optarg, NULL, 10); + break; + case 's': + snr_dB = strtof(optarg, NULL); + break; + case 'v': + is_verbose = true; + break; + default: + usage(argv[0]); + exit(-1); + } + } +} + +int main(int argc, char** argv) +{ + parse_args(argc, argv); + if (config_idx != 0) { + ERROR("Preamble format not yet implemented"); + return SRSRAN_ERROR; + } + srsran_prach_t prach; + + const int fft_size = srsran_symbol_sz(nof_prb); + const float main_scs_kHz = 15; // UL subcarrier spacing (i.e., Delta f) + const float sampling_time_us = 1000.0F / (main_scs_kHz * (float)fft_size); + const int slot_length = 15 * fft_size; // number of samples in a slot + + if (srsran_prach_init(&prach, fft_size)) { + ERROR("Initializing PRACH"); + srsran_prach_free(&prach); + return SRSRAN_ERROR; + } + + cf_t preamble[MAX_LEN]; + srsran_vec_cf_zero(preamble, MAX_LEN); + + srsran_prach_cfg_t prach_cfg; + ZERO_OBJECT(prach_cfg); + + // Setup according to TS38.104 Section 8.4 + prach_cfg.is_nr = true; + prach_cfg.config_idx = 0; // preamble format 0 + prach_cfg.hs_flag = false; // no high speed + prach_cfg.freq_offset = 0; + prach_cfg.root_seq_idx = 22; // logical (root sequence) index i + prach_cfg.zero_corr_zone = 1; // zero correlation zone -> implies Ncs = 13 + prach_cfg.num_ra_preambles = 0; // use default + const uint32_t seq_index = 32; // sequence index "v" + const float prach_scs_kHz = 1.25F; // PRACH subcarrier spacing (i.e., Delta f^RA) + const float max_time_error_us = 1.04F; // time error tolerance + const int nof_offset_steps = 10; + + if (srsran_prach_set_cfg(&prach, &prach_cfg, nof_prb)) { + ERROR("Error initiating PRACH object"); + srsran_prach_free(&prach); + return SRSRAN_ERROR; + } + + if (srsran_prach_gen(&prach, seq_index, 0, preamble) < SRSRAN_SUCCESS) { + ERROR("Generating PRACH preamble"); + srsran_prach_free(&prach); + return SRSRAN_ERROR; + } + + const uint32_t preamble_length = prach.N_seq; + + float prach_pwr_sqrt = sqrtf(srsran_vec_avg_power_cf(preamble, preamble_length)); + if (!isnormal(prach_pwr_sqrt)) { + ERROR("PRACH preamble power is not a finite, nonzero value"); + srsran_prach_free(&prach); + return SRSRAN_ERROR; + } + srsran_vec_sc_prod_cfc(preamble, 1.0F / prach_pwr_sqrt, preamble, preamble_length); + + int vector_length = 2 * slot_length; + cf_t symbols[vector_length]; + cf_t noise_vec[vector_length]; + + uint32_t indices[64] = {0}; + float offset_est[64] = {0}; + uint32_t n_indices = 0; + + float time_offset_us = 0; + int offset_samples = 0; + float noise_var = srsran_convert_dB_to_power(-snr_dB); + int ok_detection = 0; + int missed_detection = 0; + int false_detection_signal_tmp = 0; + int false_detection_signal = 0; + int false_detection_noise = 0; + int offset_est_error = 0; + + // Timing offset base value is equivalent to N_cs/2 + const uint32_t ZC_length = prach.N_zc; // Zadoff-Chu sequence length (i.e., L_RA) + const float base_time_offset_us = (float)prach.N_cs * 1000 / (2.0F * (float)ZC_length * prach_scs_kHz); + + int step = SRSRAN_MAX(nof_runs / 100, 1); + for (int i_run = 0; i_run < nof_runs; i_run++) { + // show we are doing something + if (i_run % (20 * step) == 0) { + printf("\n"); + } + if (i_run % step == 0) { + printf("*"); + fflush(stdout); + } + + srsran_vec_cf_zero(noise_vec, vector_length); + srsran_ch_awgn_c(noise_vec, noise_vec, noise_var, vector_length); + if (is_verbose) { + float prach_pwr = srsran_vec_avg_power_cf(preamble, preamble_length); + float noise_pwr = srsran_vec_avg_power_cf(noise_vec, vector_length); + printf(" Tx power: %.3f\n", prach_pwr); + printf(" Noise power: %.3f\n", noise_pwr); + printf(" Target/measured SNR: %.3f / %.3f dB\n", snr_dB, srsran_convert_power_to_dB(prach_pwr / noise_pwr)); + } + // Cycle timing offset with a 0.1-us step starting from the base value + for (int i = 0; i < nof_offset_steps; i++) { + time_offset_us = base_time_offset_us + (float)i * 0.1F; + offset_samples = (int)roundf(time_offset_us / sampling_time_us); + srsran_vec_cf_copy(symbols, noise_vec, vector_length); + srsran_vec_sum_ccc(&symbols[offset_samples], preamble, &symbols[offset_samples], preamble_length); + + srsran_prach_detect_offset(&prach, 0, &symbols[prach.N_cp], slot_length, indices, offset_est, NULL, &n_indices); + false_detection_signal_tmp = 0; + for (int j = 0; j < n_indices; j++) { + if (indices[j] != seq_index) { + false_detection_signal_tmp++; + } else if (fabsf(offset_est[j] * 1.0e6F - time_offset_us) > max_time_error_us) { + offset_est_error++; + } else { + ok_detection++; + } + } + false_detection_signal += (n_indices > 1 || false_detection_signal_tmp == 1); + // Missed detection if no preamble was detected or no detected preamble is the right one + missed_detection += (n_indices == 0 || n_indices == false_detection_signal_tmp); + } + + srsran_prach_detect_offset(&prach, 0, &noise_vec[prach.N_cp], slot_length, indices, offset_est, NULL, &n_indices); + false_detection_noise += (n_indices > 0); + } + int total_runs = nof_offset_steps * nof_runs; + if (missed_detection + ok_detection + offset_est_error != total_runs) { + srsran_prach_free(&prach); + ERROR("Counting detection errors"); + return SRSRAN_ERROR; + } + + printf("\n\nPRACH performance test: format 0, %d PRB, AWGN channel, SNR=%.1f dB\n", nof_prb, snr_dB); + printf("\nMissed detection probability: %.3e (%d out of %d)\n", + (float)missed_detection / (float)total_runs, + missed_detection, + total_runs); + printf("Probability of timing error: %.3e (%d out of %d)\n", + (float)offset_est_error / (float)total_runs, + offset_est_error, + total_runs); + printf("Probability of OK detection: %.3e (%d out of %d)\n", + (float)ok_detection / (float)total_runs, + ok_detection, + total_runs); + printf("\nProbability of false detection with preamble: %.3e (%d out of %d)\n", + (float)false_detection_signal / (float)total_runs, + false_detection_signal, + total_runs); + printf("Probability of false detection without preamble: %.3e (%d out of %d)\n", + (float)false_detection_noise / (float)nof_runs, + false_detection_noise, + nof_runs); + + srsran_prach_free(&prach); + + printf("Done\n"); +} diff --git a/lib/src/phy/rf/rf_skiq_imp.c b/lib/src/phy/rf/rf_skiq_imp.c index 9dafd4352..bb61d893b 100644 --- a/lib/src/phy/rf/rf_skiq_imp.c +++ b/lib/src/phy/rf/rf_skiq_imp.c @@ -885,7 +885,7 @@ int rf_skiq_send_timed(void* h, } int rf_skiq_send_timed_multi(void* h_, - void** data_, + void* data_[SRSRAN_MAX_PORTS], int nsamples, time_t secs, double frac_secs, diff --git a/lib/src/phy/rf/rf_zmq_imp.c b/lib/src/phy/rf/rf_zmq_imp.c index 555f00cda..1286d1fe2 100644 --- a/lib/src/phy/rf/rf_zmq_imp.c +++ b/lib/src/phy/rf/rf_zmq_imp.c @@ -794,7 +794,7 @@ int rf_zmq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool bl for (int j = 0; j < decim_factor; j++, n++) { avg += ptr[n]; } - dst[i] = avg; + dst[i] = avg; // divide by decim_factor later via scale } rf_zmq_info(handler->id, @@ -810,6 +810,10 @@ int rf_zmq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool bl pthread_mutex_lock(&handler->rx_gain_mutex); float scale = srsran_convert_dB_to_amplitude(handler->rx_gain); pthread_mutex_unlock(&handler->rx_gain_mutex); + // scale shall also incorporate decim_factor + if (decim_factor > 0) { + scale = scale / decim_factor; + } for (uint32_t c = 0; c < handler->nof_channels; c++) { if (buffers[c]) { srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples); diff --git a/lib/src/phy/rf/rf_zmq_test.c b/lib/src/phy/rf/rf_zmq_test.c index 3e763e573..5de78991f 100644 --- a/lib/src/phy/rf/rf_zmq_test.c +++ b/lib/src/phy/rf/rf_zmq_test.c @@ -26,18 +26,22 @@ #include #include #include +#include #include #include -#define NOF_RX_ANT 1 +#define PRINT_SAMPLES 1 +#define COMPARE_BITS 0 +#define COMPARE_EPSILON (1e-6f) +#define NOF_RX_ANT 4 #define NUM_SF (500) #define SF_LEN (1920) #define RF_BUFFER_SIZE (SF_LEN * NUM_SF) #define TX_OFFSET_MS (4) -static cf_t ue_rx_buffer[RF_BUFFER_SIZE]; -static cf_t enb_tx_buffer[RF_BUFFER_SIZE]; -static cf_t enb_rx_buffer[RF_BUFFER_SIZE]; +static cf_t ue_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; +static cf_t enb_tx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; +static cf_t enb_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE]; static srsran_rf_t ue_radio, enb_radio; pthread_t rx_thread; @@ -62,13 +66,15 @@ void* ue_rx_thread_function(void* args) uint32_t num_rxed_samps = 0; for (uint32_t i = 0; i < num_slots; ++i) { void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; - data_ptr[0] = &ue_rx_buffer[i * num_samps_per_slot]; + for (uint32_t c = 0; c < NOF_RX_ANT; c++) { + data_ptr[c] = &ue_rx_buffer[c][i * num_samps_per_slot]; + } num_rxed_samps += srsran_rf_recv_with_time_multi(&ue_radio, data_ptr, num_samps_per_slot, true, NULL, NULL); } printf("received %d samples.\n", num_rxed_samps); - printf("closing ue norf device\n"); + printf("closing ue zmq device\n"); srsran_rf_close(&ue_radio); return NULL; @@ -87,17 +93,24 @@ void enb_tx_function(const char* tx_args, bool timed_tx) } // generate random tx data - for (int i = 0; i < RF_BUFFER_SIZE; i++) { - enb_tx_buffer[i] = ((float)rand() / (float)RAND_MAX) + _Complex_I * ((float)rand() / (float)RAND_MAX); + for (int c = 0; c < NOF_RX_ANT; c++) { + for (int i = 0; i < RF_BUFFER_SIZE; i++) { + enb_tx_buffer[c][i] = ((float)rand() / (float)RAND_MAX) + _Complex_I * ((float)rand() / (float)RAND_MAX); + } } // send data subframe per subframe uint32_t num_txed_samples = 0; + // initial transmission without ts void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; - data_ptr[0] = &enb_tx_buffer[num_txed_samples]; - int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false); + cf_t tx_buffer[NOF_RX_ANT][SF_LEN]; + for (int c = 0; c < NOF_RX_ANT; c++) { + memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof (cf_t)); + data_ptr[c] = &tx_buffer[c][0]; + } + int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false); num_txed_samples += SF_LEN; // from here on, all transmissions are timed relative to the last rx time @@ -105,11 +118,16 @@ void enb_tx_function(const char* tx_args, bool timed_tx) for (uint32_t i = 0; i < NUM_SF - ((timed_tx) ? TX_OFFSET_MS : 1); ++i) { // first recv samples - data_ptr[0] = enb_rx_buffer; + for (int c = 0; c < NOF_RX_ANT; c++) { + data_ptr[c] = enb_rx_buffer[c]; + } srsran_rf_recv_with_time_multi(&enb_radio, data_ptr, SF_LEN, true, &rx_time.full_secs, &rx_time.frac_secs); // prepare data buffer - data_ptr[0] = &enb_tx_buffer[num_txed_samples]; + for (int c = 0; c < NOF_RX_ANT; c++) { + memcpy(&tx_buffer[c], &enb_tx_buffer[c][num_txed_samples], SF_LEN * sizeof (cf_t)); + data_ptr[c] = &tx_buffer[c][0]; + } if (timed_tx) { // timed tx relative to receive time (this will cause a cap in the rx'ed samples at the UE resulting in 3 zero @@ -157,25 +175,46 @@ int run_test(const char* rx_args, const char* tx_args, bool timed_tx) // wait for rx thread pthread_join(rx_thread, NULL); - // subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx) - for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) { - uint32_t sf_offet = 0; - if (timed_tx && i >= 1) { - // for timed transmission, the enb inserts 3 zero subframes after the first untimed tx - sf_offet = (TX_OFFSET_MS - 1) * SF_LEN; - } + // channel-wise comparison + for (int c = 0; c < NOF_RX_ANT; c++) { + // subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx) + for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) { + uint32_t sf_offet = 0; + if (timed_tx && i >= 1) { + // for timed transmission, the enb inserts 3 zero subframes after the first untimed tx + sf_offet = (TX_OFFSET_MS - 1) * SF_LEN; + } -#if 0 - // print first 3 samples for each SF - printf("enb_tx_buffer sf%d:\n", i); - srsran_vec_fprint_c(stdout, &enb_tx_buffer[i * SF_LEN], 3); - printf("ue_rx_buffer sf%d:\n", i); - srsran_vec_fprint_c(stdout, &ue_rx_buffer[sf_offet + i * SF_LEN], 3); +#if PRINT_SAMPLES + // print first 10 samples for each SF + printf("enb_tx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &enb_tx_buffer[c][i * SF_LEN], 10); + printf("ue_rx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &ue_rx_buffer[c][sf_offet + i * SF_LEN], 10); #endif - if (memcmp(&ue_rx_buffer[sf_offet + i * SF_LEN], &enb_tx_buffer[i * SF_LEN], SF_LEN) != 0) { - fprintf(stderr, "data mismatch in subframe %d\n", i); - goto exit; +#if COMPARE_BITS + int d = memcmp(&ue_rx_buffer[sf_offet + i * SF_LEN], &enb_tx_buffer[i * SF_LEN], SF_LEN); + if (d) { + d = d > 0 ? d : -d; + fprintf(stderr, "data mismatch in subframe %d, sample %d\n", i, d); + printf("enb_tx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &enb_tx_buffer[i * SF_LEN + d], 10); + printf("ue_rx_buffer sf%d:\n", i); + srsran_vec_fprint_c(stdout, &ue_rx_buffer[sf_offet + i * SF_LEN + d], 10); + goto exit; + } +#else + srsran_vec_sub_ccc(&ue_rx_buffer[c][sf_offet + i * SF_LEN], + &enb_tx_buffer[c][i * SF_LEN], + &ue_rx_buffer[c][sf_offet + i * SF_LEN], + SF_LEN); + uint32_t max_ix = srsran_vec_max_abs_ci(&ue_rx_buffer[c][sf_offet + i * SF_LEN], SF_LEN); + if (cabsf(ue_rx_buffer[c][sf_offet + i * SF_LEN + max_ix]) > COMPARE_EPSILON) { + fprintf(stderr, "data mismatch in subframe %d\n", i); + goto exit; + } +#endif } } @@ -204,56 +243,76 @@ int param_test(const char* args_param, const int num_channels) int main() { - // two Rx ports - if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { - fprintf(stderr, "Param test failed!\n"); - return SRSRAN_ERROR; - } +// // two Rx ports +// if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { +// fprintf(stderr, "Param test failed!\n"); +// return SRSRAN_ERROR; +// } - // multiple rx ports, no channel index provided - if (param_test("rx_port=ipc://dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,base_srate=1.92e6", 4)) { - fprintf(stderr, "Param test failed!\n"); - return SRSRAN_ERROR; - } +// // multiple rx ports, no channel index provided +// if (param_test("rx_port=ipc://dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,base_srate=1.92e6", 4)) { +// fprintf(stderr, "Param test failed!\n"); +// return SRSRAN_ERROR; +// } - // One Rx, one Tx and all generic options - if (param_test("rx_port0=tcp://" - "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test", - 1)) { - fprintf(stderr, "Param test failed!\n"); - return SRSRAN_ERROR; - } +// // One Rx, one Tx and all generic options +// if (param_test("rx_port0=tcp://" +// "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test", +// 1)) { +// fprintf(stderr, "Param test failed!\n"); +// return SRSRAN_ERROR; +// } - // 1 port, 2 antennas, MIMO freq config - if (param_test( - "tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://" - "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6", - 2)) { - fprintf(stderr, "Param test failed!\n"); - return SRSRAN_ERROR; - } +// // 1 port, 2 antennas, MIMO freq config +// if (param_test( +// "tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://" +// "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6", +// 2)) { +// fprintf(stderr, "Param test failed!\n"); +// return SRSRAN_ERROR; +// } +#if NOF_RX_ANT == 1 // single tx, single rx with continuous transmissions (no timed tx) using IPC transport if (run_test("rx_port=ipc://link1,id=ue,base_srate=1.92e6", "tx_port=ipc://link1,id=enb,base_srate=1.92e6", false) != SRSRAN_SUCCESS) { fprintf(stderr, "Single tx, single rx test failed!\n"); return -1; } +#endif - // two trx radios with continous tx (no timed tx) using TCP transport for both directions - if (run_test("tx_port=tcp://*:5554,rx_port=tcp://" - "localhost:5555,id=ue,base_srate=1.92e6,log_trx_timeout=true,trx_timeout_ms=1000", - "rx_port=tcp://localhost:5554,tx_port=tcp://*:5555,id=enb,base_srate=1.92e6", + // up to 4 trx radios with continous tx (no decimation, no timed tx) + if (run_test("tx_port=tcp://*:5554,tx_port=tcp://*:5556,tx_port=tcp://*:5558,tx_port=tcp://*:5560,rx_port=tcp://" + "localhost:5555,rx_port=tcp://localhost:5557,rx_port=tcp://localhost:5559,rx_port=tcp://" + "localhost:5561,id=ue,base_srate=1.92e6,log_trx_timeout=true,trx_timeout_ms=1000", + "rx_port=tcp://localhost:5554,rx_port=tcp://localhost:5556,rx_port=tcp://localhost:5558,rx_port=tcp://" + "localhost:5560,tx_port=tcp://*:5555,tx_port=tcp://*:5557,tx_port=tcp://*:5559,tx_port=tcp://" + "*:5561,id=enb,base_srate=1.92e6", false) != SRSRAN_SUCCESS) { - fprintf(stderr, "Two TRx radio test failed!\n"); + fprintf(stderr, "Multi TRx radio test failed!\n"); return -1; } - // two trx radios with continous tx (no timed tx) using TCP for UL (UE tx) and IPC for eNB DL (eNB tx) - if (run_test("tx_port=tcp://*:5554,rx_port=ipc://dl,id=ue,base_srate=1.92e6", - "rx_port=tcp://localhost:5554,tx_port=ipc://dl,id=enb,base_srate=1.92e6", + // up to 4 trx radios with continous tx (timed tx) using TCP for UL (UE tx) and IPC for eNB DL (eNB tx) + if (run_test("tx_port=tcp://*:5554,tx_port=tcp://*:5556,tx_port=tcp://*:5558,tx_port=tcp://*:5560,rx_port=ipc://" + "dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,id=ue,base_srate=1.92e6", + "rx_port=tcp://localhost:5554,rx_port=tcp://localhost:5556,rx_port=tcp://localhost:5558,rx_port=tcp://" + "localhost:5560,tx_port=ipc://dl0,tx_port=ipc://dl1,tx_port=ipc://dl2,tx_port=ipc://" + "dl3,id=enb,base_srate=1.92e6", true) != SRSRAN_SUCCESS) { - fprintf(stderr, "Two TRx radio test with timed tx failed!\n"); + fprintf(stderr, "Multi TRx radio test with timed tx failed!\n"); + return -1; + } + + // up to 4 trx radios with continous tx (timed tx) using TCP for UL (UE tx) and IPC for eNB DL (eNB tx) + // with decimation 23.04e6 <-> 1.92e6 + if (run_test("tx_port=tcp://*:5554,tx_port=tcp://*:5556,tx_port=tcp://*:5558,tx_port=tcp://*:5560,rx_port=ipc://" + "dl0,rx_port=ipc://dl1,rx_port=ipc://dl2,rx_port=ipc://dl3,id=ue,base_srate=23.04e6", + "rx_port=tcp://localhost:5554,rx_port=tcp://localhost:5556,rx_port=tcp://localhost:5558,rx_port=tcp://" + "localhost:5560,tx_port=ipc://dl0,tx_port=ipc://dl1,tx_port=ipc://dl2,tx_port=ipc://" + "dl3,id=enb,base_srate=23.04e6", + true) != SRSRAN_SUCCESS) { + fprintf(stderr, "Multi TRx radio test with timed tx and decimation failed!\n"); return -1; } diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index 41b3b664b..0b0829187 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -46,7 +46,7 @@ /* * Default NR-PBCH DMRS normalised correlation (RSRP/EPRE) threshold */ -#define SSB_PBCH_DMRS_DEFAULT_CORR_THR 0.6f +#define SSB_PBCH_DMRS_DEFAULT_CORR_THR 0.5f static int ssb_init_corr(srsran_ssb_t* q) { @@ -865,8 +865,9 @@ static int ssb_pss_search(srsran_ssb_t* q, // Find maximum uint32_t peak_idx = srsran_vec_max_abs_ci(q->tmp_time, q->corr_window); - // Average power, skip window if value is invalid (0.0, nan or inf) - float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz); + // Average power, take total power of the frequency domain signal after filtering, skip correlation window if + // value is invalid (0.0, nan or inf) + float avg_pwr_corr = srsran_vec_avg_power_cf(q->tmp_corr, q->corr_sz); if (!isnormal(avg_pwr_corr)) { continue; } @@ -974,6 +975,11 @@ int srsran_ssb_csi_search(srsran_ssb_t* q, t_offset = 0; } + // Make sure SSB time offset is in bounded in the input buffer + if (t_offset + q->ssb_sz > nof_samples) { + return SRSRAN_SUCCESS; + } + // Demodulate cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; if (ssb_demodulate(q, in, t_offset, coarse_cfo_hz, ssb_grid) < SRSRAN_SUCCESS) { @@ -1043,11 +1049,12 @@ int srsran_ssb_csi_measure(srsran_ssb_t* q, return SRSRAN_SUCCESS; } -static int ssb_select_pbch(srsran_ssb_t* q, - uint32_t N_id, - const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], - uint32_t* found_n_hf, - uint32_t* found_ssb_idx_4lsb) +static int ssb_select_pbch(srsran_ssb_t* q, + uint32_t N_id, + const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], + uint32_t* found_n_hf, + uint32_t* found_ssb_idx_4lsb, + srsran_dmrs_pbch_meas_t* pbch_meas) { // Prepare PBCH DMRS configuration srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; @@ -1089,6 +1096,7 @@ static int ssb_select_pbch(srsran_ssb_t* q, // Save findings *found_n_hf = best_n_hf; *found_ssb_idx_4lsb = best_ssb_idx; + *pbch_meas = best_meas; return SRSRAN_SUCCESS; } @@ -1186,6 +1194,9 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs return SRSRAN_ERROR; } + // Set the SSB search result with default value with PBCH CRC unmatched, meaning no cell is found + SRSRAN_MEM_ZERO(res, srsran_ssb_search_res_t, 1); + // Search for PSS in time domain uint32_t N_id_2 = 0; uint32_t t_offset = 0; @@ -1202,6 +1213,11 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs t_offset = 0; } + // Make sure SSB time offset is in bounded in the input buffer + if (t_offset + q->ssb_sz > nof_samples) { + return SRSRAN_SUCCESS; + } + // Demodulate cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; if (ssb_demodulate(q, in, t_offset, coarse_cfo_hz, ssb_grid) < SRSRAN_SUCCESS) { @@ -1221,20 +1237,32 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs uint32_t N_id = SRSRAN_NID_NR(N_id_1, N_id_2); // Select the most suitable SSB candidate - uint32_t n_hf = 0; - uint32_t ssb_idx = 0; - if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) { + uint32_t n_hf = 0; + uint32_t ssb_idx = 0; + srsran_dmrs_pbch_meas_t pbch_meas = {}; + if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx, &pbch_meas) < SRSRAN_SUCCESS) { ERROR("Error selecting PBCH"); return SRSRAN_ERROR; } - // Compute PBCH channel estimates + // Avoid decoding if the selected PBCH DMRS do not reach the minimum threshold + if (pbch_meas.corr < q->args.pbch_dmrs_thr) { + return SRSRAN_SUCCESS; + } + + // Decode PBCH srsran_pbch_msg_nr_t pbch_msg = {}; if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, &pbch_msg) < SRSRAN_SUCCESS) { ERROR("Error decoding PBCH"); return SRSRAN_ERROR; } + // If PBCH was not decoded, skip measurements + if (!pbch_msg.crc) { + return SRSRAN_SUCCESS; + } + + // Perform measurements from PSS and SSS srsran_csi_trs_measurements_t measurements = {}; if (ssb_measure(q, ssb_grid, N_id, &measurements) < SRSRAN_SUCCESS) { ERROR("Error measuring"); @@ -1336,6 +1364,9 @@ int srsran_ssb_find(srsran_ssb_t* q, return SRSRAN_ERROR; } + // Set the PBCH message result with default value (CRC unmatched), meaning no cell is found + SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1); + // Copy tail from previous execution into the start of this srsran_vec_cf_copy(q->sf_buffer, &q->sf_buffer[q->sf_sz], q->ssb_sz); @@ -1356,6 +1387,11 @@ int srsran_ssb_find(srsran_ssb_t* q, t_offset = 0; } + // Make sure SSB time offset is in bounded in the input buffer + if (t_offset > q->sf_sz) { + return SRSRAN_SUCCESS; + } + // Demodulate cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; if (ssb_demodulate(q, q->sf_buffer, t_offset, 0.0f, ssb_grid) < SRSRAN_SUCCESS) { @@ -1370,13 +1406,19 @@ int srsran_ssb_find(srsran_ssb_t* q, } // Select the most suitable SSB candidate - uint32_t n_hf = 0; - uint32_t ssb_idx = 0; // SSB candidate index - if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) { + uint32_t n_hf = 0; + uint32_t ssb_idx = 0; // SSB candidate index + srsran_dmrs_pbch_meas_t pbch_meas = {}; + if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx, &pbch_meas) < SRSRAN_SUCCESS) { ERROR("Error selecting PBCH"); return SRSRAN_ERROR; } + // Avoid decoding if the selected PBCH DMRS do not reach the minimum threshold + if (pbch_meas.corr < q->args.pbch_dmrs_thr) { + return SRSRAN_SUCCESS; + } + // Calculate the SSB offset in the subframe uint32_t ssb_offset = srsran_ssb_candidate_sf_offset(q, ssb_idx); diff --git a/lib/src/phy/sync/test/ssb_decode_test.c b/lib/src/phy/sync/test/ssb_decode_test.c index 9fdf9032c..1bf614a19 100644 --- a/lib/src/phy/sync/test/ssb_decode_test.c +++ b/lib/src/phy/sync/test/ssb_decode_test.c @@ -29,6 +29,9 @@ #include #include +#define SSB_DECODE_TEST_PCI_STRIDE 53 +#define SSB_DECODE_TEST_SSB_STRIDE 3 + // NR parameters static uint32_t carrier_nof_prb = 52; static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; @@ -128,7 +131,7 @@ static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx) pbch_msg->crc = true; } -static int test_case_1(srsran_ssb_t* ssb) +static int test_case_true(srsran_ssb_t* ssb) { // For benchmarking purposes uint64_t t_encode_usec = 0; @@ -147,8 +150,8 @@ static int test_case_1(srsran_ssb_t* ssb) // For each PCI... uint64_t count = 0; - for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += 23) { - for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx++, count++) { + for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += SSB_DECODE_TEST_PCI_STRIDE) { + for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx += SSB_DECODE_TEST_SSB_STRIDE, count++) { struct timeval t[3] = {}; // Build PBCH message @@ -158,7 +161,7 @@ static int test_case_1(srsran_ssb_t* ssb) // Print encoded PBCH message char str[512] = {}; srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str)); - INFO("test_case_1 - encoded pci=%d %s", pci, str); + INFO("test_case_true - encoded pci=%d %s", pci, str); // Initialise baseband srsran_vec_cf_zero(buffer, hf_len); @@ -184,7 +187,7 @@ static int test_case_1(srsran_ssb_t* ssb) // Print decoded PBCH message srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str)); - INFO("test_case_1 - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO"); + INFO("test_case_true - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO"); // Assert PBCH message CRC TESTASSERT(pbch_msg_rx.crc); @@ -200,7 +203,7 @@ static int test_case_1(srsran_ssb_t* ssb) // Print decoded PBCH message srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str)); - INFO("test_case_1 - found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO"); + INFO("test_case_true - found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO"); // Assert PBCH message CRC TESTASSERT(res.pbch_msg.crc); @@ -209,11 +212,11 @@ static int test_case_1(srsran_ssb_t* ssb) } if (!count) { - ERROR("Error in test case 1: undefined division"); + ERROR("Error in test case true: undefined division"); return SRSRAN_ERROR; } - INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode; %.1f usec/decode;", + INFO("test_case_true - %.1f usec/encode; %.1f usec/decode; %.1f usec/decode;", (double)t_encode_usec / (double)(count), (double)t_decode_usec / (double)(count), (double)t_search_usec / (double)(count)); @@ -221,6 +224,77 @@ static int test_case_1(srsran_ssb_t* ssb) return SRSRAN_SUCCESS; } +static int test_case_false(srsran_ssb_t* ssb) +{ + // For benchmarking purposes + uint64_t t_decode_usec = 0; + uint64_t t_search_usec = 0; + + // SSB configuration + srsran_ssb_cfg_t ssb_cfg = {}; + ssb_cfg.srate_hz = srate_hz; + ssb_cfg.center_freq_hz = carrier_freq_hz; + ssb_cfg.ssb_freq_hz = ssb_freq_hz; + ssb_cfg.scs = ssb_scs; + ssb_cfg.pattern = ssb_pattern; + + TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS); + + // For each PCI... + uint32_t count = 0; + for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += SSB_DECODE_TEST_PCI_STRIDE, count++) { + struct timeval t[3] = {}; + + // Initialise baseband + srsran_vec_cf_zero(buffer, hf_len); + + // Channel, as it is zero it only adds noise + srsran_channel_awgn_run_c(&awgn, buffer, buffer, hf_len); + + // Decode + gettimeofday(&t[1], NULL); + srsran_pbch_msg_nr_t pbch_msg_rx = {}; + TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, false, 0, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Print decoded PBCH message + char str[512] = {}; + srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str)); + INFO("test_case_false - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO"); + + // Assert PBCH message CRC is not okay + TESTASSERT(!pbch_msg_rx.crc); + + // Search + srsran_ssb_search_res_t res = {}; + gettimeofday(&t[1], NULL); + TESTASSERT(srsran_ssb_search(ssb, buffer, hf_len, &res) == SRSRAN_SUCCESS); + gettimeofday(&t[2], NULL); + get_time_interval(t); + t_search_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL; + + // Print decoded PBCH message + srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str)); + INFO("test_case_false - false found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO"); + + // Assert PBCH message CRC + TESTASSERT(!res.pbch_msg.crc); + } + + if (!count) { + ERROR("Error in test case true: undefined division"); + return SRSRAN_ERROR; + } + + INFO("test_case_false - %.1f usec/decode; %.1f usec/decode;", + (double)t_decode_usec / (double)(count), + (double)t_search_usec / (double)(count)); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { int ret = SRSRAN_ERROR; @@ -257,7 +331,12 @@ int main(int argc, char** argv) goto clean_exit; } - if (test_case_1(&ssb) != SRSRAN_SUCCESS) { + if (test_case_true(&ssb) != SRSRAN_SUCCESS) { + ERROR("test case failed"); + goto clean_exit; + } + + if (test_case_false(&ssb) != SRSRAN_SUCCESS) { ERROR("test case failed"); goto clean_exit; } diff --git a/lib/test/common/test_security_kdf.cc b/lib/test/common/test_security_kdf.cc index d18cd8c8b..f243db73c 100644 --- a/lib/test/common/test_security_kdf.cc +++ b/lib/test/common/test_security_kdf.cc @@ -167,6 +167,26 @@ int test_generate_k_nas() return SRSRAN_SUCCESS; } +int test_generate_k_gnb() +{ + auto& logger = srslog::fetch_basic_logger("LOG", false); + + as_key_t k_gnb_o; + + as_key_t k_gnb = {0x49, 0x3a, 0x16, 0xc5, 0x8b, 0x77, 0xb6, 0x27, 0xfa, 0x3f, 0x1a, 0xc6, 0x34, 0x4c, 0x18, 0x30, + 0x39, 0xf0, 0x1b, 0xa0, 0xcb, 0x76, 0x36, 0xbb, 0xcc, 0xc4, 0x36, 0x5b, 0x02, 0x3b, 0xd5, 0x62}; + + as_key_t k_amf = {0xd6, 0x55, 0xf1, 0x61, 0x42, 0x03, 0x5d, 0x4d, 0x72, 0xca, 0x39, 0x58, 0x3d, 0x22, 0x8d, 0x2d, + 0xd2, 0xec, 0x0c, 0xa7, 0x92, 0x9a, 0xd0, 0x07, 0xf5, 0x3b, 0x38, 0x2d, 0x05, 0x54, 0x44, 0x05}; + + uint32_t nas_ul_count = 0; + + TESTASSERT(srsran::security_generate_k_gnb(k_amf, nas_ul_count, k_gnb_o) == SRSRAN_SUCCESS); + TESTASSERT(k_gnb_o == k_gnb); + + return SRSRAN_SUCCESS; +} + int test_generate_k_enb() { auto& logger = srslog::fetch_basic_logger("LOG", false); @@ -442,7 +462,7 @@ int main(int argc, char** argv) TESTASSERT(test_generate_up_keys() == SRSRAN_SUCCESS); TESTASSERT(test_generate_k_enb_star() == SRSRAN_SUCCESS); TESTASSERT(test_generate_k_nh() == SRSRAN_SUCCESS); - + TESTASSERT(test_generate_k_gnb() == SRSRAN_SUCCESS); TESTASSERT(test_generate_res_star() == SRSRAN_SUCCESS); TESTASSERT(test_generate_k_ausf() == SRSRAN_SUCCESS); TESTASSERT(test_generate_k_seaf() == SRSRAN_SUCCESS); diff --git a/srsenb/hdr/stack/s1ap/s1ap.h b/srsenb/hdr/stack/s1ap/s1ap.h index 898ea13f4..d03ce8d60 100644 --- a/srsenb/hdr/stack/s1ap/s1ap.h +++ b/srsenb/hdr/stack/s1ap/s1ap.h @@ -116,7 +116,7 @@ public: // Stack interface bool - handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); + handle_mme_rx_msg(srsran::unique_byte_buffer_t pdu, const sockaddr_in& from, const sctp_sndrcvinfo& sri, int flags); void start_pcap(srsran::s1ap_pcap* pcap_); private: @@ -270,6 +270,7 @@ private: void set_state(s1ap_proc_id_t state, const erab_id_list& erabs_updated, const erab_item_list& erabs_failed_to_update); + s1ap_proc_id_t get_state() const { return current_state; } ue_ctxt_t ctxt = {}; uint16_t stream_id = 1; diff --git a/srsenb/src/stack/s1ap/s1ap.cc b/srsenb/src/stack/s1ap/s1ap.cc index ecd0d5d0d..d253cd820 100644 --- a/srsenb/src/stack/s1ap/s1ap.cc +++ b/srsenb/src/stack/s1ap/s1ap.cc @@ -785,6 +785,14 @@ bool s1ap::handle_initialctxtsetuprequest(const init_context_setup_request_s& ms return false; } + if (u->get_state() == s1ap_proc_id_t::init_context_setup_request) { + logger.warning("Initial Context Setup Request already in progress. Ignoring ICS request."); + asn1::s1ap::cause_c cause; + cause.set_protocol().value = cause_protocol_opts::msg_not_compatible_with_receiver_state; + send_error_indication(cause, msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value); + return false; + } + // Setup UE ctxt in RRC if (not rrc->setup_ue_ctxt(u->ctxt.rnti, msg)) { return false; @@ -1999,6 +2007,9 @@ s1ap::ue* s1ap::handle_s1apmsg_ue_id(uint32_t enb_id, uint32_t mme_id) ue* user_ptr = users.find_ue_enbid(enb_id); ue* user_mme_ptr = nullptr; cause_c cause; + + logger.info("Checking UE S1 logical connection. eNB UE S1AP ID=%d, MME UE S1AP ID=%d", enb_id, mme_id); + if (user_ptr != nullptr) { if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) { // No ID inconsistency found diff --git a/srsgnb/hdr/stack/mac/sched_nr_interface.h b/srsgnb/hdr/stack/mac/sched_nr_interface.h index d2875035c..717de0863 100644 --- a/srsgnb/hdr/stack/mac/sched_nr_interface.h +++ b/srsgnb/hdr/stack/mac/sched_nr_interface.h @@ -92,8 +92,8 @@ struct sched_nr_cell_cfg_t { uint32_t pci; uint32_t dl_cell_nof_prb; uint32_t ul_cell_nof_prb; - asn1::copy_ptr dl_cfg_common; - asn1::copy_ptr ul_cfg_common; + asn1::rrc_nr::dl_cfg_common_sib_s dl_cfg_common; + asn1::rrc_nr::ul_cfg_common_sib_s ul_cfg_common; srsran::optional tdd_ul_dl_cfg_common; ssb_positions_in_burst_t ssb_positions_in_burst; uint32_t ssb_periodicity_ms = 0; @@ -107,8 +107,6 @@ struct sched_nr_cell_cfg_t { double dl_center_frequency_hz; double ul_center_frequency_hz; double ssb_center_freq_hz; - uint32_t offset_to_carrier; - srsran_subcarrier_spacing_t scs; }; class sched_nr_interface diff --git a/srsgnb/hdr/stack/rrc/rrc_nr.h b/srsgnb/hdr/stack/rrc/rrc_nr.h index 763135f78..9f46b259a 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr.h @@ -46,6 +46,7 @@ namespace srsenb { class enb_bearer_manager; +class du_config_manager; enum class rrc_nr_state_t { RRC_IDLE, RRC_INACTIVE, RRC_CONNECTED }; @@ -151,8 +152,8 @@ private: asn1::rrc_nr::sp_cell_cfg_s base_sp_cell_cfg; // vars + std::unique_ptr du_cfg; struct cell_ctxt_t { - asn1::rrc_nr::mib_s mib; asn1::rrc_nr::sib1_s sib1; asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_l_ sibs; srsran::unique_byte_buffer_t mib_buffer = nullptr; diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_config.h b/srsgnb/hdr/stack/rrc/rrc_nr_config.h index 397a54a2c..469e0c07e 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_config.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_config.h @@ -41,17 +41,19 @@ struct rrc_nr_cfg_sr_t { // Cell/Sector configuration for NR cells struct rrc_cell_cfg_nr_t { - phy_cell_cfg_nr_t phy_cell; // already contains all PHY-related parameters (i.e. RF port, PCI, etc.) - uint32_t tac; // Tracking area code - uint32_t dl_arfcn; // DL freq already included in phy_cell - uint32_t ul_arfcn; // UL freq also in phy_cell - uint32_t dl_absolute_freq_point_a; // derived from DL ARFCN - uint32_t ul_absolute_freq_point_a; // derived from UL ARFCN - uint32_t ssb_absolute_freq_point; // derived from DL ARFCN (SSB arfcn) - uint32_t band; - uint32_t coreset0_idx; // Table 13-{1,...15} row index - srsran_duplex_mode_t duplex_mode; - srsran_ssb_cfg_t ssb_cfg; + phy_cell_cfg_nr_t phy_cell; // already contains all PHY-related parameters (i.e. RF port, PCI, etc.) + uint32_t tac; // Tracking area code + uint32_t dl_arfcn; // DL freq already included in phy_cell + uint32_t ul_arfcn; // UL freq also in phy_cell + uint32_t dl_absolute_freq_point_a; // derived from DL ARFCN + uint32_t ul_absolute_freq_point_a; // derived from UL ARFCN + uint32_t band; + uint32_t coreset0_idx; // Table 13-{1,...15} row index + srsran_duplex_mode_t duplex_mode; + double ssb_freq_hz; + uint32_t ssb_absolute_freq_point; // derived from DL ARFCN (SSB arfcn) + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_patern_t ssb_pattern; }; typedef std::vector rrc_cell_list_nr_t; diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h b/srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h new file mode 100644 index 000000000..05e063e4f --- /dev/null +++ b/srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h @@ -0,0 +1,68 @@ +/** + * + * \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_DU_MANAGER_H +#define SRSRAN_RRC_NR_DU_MANAGER_H + +#include "rrc_nr_config.h" +#include "srsgnb/hdr/stack/mac/sched_nr_interface.h" +#include "srsran/asn1/rrc_nr.h" + +namespace srsenb { + +class du_cell_config +{ +public: + uint32_t cc; + uint32_t pci; + + asn1::rrc_nr::mib_s mib; + srsran::unique_byte_buffer_t packed_mib; + + asn1::rrc_nr::sib1_s sib1; + srsran::unique_byte_buffer_t packed_sib1; + + const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg_common() const + { + return sib1.serving_cell_cfg_common; + } + + /// SI messages (index=0 for SIB1) + srsran::const_byte_span packed_si_msg(uint32_t idx) { return srsran::make_span(packed_sib1); } + size_t nof_si_msgs() const { return 1; } +}; + +class du_config_manager +{ +public: + explicit du_config_manager(const rrc_nr_cfg_t& cfg); + ~du_config_manager(); + + const rrc_nr_cfg_t& cfg; + + int add_cell(); + + const du_cell_config& cell(uint32_t cc) const + { + srsran_assert(cc < cells.size(), "Unknown DU Cell Index=%d", cc); + return *cells[cc]; + } + +private: + srslog::basic_logger& logger; + + std::vector > cells; +}; + +} // namespace srsenb + +#endif // SRSRAN_RRC_NR_DU_MANAGER_H diff --git a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h index 9c30451f9..1caf6fb4f 100644 --- a/srsgnb/hdr/stack/rrc/rrc_nr_ue.h +++ b/srsgnb/hdr/stack/rrc/rrc_nr_ue.h @@ -117,6 +117,9 @@ private: /// Update MAC based on ASN1 message int update_mac(const asn1::rrc_nr::cell_group_cfg_s& cell_group_config, bool is_config_complete); + /// Update AS security config on active RB + int update_as_security(uint32_t lcid, bool enable_integrity, bool enable_ciphering); + int pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig); int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config); diff --git a/srsgnb/src/stack/mac/sched_nr.cc b/srsgnb/src/stack/mac/sched_nr.cc index 6500c59f9..bde49cbec 100644 --- a/srsgnb/src/stack/mac/sched_nr.cc +++ b/srsgnb/src/stack/mac/sched_nr.cc @@ -133,7 +133,7 @@ public: for (ue_event_t& ev : current_slot_ue_events) { auto ue_it = ues.find(ev.rnti); if (ue_it == ues.end()) { - sched_logger.warning("SCHED: \"%s\" called for inexistent rnti=0x%x.", ev.event_name, ev.rnti); + sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x.", ev.event_name, ev.rnti); ev.rnti = SRSRAN_INVALID_RNTI; } else if (ue_it->second->has_ca()) { // events specific to existing UEs with CA @@ -161,7 +161,7 @@ public: } auto ue_it = ues.find(ev.rnti); if (ue_it == ues.end()) { - sched_logger.warning("SCHED: \"%s\" called for inexistent rnti=0x%x.", ev.event_name, ev.rnti); + sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x.", ev.event_name, ev.rnti); ev.rnti = SRSRAN_INVALID_RNTI; } else if (not ue_it->second->has_ca() and ue_it->second->carriers[cc] != nullptr) { ev.callback(*ue_it->second, evlogger); @@ -174,7 +174,7 @@ public: if (ue_it != ues.end() and ue_it->second->carriers[cc] != nullptr) { ev.callback(*ue_it->second->carriers[cc], evlogger); } else { - sched_logger.warning("SCHED: \"%s\" called for inexistent rnti=0x%x,cc=%d.", ev.event_name, ev.rnti, ev.cc); + sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x,cc=%d.", ev.event_name, ev.rnti, ev.cc); } } } diff --git a/srsgnb/src/stack/mac/sched_nr_cfg.cc b/srsgnb/src/stack/mac/sched_nr_cfg.cc index 7d584a24d..7a8d596f2 100644 --- a/srsgnb/src/stack/mac/sched_nr_cfg.cc +++ b/srsgnb/src/stack/mac/sched_nr_cfg.cc @@ -149,15 +149,11 @@ cell_config_manager::cell_config_manager(uint32_t cc_, carrier.dl_center_frequency_hz = cell.dl_center_frequency_hz; carrier.ul_center_frequency_hz = cell.ul_center_frequency_hz; carrier.ssb_center_freq_hz = cell.ssb_center_freq_hz; - carrier.offset_to_carrier = cell.offset_to_carrier; - carrier.scs = cell.scs; carrier.nof_prb = cell.dl_cell_nof_prb; carrier.start = 0; // TODO: Check carrier.max_mimo_layers = cell.nof_layers; - if (cell.dl_cfg_common.is_present()) { - carrier.offset_to_carrier = cell.dl_cfg_common->freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; - carrier.scs = (srsran_subcarrier_spacing_t)cell.dl_cfg_common->init_dl_bwp.generic_params.subcarrier_spacing.value; - } + carrier.offset_to_carrier = cell.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; + carrier.scs = (srsran_subcarrier_spacing_t)cell.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value; // TDD-UL-DL-ConfigCommon duplex.mode = SRSRAN_DUPLEX_MODE_FDD; diff --git a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc index 1a52670e5..dc7d4b31b 100644 --- a/srsgnb/src/stack/mac/sched_nr_interface_utils.cc +++ b/srsgnb/src/stack/mac/sched_nr_interface_utils.cc @@ -34,9 +34,9 @@ uint32_t coreset_nof_cces(const srsran_coreset_t& coreset) void make_mib_cfg(const sched_nr_cell_cfg_t& cfg, srsran_mib_nr_t* mib) { - *mib = {}; - mib->scs_common = cfg.scs; - mib->ssb_offset = 6; // TODO + *mib = {}; + mib->scs_common = (srsran_subcarrier_spacing_t)cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value; + mib->ssb_offset = 6; // TODO mib->dmrs_typeA_pos = (srsran_dmrs_sch_typeA_pos_t)cfg.dmrs_type_a_position.value; mib->coreset0_idx = cfg.pdcch_cfg_sib1.ctrl_res_set_zero; mib->ss0_idx = cfg.pdcch_cfg_sib1.search_space_zero; @@ -62,12 +62,10 @@ void make_ssb_cfg(const sched_nr_cell_cfg_t& cfg, srsran::phy_cfg_nr_t::ssb_cfg_ } ssb->scs = (srsran_subcarrier_spacing_t)cfg.ssb_scs.value; ssb->pattern = SRSRAN_SSB_PATTERN_A; - if (cfg.dl_cfg_common.is_present()) { - if (cfg.dl_cfg_common->freq_info_dl.freq_band_list.size() > 0 and - cfg.dl_cfg_common->freq_info_dl.freq_band_list[0].freq_band_ind_nr_present) { - uint32_t band = cfg.dl_cfg_common->freq_info_dl.freq_band_list[0].freq_band_ind_nr; - ssb->pattern = srsran::srsran_band_helper::get_ssb_pattern(band, ssb->scs); - } + if (cfg.dl_cfg_common.freq_info_dl.freq_band_list.size() > 0 and + cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present) { + uint32_t band = cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr; + ssb->pattern = srsran::srsran_band_helper::get_ssb_pattern(band, ssb->scs); } } @@ -93,10 +91,11 @@ srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_cell_cfg_t& cfg) ue_phy_cfg.carrier.dl_center_frequency_hz = cfg.dl_center_frequency_hz; ue_phy_cfg.carrier.ul_center_frequency_hz = cfg.ul_center_frequency_hz; ue_phy_cfg.carrier.ssb_center_freq_hz = cfg.ssb_center_freq_hz; - ue_phy_cfg.carrier.offset_to_carrier = cfg.offset_to_carrier; - ue_phy_cfg.carrier.scs = cfg.scs; - ue_phy_cfg.carrier.nof_prb = cfg.dl_cell_nof_prb; - ue_phy_cfg.carrier.max_mimo_layers = cfg.nof_layers; + ue_phy_cfg.carrier.offset_to_carrier = cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier; + ue_phy_cfg.carrier.scs = + (srsran_subcarrier_spacing_t)cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value; + ue_phy_cfg.carrier.nof_prb = cfg.dl_cell_nof_prb; + ue_phy_cfg.carrier.max_mimo_layers = cfg.nof_layers; make_ssb_cfg(cfg, &ue_phy_cfg.ssb); // remove UE-specific SearchSpaces (they will be added later via RRC) diff --git a/srsgnb/src/stack/mac/sched_nr_ue.cc b/srsgnb/src/stack/mac/sched_nr_ue.cc index 98c0ec488..d6bbfef34 100644 --- a/srsgnb/src/stack/mac/sched_nr_ue.cc +++ b/srsgnb/src/stack/mac/sched_nr_ue.cc @@ -235,7 +235,7 @@ void ue::new_slot(slot_point pdcch_slot) slot_ue ue::make_slot_ue(slot_point pdcch_slot, uint32_t cc) { - srsran_assert(carriers[cc] != nullptr, "make_slot_ue() called for inexistent rnti=0x%x,cc=%d", rnti, cc); + srsran_assert(carriers[cc] != nullptr, "make_slot_ue() called for unknown rnti=0x%x,cc=%d", rnti, cc); return slot_ue(*carriers[cc], pdcch_slot); } diff --git a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h index 20797e609..a7ce51a78 100644 --- a/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h +++ b/srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h @@ -62,11 +62,16 @@ inline sched_nr_cell_cfg_t get_default_cell_cfg(const srsran::phy_cfg_nr_t& phy_ cell_cfg.dl_center_frequency_hz = phy_cfg.carrier.dl_center_frequency_hz; cell_cfg.ul_center_frequency_hz = phy_cfg.carrier.ul_center_frequency_hz; cell_cfg.ssb_center_freq_hz = phy_cfg.carrier.ssb_center_freq_hz; - cell_cfg.offset_to_carrier = phy_cfg.carrier.offset_to_carrier; - cell_cfg.scs = phy_cfg.carrier.scs; - cell_cfg.dl_cell_nof_prb = phy_cfg.carrier.nof_prb; - cell_cfg.nof_layers = phy_cfg.carrier.max_mimo_layers; - cell_cfg.ssb_periodicity_ms = phy_cfg.ssb.periodicity_ms; + cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list.resize(1); + cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing = + (asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.carrier.scs; + cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = + phy_cfg.carrier.offset_to_carrier; + cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing = + (asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.carrier.scs; + cell_cfg.dl_cell_nof_prb = phy_cfg.carrier.nof_prb; + cell_cfg.nof_layers = phy_cfg.carrier.max_mimo_layers; + cell_cfg.ssb_periodicity_ms = phy_cfg.ssb.periodicity_ms; for (uint32_t i = 0; i < cell_cfg.ssb_positions_in_burst.in_one_group.length(); ++i) { cell_cfg.ssb_positions_in_burst.in_one_group.set(i, phy_cfg.ssb.position_in_burst[i]); } diff --git a/srsgnb/src/stack/rrc/CMakeLists.txt b/srsgnb/src/stack/rrc/CMakeLists.txt index 48324882d..dbfd778b0 100644 --- a/srsgnb/src/stack/rrc/CMakeLists.txt +++ b/srsgnb/src/stack/rrc/CMakeLists.txt @@ -22,7 +22,7 @@ set(SOURCES rrc_nr_config_utils.cc) add_library(srsgnb_rrc_config_utils STATIC ${SOURCES}) target_link_libraries(srsgnb_rrc_config_utils srsran_phy) -set(SOURCES rrc_nr.cc rrc_nr_ue.cc rrc_nr_security_context.cc cell_asn1_config.cc) +set(SOURCES rrc_nr.cc rrc_nr_ue.cc rrc_nr_security_context.cc cell_asn1_config.cc rrc_nr_du_manager.cc) add_library(srsgnb_rrc STATIC ${SOURCES}) target_link_libraries(srsgnb_rrc srsgnb_rrc_config_utils) diff --git a/srsgnb/src/stack/rrc/cell_asn1_config.cc b/srsgnb/src/stack/rrc/cell_asn1_config.cc index 16407e186..aa6914143 100644 --- a/srsgnb/src/stack/rrc/cell_asn1_config.cc +++ b/srsgnb/src/stack/rrc/cell_asn1_config.cc @@ -131,6 +131,18 @@ void set_rach_cfg_common(const srsran_prach_cfg_t& prach_cfg, asn1::rrc_nr::rach //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// +void fill_tdd_ul_dl_config_common(const rrc_cell_cfg_nr_t& cfg, asn1::rrc_nr::tdd_ul_dl_cfg_common_s& tdd) +{ + srsran_assert(cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD, "This function should only be called for TDD configs"); + // TDD UL-DL config + tdd.ref_subcarrier_spacing.value = (asn1::rrc_nr::subcarrier_spacing_opts::options)cfg.phy_cell.carrier.scs; + tdd.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10; + tdd.pattern1.nrof_dl_slots = 6; + tdd.pattern1.nrof_dl_symbols = 0; + tdd.pattern1.nrof_ul_slots = 4; + tdd.pattern1.nrof_ul_symbols = 0; +} + /// 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) { @@ -174,7 +186,7 @@ int fill_csi_report_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_me 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() = 9; + csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 1; } else { csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 7; } @@ -224,11 +236,11 @@ void fill_nzp_csi_rs_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_m auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list; // item 0 nzp_csi_res[0].nzp_csi_rs_res_id = 0; - nzp_csi_res[0].res_map.freq_domain_alloc.set_other(); - nzp_csi_res[0].res_map.freq_domain_alloc.other().from_number(0b100000); - nzp_csi_res[0].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p2; + nzp_csi_res[0].res_map.freq_domain_alloc.set_row2(); + nzp_csi_res[0].res_map.freq_domain_alloc.row2().from_number(0x800); + nzp_csi_res[0].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; nzp_csi_res[0].res_map.first_ofdm_symbol_in_time_domain = 4; - nzp_csi_res[0].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::fd_cdm2; + nzp_csi_res[0].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; nzp_csi_res[0].res_map.density.set_one(); nzp_csi_res[0].res_map.freq_band.start_rb = 0; nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52; @@ -246,7 +258,7 @@ void fill_nzp_csi_rs_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_m 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.freq_domain_alloc.row1().from_number(0x1); nzp_csi_res[1].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1; nzp_csi_res[1].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm; nzp_csi_res[1].res_map.density.set_three(); @@ -425,8 +437,8 @@ void fill_csi_resource_cfg_to_add(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_m 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); + auto& im_res = csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_rs_res_set_list.set_csi_im_res_set_list(); + im_res.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; @@ -603,10 +615,10 @@ void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg uint32_t j = 0, j2 = 0; for (uint32_t i = 0; i < out.res_to_add_mod_list.size(); ++i) { out.res_to_add_mod_list[i].pucch_res_id = i; - out.res_to_add_mod_list[i].intra_slot_freq_hop_present = true; - out.res_to_add_mod_list[i].second_hop_prb_present = true; + out.res_to_add_mod_list[i].intra_slot_freq_hop_present = false; if (i < 8 or i == 16) { out.res_to_add_mod_list[i].start_prb = 51; + out.res_to_add_mod_list[i].second_hop_prb_present = true; out.res_to_add_mod_list[i].second_hop_prb = 0; out.res_to_add_mod_list[i].format.set_format1().init_cyclic_shift = (4 * (j % 3)); out.res_to_add_mod_list[i].format.format1().nrof_symbols = 14; @@ -615,6 +627,7 @@ void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg j++; } else if (i < 15) { out.res_to_add_mod_list[i].start_prb = 1; + out.res_to_add_mod_list[i].second_hop_prb_present = true; out.res_to_add_mod_list[i].second_hop_prb = 50; out.res_to_add_mod_list[i].format.set_format2().nrof_prbs = 1; out.res_to_add_mod_list[i].format.format2().nrof_symbols = 2; @@ -622,6 +635,7 @@ void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg j2++; } else { out.res_to_add_mod_list[i].start_prb = 50; + out.res_to_add_mod_list[i].second_hop_prb_present = true; out.res_to_add_mod_list[i].second_hop_prb = 1; out.res_to_add_mod_list[i].format.set_format2().nrof_prbs = 1; out.res_to_add_mod_list[i].format.format2().nrof_symbols = 2; @@ -637,6 +651,9 @@ void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg out.format2.set_setup(); out.format2.setup().max_code_rate_present = true; out.format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25; + // NOTE: IMPORTANT!! The gNB expects the CSI to be reported along with HARQ-ACK + // If simul_harq_ack_csi_present = false, PUCCH might not be decoded properly when CSI is reported + out.format2.setup().simul_harq_ack_csi_present = true; // SR resources out.sched_request_res_to_add_mod_list.resize(1); @@ -865,18 +882,15 @@ int fill_serv_cell_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, ser serv_common.ss_pbch_block_pwr = cell_cfg.phy_cell.pdsch.rs_power; 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 = 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.dmrs_type_a_position = 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; - } + serv_common.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_s::ssb_periodicity_serving_cell_opts::ms10; // Fill SSB config serv_common.ssb_positions_in_burst_present = true; @@ -897,13 +911,7 @@ int fill_serv_cell_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, ser 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; + fill_tdd_ul_dl_config_common(cfg.cell_list[cc], serv_common.tdd_ul_dl_cfg_common); } serv_common.ul_cfg_common_present = true; @@ -1057,9 +1065,6 @@ int fill_master_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1 fill_sp_cell_cfg_from_enb_cfg(cfg, cc, out.sp_cell_cfg); out.sp_cell_cfg.recfg_with_sync_present = false; - // The current CSI config make the PUCCH decoding fail. We temporarily disable it until further investigation - out.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present = false; - return SRSRAN_SUCCESS; } @@ -1139,12 +1144,12 @@ void fill_dl_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, dl_cfg_common_sib cfg.freq_info_dl.freq_band_list.resize(1); cfg.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present = true; cfg.freq_info_dl.freq_band_list[0].freq_band_ind_nr = cell_cfg.band; - double ssb_freq_start = cell_cfg.ssb_cfg.ssb_freq_hz - SRSRAN_SSB_BW_SUBC * scs_hz / 2; + double ssb_freq_start = cell_cfg.ssb_freq_hz - SRSRAN_SSB_BW_SUBC * scs_hz / 2; double offset_point_a_hz = ssb_freq_start - band_helper.nr_arfcn_to_freq(cell_cfg.dl_absolute_freq_point_a); uint32_t offset_point_a_prbs = offset_point_a_hz / prb_bw; cfg.freq_info_dl.offset_to_point_a = offset_point_a_prbs; cfg.freq_info_dl.scs_specific_carrier_list.resize(1); - cfg.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = 0; + cfg.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier; cfg.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing = (subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs; cfg.freq_info_dl.scs_specific_carrier_list[0].carrier_bw = cell_cfg.phy_cell.carrier.nof_prb; @@ -1174,7 +1179,7 @@ void fill_ul_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, ul_cfg_common_sib band_helper.get_abs_freq_point_a_arfcn(cell_cfg.phy_cell.carrier.nof_prb, cell_cfg.ul_arfcn); cfg.freq_info_ul.scs_specific_carrier_list.resize(1); - cfg.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = 0; + cfg.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier; cfg.freq_info_ul.scs_specific_carrier_list[0].subcarrier_spacing = (subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs; cfg.freq_info_ul.scs_specific_carrier_list[0].carrier_bw = cell_cfg.phy_cell.carrier.nof_prb; @@ -1210,7 +1215,7 @@ void fill_ul_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, ul_cfg_common_sib cfg.time_align_timer_common.value = time_align_timer_opts::infinity; } -void fill_serv_cell_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, serving_cell_cfg_common_sib_s& cfg) +int fill_serv_cell_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, serving_cell_cfg_common_sib_s& cfg) { fill_dl_cfg_common_sib(cell_cfg, cfg.dl_cfg_common); @@ -1219,9 +1224,21 @@ void fill_serv_cell_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, serving_ce cfg.ssb_positions_in_burst.in_one_group.from_number(0x80); - cfg.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::ms20; + cfg.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::ms10; + + // The time advance offset is not supported by the current PHY + cfg.n_timing_advance_offset_present = true; + cfg.n_timing_advance_offset = serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n0; + + // TDD UL-DL config + if (cell_cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD) { + cfg.tdd_ul_dl_cfg_common_present = true; + fill_tdd_ul_dl_config_common(cell_cfg, cfg.tdd_ul_dl_cfg_common); + } cfg.ss_pbch_block_pwr = cell_cfg.phy_cell.pdsch.rs_power; + + return SRSRAN_SUCCESS; } int fill_sib1_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::sib1_s& sib1) @@ -1266,7 +1283,7 @@ int fill_sib1_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::s // sib1.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag = 0; sib1.serving_cell_cfg_common_present = true; - fill_serv_cell_cfg_common_sib(cell_cfg, sib1.serving_cell_cfg_common); + HANDLE_ERROR(fill_serv_cell_cfg_common_sib(cell_cfg, sib1.serving_cell_cfg_common)); sib1.ue_timers_and_consts_present = true; sib1.ue_timers_and_consts.t300.value = ue_timers_and_consts_s::t300_opts::ms1000; diff --git a/srsgnb/src/stack/rrc/rrc_nr.cc b/srsgnb/src/stack/rrc/rrc_nr.cc index 06e6da6dc..82c281091 100644 --- a/srsgnb/src/stack/rrc/rrc_nr.cc +++ b/srsgnb/src/stack/rrc/rrc_nr.cc @@ -23,6 +23,7 @@ #include "srsenb/hdr/common/common_enb.h" #include "srsgnb/hdr/stack/rrc/cell_asn1_config.h" #include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h" +#include "srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h" #include "srsgnb/hdr/stack/rrc/rrc_nr_ue.h" #include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h" #include "srsran/asn1/rrc_nr_utils.h" @@ -77,6 +78,11 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_, cell.ssb_absolute_freq_point); } + du_cfg = std::make_unique(cfg); + for (uint32_t i = 0; i < cfg.cell_list.size(); ++i) { + du_cfg->add_cell(); + } + // Generate cell config structs cell_ctxt.reset(new cell_ctxt_t{}); if (cfg.is_standalone) { @@ -98,13 +104,13 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_, 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"); - pdcch_cfg_common_s* asn1_pdcch; + const pdcch_cfg_common_s* asn1_pdcch; if (not cfg.is_standalone) { // Fill rrc_nr_cfg with UE-specific search spaces and coresets asn1_pdcch = &base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(); } else { - asn1_pdcch = &cell_ctxt->sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(); + asn1_pdcch = &du_cfg->cell(0).serv_cell_cfg_common().dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(); } srsran_assert(check_nr_phy_cell_cfg_valid(cfg.cell_list[0].phy_cell) == SRSRAN_SUCCESS, "Invalid PhyCell Config"); @@ -293,7 +299,13 @@ void rrc_nr::config_phy() common_cfg.pdcch = cfg.cell_list[0].phy_cell.pdcch; common_cfg.prach = cfg.cell_list[0].phy_cell.prach; common_cfg.duplex_mode = cfg.cell_list[0].duplex_mode; - common_cfg.ssb = cfg.cell_list[0].ssb_cfg; + common_cfg.ssb = {}; + common_cfg.ssb.center_freq_hz = cfg.cell_list[0].phy_cell.dl_freq_hz; + common_cfg.ssb.ssb_freq_hz = cfg.cell_list[0].ssb_freq_hz; + common_cfg.ssb.scs = cfg.cell_list[0].ssb_scs; + common_cfg.ssb.pattern = cfg.cell_list[0].ssb_pattern; + common_cfg.ssb.duplex_mode = cfg.cell_list[0].duplex_mode; + common_cfg.ssb.periodicity_ms = du_cfg->cell(0).serv_cell_cfg_common().ssb_periodicity_serving_cell.to_number(); if (phy->set_common_cfg(common_cfg) < SRSRAN_SUCCESS) { logger.error("Couldn't set common PHY config"); return; @@ -302,54 +314,48 @@ void rrc_nr::config_phy() void rrc_nr::config_mac() { + uint32_t cc = 0; // Fill MAC scheduler configuration for SIBs // TODO: use parsed cell NR cfg configuration srsran::phy_cfg_nr_default_t::reference_cfg_t ref_args{}; - ref_args.duplex = cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_TDD + ref_args.duplex = cfg.cell_list[cc].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; std::vector sched_cells_cfg(1, get_default_cell_cfg(srsran::phy_cfg_nr_default_t{ref_args})); - sched_nr_cell_cfg_t& cell = sched_cells_cfg[0]; + sched_nr_cell_cfg_t& cell = sched_cells_cfg[cc]; // Derive cell config from rrc_nr_cfg_t - cell.bwps[0].pdcch = cfg.cell_list[0].phy_cell.pdcch; - cell.pci = cfg.cell_list[0].phy_cell.carrier.pci; - cell.nof_layers = cfg.cell_list[0].phy_cell.carrier.max_mimo_layers; - cell.dl_cell_nof_prb = cfg.cell_list[0].phy_cell.carrier.nof_prb; - cell.ul_cell_nof_prb = cfg.cell_list[0].phy_cell.carrier.nof_prb; - cell.dl_center_frequency_hz = cfg.cell_list[0].phy_cell.carrier.dl_center_frequency_hz; - cell.ul_center_frequency_hz = cfg.cell_list[0].phy_cell.carrier.ul_center_frequency_hz; - cell.ssb_center_freq_hz = cfg.cell_list[0].phy_cell.carrier.ssb_center_freq_hz; - cell.offset_to_carrier = cfg.cell_list[0].phy_cell.carrier.offset_to_carrier; - cell.scs = cfg.cell_list[0].phy_cell.carrier.scs; + cell.bwps[0].pdcch = cfg.cell_list[cc].phy_cell.pdcch; + cell.pci = cfg.cell_list[cc].phy_cell.carrier.pci; + cell.nof_layers = cfg.cell_list[cc].phy_cell.carrier.max_mimo_layers; + cell.dl_cell_nof_prb = cfg.cell_list[cc].phy_cell.carrier.nof_prb; + cell.ul_cell_nof_prb = cfg.cell_list[cc].phy_cell.carrier.nof_prb; + cell.dl_center_frequency_hz = cfg.cell_list[cc].phy_cell.carrier.dl_center_frequency_hz; + cell.ul_center_frequency_hz = cfg.cell_list[cc].phy_cell.carrier.ul_center_frequency_hz; + cell.ssb_center_freq_hz = cfg.cell_list[cc].phy_cell.carrier.ssb_center_freq_hz; + cell.dmrs_type_a_position = du_cfg->cell(cc).mib.dmrs_type_a_position; + cell.pdcch_cfg_sib1 = du_cfg->cell(cc).mib.pdcch_cfg_sib1; + if (du_cfg->cell(cc).serv_cell_cfg_common().tdd_ul_dl_cfg_common_present) { + cell.tdd_ul_dl_cfg_common.emplace(du_cfg->cell(cc).serv_cell_cfg_common().tdd_ul_dl_cfg_common); + } + cell.dl_cfg_common = du_cfg->cell(cc).serv_cell_cfg_common().dl_cfg_common; + cell.ul_cfg_common = du_cfg->cell(cc).serv_cell_cfg_common().ul_cfg_common; + cell.ss_pbch_block_power = du_cfg->cell(cc).serv_cell_cfg_common().ss_pbch_block_pwr; if (not cfg.is_standalone) { const serving_cell_cfg_common_s& serv_cell = base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common; // Derive cell config from ASN1 bool valid_cfg = srsran::make_pdsch_cfg_from_serv_cell(base_sp_cell_cfg.sp_cell_cfg_ded, &cell.bwps[0].pdsch); srsran_assert(valid_cfg, "Invalid NR cell configuration."); - if (serv_cell.tdd_ul_dl_cfg_common_present) { - cell.tdd_ul_dl_cfg_common.emplace(serv_cell.tdd_ul_dl_cfg_common); - } cell.ssb_positions_in_burst.in_one_group.set(0, true); - cell.ssb_periodicity_ms = serv_cell.ssb_periodicity_serving_cell.to_number(); - cell.ssb_scs = serv_cell.ssb_subcarrier_spacing; - cell.ss_pbch_block_power = serv_cell.ss_pbch_block_pwr; + cell.ssb_periodicity_ms = serv_cell.ssb_periodicity_serving_cell.to_number(); + cell.ssb_scs = serv_cell.ssb_subcarrier_spacing; } else { - const serving_cell_cfg_common_sib_s& serv_cell = cell_ctxt->sib1.serving_cell_cfg_common; - cell.bwps[0].pdsch.p_zp_csi_rs_set = {}; + cell.bwps[0].pdsch.p_zp_csi_rs_set = {}; bzero(cell.bwps[0].pdsch.nzp_csi_rs_sets, sizeof(cell.bwps[0].pdsch.nzp_csi_rs_sets)); - cell.dl_cfg_common.reset(new dl_cfg_common_sib_s{serv_cell.dl_cfg_common}); - cell.ul_cfg_common.reset(new ul_cfg_common_sib_s{serv_cell.ul_cfg_common}); - if (serv_cell.tdd_ul_dl_cfg_common_present) { - cell.tdd_ul_dl_cfg_common.emplace(serv_cell.tdd_ul_dl_cfg_common); - } - cell.ssb_positions_in_burst = serv_cell.ssb_positions_in_burst; - cell.ssb_periodicity_ms = serv_cell.ssb_periodicity_serving_cell.to_number(); + cell.ssb_positions_in_burst = du_cfg->cell(cc).serv_cell_cfg_common().ssb_positions_in_burst; + cell.ssb_periodicity_ms = du_cfg->cell(cc).serv_cell_cfg_common().ssb_periodicity_serving_cell.to_number(); cell.ssb_scs.value = (subcarrier_spacing_e::options)cfg.cell_list[0].phy_cell.carrier.scs; - cell.ss_pbch_block_power = serv_cell.ss_pbch_block_pwr; } - cell.dmrs_type_a_position = cell_ctxt->mib.dmrs_type_a_position; - cell.pdcch_cfg_sib1 = cell_ctxt->mib.pdcch_cfg_sib1; // Set SIB1 and SI messages cell.sibs.resize(cell_ctxt->sib_buffer.size()); @@ -373,26 +379,6 @@ void rrc_nr::config_mac() int32_t rrc_nr::generate_sibs() { - // MIB packing - fill_mib_from_enb_cfg(cfg.cell_list[0], cell_ctxt->mib); - bcch_bch_msg_s mib_msg; - mib_msg.msg.set_mib() = cell_ctxt->mib; - { - srsran::unique_byte_buffer_t mib_buf = srsran::make_byte_buffer(); - if (mib_buf == nullptr) { - logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); - return SRSRAN_ERROR; - } - asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom()); - if (mib_msg.pack(bref) != asn1::SRSASN_SUCCESS) { - logger.error("Couldn't pack mib msg"); - return SRSRAN_ERROR; - } - mib_buf->N_bytes = bref.distance_bytes(); - logger.debug(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)", mib_buf->N_bytes); - cell_ctxt->mib_buffer = std::move(mib_buf); - } - if (not cfg.is_standalone) { return SRSRAN_SUCCESS; } diff --git a/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc b/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc index 0f1de2dae..157eed11a 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_config_utils.cc @@ -117,7 +117,7 @@ int derive_coreset0_params(rrc_cell_cfg_nr_t& cell) (ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0; int ret = srsran_coreset_zero(cell.phy_cell.carrier.pci, ssb_pointA_freq_offset_Hz, - cell.ssb_cfg.scs, + cell.ssb_scs, cell.phy_cell.carrier.scs, cell.coreset0_idx, &cell.phy_cell.pdcch.coreset[0]); @@ -131,7 +131,7 @@ int derive_ssb_params(bool is_sa, srsran_subcarrier_spacing_t pdcch_scs, uint32_t coreset0_idx, uint32_t nof_prb, - srsran_ssb_cfg_t& ssb) + rrc_cell_cfg_nr_t& cell) { // Verify essential parameters are specified and valid ERROR_IF_NOT(dl_arfcn > 0, "Invalid DL ARFCN=%d", dl_arfcn); @@ -145,20 +145,17 @@ int derive_ssb_params(bool is_sa, double dl_freq_hz = band_helper.nr_arfcn_to_freq(dl_arfcn); uint32_t dl_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(nof_prb, dl_arfcn); - ssb.center_freq_hz = dl_freq_hz; - ssb.duplex_mode = band_helper.get_duplex_mode(band); - // derive SSB pattern and scs - ssb.pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz); - if (ssb.pattern == SRSRAN_SSB_PATTERN_A) { + cell.ssb_pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz); + if (cell.ssb_pattern == SRSRAN_SSB_PATTERN_A) { // 15kHz SSB SCS - ssb.scs = srsran_subcarrier_spacing_15kHz; + cell.ssb_scs = srsran_subcarrier_spacing_15kHz; } else { // try to optain SSB pattern for same band with 30kHz SCS - ssb.pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_30kHz); - if (ssb.pattern == SRSRAN_SSB_PATTERN_B || ssb.pattern == SRSRAN_SSB_PATTERN_C) { + cell.ssb_pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_30kHz); + if (cell.ssb_pattern == SRSRAN_SSB_PATTERN_B || cell.ssb_pattern == SRSRAN_SSB_PATTERN_C) { // SSB SCS is 30 kHz - ssb.scs = srsran_subcarrier_spacing_30kHz; + cell.ssb_scs = srsran_subcarrier_spacing_30kHz; } else { srsran_terminate("Can't derive SSB pattern from band %d", band); } @@ -168,29 +165,20 @@ int derive_ssb_params(bool is_sa, int coreset0_rb_offset = 0; if (is_sa) { // Get offset in RBs between CORESET#0 and SSB - coreset0_rb_offset = srsran_coreset0_ssb_offset(coreset0_idx, ssb.scs, pdcch_scs); + coreset0_rb_offset = srsran_coreset0_ssb_offset(coreset0_idx, cell.ssb_scs, pdcch_scs); ERROR_IF_NOT(coreset0_rb_offset >= 0, "Failed to compute RB offset between CORESET#0 and SSB"); } else { // TODO: Verify if specified SSB frequency is valid } uint32_t ssb_abs_freq_point = - band_helper.get_abs_freq_ssb_arfcn(band, ssb.scs, dl_absolute_freq_point_a, coreset0_rb_offset); + band_helper.get_abs_freq_ssb_arfcn(band, cell.ssb_scs, dl_absolute_freq_point_a, coreset0_rb_offset); ERROR_IF_NOT(ssb_abs_freq_point > 0, "Can't derive SSB freq point for dl_arfcn=%d and band %d", band_helper.freq_to_nr_arfcn(dl_freq_hz), band); // Convert to frequency for PHY - ssb.ssb_freq_hz = band_helper.nr_arfcn_to_freq(ssb_abs_freq_point); - - ssb.periodicity_ms = 10; // TODO: make a param - ssb.beta_pss = 0.0; - ssb.beta_sss = 0.0; - ssb.beta_pbch = 0.0; - ssb.beta_pbch_dmrs = 0.0; - // set by PHY layer in worker_pool::set_common_cfg - ssb.srate_hz = 0.0; - ssb.scaling = 0.0; + cell.ssb_freq_hz = band_helper.nr_arfcn_to_freq(ssb_abs_freq_point); return SRSRAN_SUCCESS; } @@ -259,9 +247,9 @@ int set_derived_nr_cell_params(bool is_sa, rrc_cell_cfg_nr_t& cell) cell.phy_cell.carrier.scs, cell.coreset0_idx, cell.phy_cell.carrier.nof_prb, - cell.ssb_cfg); - cell.phy_cell.carrier.ssb_center_freq_hz = cell.ssb_cfg.ssb_freq_hz; - cell.ssb_absolute_freq_point = band_helper.freq_to_nr_arfcn(cell.ssb_cfg.ssb_freq_hz); + cell); + cell.phy_cell.carrier.ssb_center_freq_hz = cell.ssb_freq_hz; + cell.ssb_absolute_freq_point = band_helper.freq_to_nr_arfcn(cell.ssb_freq_hz); // Derive remaining config params if (is_sa) { @@ -346,7 +334,6 @@ int set_derived_nr_rrc_params(rrc_nr_cfg_t& rrc_cfg) int check_nr_cell_cfg_valid(const rrc_cell_cfg_nr_t& cell, bool is_sa) { // verify SSB params are consistent - ERROR_IF_NOT(cell.ssb_cfg.center_freq_hz == cell.phy_cell.dl_freq_hz, "Invalid SSB param generation"); HANDLE_ERROR(check_nr_phy_cell_cfg_valid(cell.phy_cell)); if (is_sa) { diff --git a/srsgnb/src/stack/rrc/rrc_nr_du_manager.cc b/srsgnb/src/stack/rrc/rrc_nr_du_manager.cc new file mode 100644 index 000000000..7faf37b17 --- /dev/null +++ b/srsgnb/src/stack/rrc/rrc_nr_du_manager.cc @@ -0,0 +1,96 @@ +/** + * + * \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 "srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h" +#include "srsgnb/hdr/stack/rrc/cell_asn1_config.h" +#include "srsran/common/string_helpers.h" + +using namespace asn1::rrc_nr; + +namespace srsenb { + +du_config_manager::du_config_manager(const rrc_nr_cfg_t& cfg_) : cfg(cfg_), logger(srslog::fetch_basic_logger("RRC-NR")) +{} + +du_config_manager::~du_config_manager() {} + +int du_config_manager::add_cell() +{ + // add cell + std::unique_ptr obj = std::make_unique(); + du_cell_config& cell = *obj; + cell.cc = cells.size(); + + // Fill general cell params + cell.pci = cfg.cell_list[cell.cc].phy_cell.carrier.pci; + + // fill MIB ASN.1 + if (fill_mib_from_enb_cfg(cfg.cell_list[cell.cc], cell.mib) != SRSRAN_SUCCESS) { + return SRSRAN_ERROR; + } + + // Pack MIB + cell.packed_mib = srsran::make_byte_buffer(); + if (cell.packed_mib == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + { + asn1::bit_ref bref(cell.packed_mib->msg, cell.packed_mib->get_tailroom()); + bcch_bch_msg_s bch_msg; + bch_msg.msg.set_mib() = cell.mib; + if (bch_msg.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Couldn't pack mib msg"); + return SRSRAN_ERROR; + } + cell.packed_mib->N_bytes = bref.distance_bytes(); + } + logger.info( + cell.packed_mib->data(), cell.packed_mib->size(), "BCCH-BCH Message (with MIB) (%d B)", cell.packed_mib->size()); + asn1::json_writer js; + cell.mib.to_json(js); + logger.info("MIB content: %s", js.to_string().c_str()); + + // fill SIB1 ASN.1 + if (fill_sib1_from_enb_cfg(cfg, cell.cc, cell.sib1) != SRSRAN_SUCCESS) { + logger.error("Couldn't generate SIB1"); + return SRSRAN_ERROR; + } + + // Pack SIB1 + cell.packed_sib1 = srsran::make_byte_buffer(); + if (cell.packed_sib1 == nullptr) { + logger.error("Couldn't allocate PDU in %s().", __FUNCTION__); + return SRSRAN_ERROR; + } + { + asn1::bit_ref bref(cell.packed_sib1->msg, cell.packed_sib1->get_tailroom()); + bcch_dl_sch_msg_s bcch_msg; + bcch_msg.msg.set_c1().set_sib_type1() = cell.sib1; + if (bcch_msg.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Couldn't pack SIB1 msg"); + return SRSRAN_ERROR; + } + cell.packed_sib1->N_bytes = bref.distance_bytes(); + } + logger.info(cell.packed_sib1->data(), + cell.packed_sib1->size(), + "BCCH-DL-SCH-Message (with SIB1) (%d B)", + cell.packed_sib1->size()); + cell.sib1.to_json(js); + logger.info("SIB1 content: %s", js.to_string().c_str()); + + cells.push_back(std::move(obj)); + return SRSRAN_SUCCESS; +} + +} // namespace srsenb \ No newline at end of file diff --git a/srsgnb/src/stack/rrc/rrc_nr_security_context.cc b/srsgnb/src/stack/rrc/rrc_nr_security_context.cc index 4fecc2da4..a51dd0446 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_security_context.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_security_context.cc @@ -192,7 +192,7 @@ void nr_security_context::generate_as_keys() logger.info(sec_cfg.k_nr_rrc_enc.data(), 32, "NR RRC Encryption Key (k_nr_rrc_enc)"); logger.info(sec_cfg.k_nr_rrc_int.data(), 32, "NR RRC Integrity Key (k_nr_rrc_int)"); logger.info(sec_cfg.k_nr_up_enc.data(), 32, "NR UP Encryption Key (k_nr_up_enc)"); - logger.info(sec_cfg.k_nr_up_int.data(), 32, "NR UP Encryption Key (k_nr_up_enc)"); + logger.info(sec_cfg.k_nr_up_int.data(), 32, "NR UP Integrity Key (k_nr_up_int)"); } void nr_security_context::regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_arfcn) diff --git a/srsgnb/src/stack/rrc/rrc_nr_ue.cc b/srsgnb/src/stack/rrc/rrc_nr_ue.cc index c8de551c9..88be51118 100644 --- a/srsgnb/src/stack/rrc/rrc_nr_ue.cc +++ b/srsgnb/src/stack/rrc/rrc_nr_ue.cc @@ -1117,20 +1117,8 @@ void rrc_nr::ue::handle_rrc_setup_complete(const asn1::rrc_nr::rrc_setup_complet /// TS 38.331, SecurityModeCommand void rrc_nr::ue::send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu) { - // FIXME: Currently we are using the PDCP-LTE, so we need to convert from nr_as_security_cfg to as_security_config. - // When we start using PDCP-NR we can avoid this step. - srsran::nr_as_security_config_t tmp_cnfg = sec_ctx.get_as_sec_cfg(); - srsran::as_security_config_t pdcp_cnfg = {}; - pdcp_cnfg.k_rrc_int = tmp_cnfg.k_nr_rrc_int; - pdcp_cnfg.k_rrc_enc = tmp_cnfg.k_nr_rrc_enc; - pdcp_cnfg.k_up_int = tmp_cnfg.k_nr_up_int; - pdcp_cnfg.k_up_enc = tmp_cnfg.k_nr_up_enc; - pdcp_cnfg.integ_algo = (srsran::INTEGRITY_ALGORITHM_ID_ENUM)tmp_cnfg.integ_algo; - pdcp_cnfg.cipher_algo = (srsran::CIPHERING_ALGORITHM_ID_ENUM)tmp_cnfg.cipher_algo; - - // Setup SRB1 security/integrity. Encryption is set on completion - parent->pdcp->config_security(rnti, srb_to_lcid(srsran::nr_srb::srb1), pdcp_cnfg); - parent->pdcp->enable_integrity(rnti, srb_to_lcid(srsran::nr_srb::srb1)); + // apply selected security config and enable integrity on SRB1 before generating security mode command + update_as_security(srb_to_lcid(srsran::nr_srb::srb1), true, false); if (nas_pdu != nullptr) { nas_pdu_queue.push_back(std::move(nas_pdu)); @@ -1148,11 +1136,58 @@ void rrc_nr::ue::send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu } } +/** + * @brief Internal helper to update the security configuration of a PDCP bearer + * + * If no valid AS security config is present (yet) the method doesn't modify the + * PDCP config and returns SRSRAN_ERROR. In some cases, however, + * for example during RRC Setup, this is in fact the expected behaviour as + * AS security isn't established yet. + * + * @param lcid Logical channel ID of the bearer + * @param enable_integrity Whether to enable integrity protection for the bearer + * @param enable_ciphering Whether to enable ciphering for the bearer + * @return int SRSRAN_SUCCESS if a valid AS security config was found and the security was configured + */ +int rrc_nr::ue::update_as_security(uint32_t lcid, bool enable_integrity = true, bool enable_ciphering = true) +{ + if (not sec_ctx.is_as_sec_cfg_valid()) { + parent->logger.error("Invalid AS security configuration. Skipping configuration for lcid=%d", lcid); + return SRSRAN_ERROR; + } + + // FIXME: Currently we are using the PDCP-LTE, so we need to convert from nr_as_security_cfg to as_security_config. + // When we start using PDCP-NR we can avoid this step. + srsran::nr_as_security_config_t tmp_cnfg = sec_ctx.get_as_sec_cfg(); + srsran::as_security_config_t pdcp_cnfg = {}; + pdcp_cnfg.k_rrc_int = tmp_cnfg.k_nr_rrc_int; + pdcp_cnfg.k_rrc_enc = tmp_cnfg.k_nr_rrc_enc; + pdcp_cnfg.k_up_int = tmp_cnfg.k_nr_up_int; + pdcp_cnfg.k_up_enc = tmp_cnfg.k_nr_up_enc; + pdcp_cnfg.integ_algo = (srsran::INTEGRITY_ALGORITHM_ID_ENUM)tmp_cnfg.integ_algo; + pdcp_cnfg.cipher_algo = (srsran::CIPHERING_ALGORITHM_ID_ENUM)tmp_cnfg.cipher_algo; + + // configure algorithm and keys + parent->pdcp->config_security(rnti, lcid, pdcp_cnfg); + + if (enable_integrity) { + parent->pdcp->enable_integrity(rnti, lcid); + } + + if (enable_ciphering) { + parent->pdcp->enable_encryption(rnti, lcid); + } + + return SRSRAN_SUCCESS; +} + /// TS 38.331, SecurityModeComplete void rrc_nr::ue::handle_security_mode_complete(const asn1::rrc_nr::security_mode_complete_s& msg) { parent->logger.info("SecurityModeComplete transaction ID: %d", msg.rrc_transaction_id); - parent->pdcp->enable_encryption(rnti, srb_to_lcid(srsran::nr_srb::srb1)); + + // finally, also enable ciphering on SRB1 + update_as_security(srb_to_lcid(srsran::nr_srb::srb1), false, true); send_rrc_reconfiguration(); // Note: Skip UE capabilities @@ -1366,20 +1401,8 @@ int rrc_nr::ue::update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radi } parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg); - // enable security config if (sec_ctx.is_as_sec_cfg_valid()) { - srsran::nr_as_security_config_t tmp_cnfg = sec_ctx.get_as_sec_cfg(); - srsran::as_security_config_t pdcp_cnfg = {}; - pdcp_cnfg.k_rrc_int = tmp_cnfg.k_nr_rrc_int; - pdcp_cnfg.k_rrc_enc = tmp_cnfg.k_nr_rrc_enc; - pdcp_cnfg.k_up_int = tmp_cnfg.k_nr_up_int; - pdcp_cnfg.k_up_enc = tmp_cnfg.k_nr_up_enc; - pdcp_cnfg.integ_algo = (srsran::INTEGRITY_ALGORITHM_ID_ENUM)tmp_cnfg.integ_algo; - pdcp_cnfg.cipher_algo = (srsran::CIPHERING_ALGORITHM_ID_ENUM)tmp_cnfg.cipher_algo; - - // Setup SRB1 security/integrity. Encryption is set on completion - parent->pdcp->config_security(rnti, srb_to_lcid(srsran::nr_srb::srb1), pdcp_cnfg); - parent->pdcp->enable_integrity(rnti, srb_to_lcid(srsran::nr_srb::srb1)); + update_as_security(rlc_bearer->lc_ch_id); } } @@ -1399,6 +1422,10 @@ int rrc_nr::ue::update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radi return SRSRAN_ERROR; } parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg); + + if (sec_ctx.is_as_sec_cfg_valid()) { + update_as_security(rlc_bearer->lc_ch_id); + } } return SRSRAN_SUCCESS; @@ -1449,6 +1476,14 @@ int rrc_nr::ue::update_mac(const cell_group_cfg_s& cell_group_config, bool is_co // TODO: remaining fields } } + + if (cell_group_config.sp_cell_cfg_present and cell_group_config.sp_cell_cfg.sp_cell_cfg_ded_present and + cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present and + cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp_present and + cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg_present) { + auto& pucch_cfg = cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg.setup(); + srsran::fill_phy_pucch_cfg(pucch_cfg, &uecfg.phy_cfg.pucch); + } } else { auto& pdcch = cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(); for (auto& ss : pdcch.search_spaces_to_add_mod_list) { @@ -1466,6 +1501,7 @@ int rrc_nr::ue::update_mac(const cell_group_cfg_s& cell_group_config, bool is_co uecfg.sp_cell_cfg.reset(new sp_cell_cfg_s{cell_group_cfg.sp_cell_cfg}); uecfg.mac_cell_group_cfg.reset(new mac_cell_group_cfg_s{cell_group_cfg.mac_cell_group_cfg}); uecfg.phy_cell_group_cfg.reset(new phys_cell_group_cfg_s{cell_group_cfg.phys_cell_group_cfg}); + srsran::make_csi_cfg_from_serv_cell(cell_group_config.sp_cell_cfg.sp_cell_cfg_ded, &uecfg.phy_cfg.csi); parent->mac->ue_cfg(rnti, uecfg); return SRSRAN_SUCCESS; diff --git a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc index 8095e9a5e..997b6a27e 100644 --- a/srsgnb/src/stack/rrc/test/rrc_nr_test.cc +++ b/srsgnb/src/stack/rrc/test/rrc_nr_test.cc @@ -179,6 +179,7 @@ void test_rrc_sa_connection() SRSRAN_SUCCESS); TESTASSERT_SUCCESS(rrc_obj.add_user(0x4601, 0)); + TESTASSERT_SUCCESS(rrc_obj.ue_set_security_cfg_key(0x4601, {})); test_rrc_nr_connection_establishment(task_sched, rrc_obj, rlc_obj, mac_obj, ngap_obj, 0x4601); test_rrc_nr_info_transfer(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601); diff --git a/srsue/hdr/phy/dummy_phy.h b/srsue/hdr/phy/dummy_phy.h index a53feebb7..2b079c899 100644 --- a/srsue/hdr/phy/dummy_phy.h +++ b/srsue/hdr/phy/dummy_phy.h @@ -62,7 +62,7 @@ public: void meas_stop() final {} /* Cell search and selection procedures */ - bool cell_search() final { return false; } + bool cell_search(int earfcn) final { return false; } bool cell_select(phy_cell_t cell) final { return false; } bool cell_is_camping() final { return false; } diff --git a/srsue/hdr/phy/nr/cell_search.h b/srsue/hdr/phy/nr/cell_search.h index 0ffcf1c65..362e53c87 100644 --- a/srsue/hdr/phy/nr/cell_search.h +++ b/srsue/hdr/phy/nr/cell_search.h @@ -58,8 +58,6 @@ public: bool start(const cfg_t& cfg); ret_t run_slot(const cf_t* buffer, uint32_t slot_sz); - void reset(); - private: srslog::basic_logger& logger; srsran_ssb_t ssb = {}; diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index 5dc8edb9d..a535494cf 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -77,7 +77,7 @@ public: /********** RRC INTERFACE ********************/ - bool cell_search() final; + bool cell_search(int earfcn) final; bool cell_select(phy_cell_t cell) final; // Sets the new PHY configuration for the given CC. The configuration is applied in the background. The notify() diff --git a/srsue/hdr/phy/sync.h b/srsue/hdr/phy/sync.h index 5a48be22d..2a407aee6 100644 --- a/srsue/hdr/phy/sync.h +++ b/srsue/hdr/phy/sync.h @@ -77,7 +77,7 @@ public: // RRC interface for controling the SYNC state bool cell_search_init(); - rrc_interface_phy_lte::cell_search_ret_t cell_search_start(phy_cell_t* cell); + rrc_interface_phy_lte::cell_search_ret_t cell_search_start(phy_cell_t* cell, int earfcn); bool cell_select_init(phy_cell_t cell); bool cell_select_start(phy_cell_t cell); bool cell_is_camping(); diff --git a/srsue/hdr/stack/rrc/phy_controller.h b/srsue/hdr/stack/rrc/phy_controller.h index 456df9cdf..300b091b6 100644 --- a/srsue/hdr/stack/rrc/phy_controller.h +++ b/srsue/hdr/stack/rrc/phy_controller.h @@ -49,7 +49,9 @@ public: struct cell_sel_cmd { phy_cell_t phy_cell; }; - struct cell_search_cmd {}; + struct cell_search_cmd { + int earfcn; + }; struct in_sync_ev { static const bool log_verbose = false; }; @@ -61,7 +63,7 @@ public: // PHY procedures interfaces bool start_cell_select(const phy_cell_t& phy_cell, srsran::event_observer observer = {}); - bool start_cell_search(srsran::event_observer observer); + bool start_cell_search(srsran::event_observer observer, int earfcn); void cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t found_cell); void cell_selection_completed(bool outcome); void in_sync(); @@ -126,7 +128,7 @@ public: // clang-format on }; struct searching_cell { - void enter(phy_controller* f); + void enter(phy_controller* f, const cell_search_cmd& ev); }; private: diff --git a/srsue/hdr/stack/rrc/rrc.h b/srsue/hdr/stack/rrc/rrc.h index efe2c7057..b0284ee75 100644 --- a/srsue/hdr/stack/rrc/rrc.h +++ b/srsue/hdr/stack/rrc/rrc.h @@ -220,6 +220,9 @@ private: meas_cell_list meas_cells_nr; + // if this is set to a valid earfcn, this earfcn will be used for cell search + int cell_search_earfcn = -1; + bool initiated = false; asn1::rrc::reest_cause_e m_reest_cause = asn1::rrc::reest_cause_e::nulltype; uint16_t m_reest_rnti = 0; @@ -337,7 +340,8 @@ private: bool con_reconfig_ho(const asn1::rrc::rrc_conn_recfg_s& reconfig); void ho_failed(); void start_go_idle(); - void rrc_connection_release(const std::string& cause); + void handle_rrc_connection_release(const asn1::rrc::rrc_conn_release_s& release); + void start_rrc_redirect(uint32_t new_dl_earfcn); void radio_link_failure_push_cmd(); void radio_link_failure_process(); void leave_connected(); diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr.h b/srsue/hdr/stack/rrc_nr/rrc_nr.h index 3a869f896..3e8c6765b 100644 --- a/srsue/hdr/stack/rrc_nr/rrc_nr.h +++ b/srsue/hdr/stack/rrc_nr/rrc_nr.h @@ -24,6 +24,7 @@ #include "../rrc/rrc_cell.h" #include "rrc_nr_config.h" +#include "rrc_nr_metrics.h" #include "srsran/adt/circular_map.h" #include "srsran/asn1/rrc_nr.h" #include "srsran/asn1/rrc_nr_utils.h" @@ -44,8 +45,6 @@ class usim_interface_rrc_nr; class pdcp_interface_rrc; class rlc_interface_rrc; -struct rrc_nr_metrics_t {}; - class rrc_nr final : public rrc_interface_phy_nr, public rrc_interface_pdcp, public rrc_interface_rlc, @@ -146,6 +145,7 @@ private: // senders void send_setup_request(srsran::nr_establishment_cause_t cause); void send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg); + void send_rrc_reconfig_complete(); void send_ul_info_transfer(srsran::unique_byte_buffer_t nas_msg); void send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg); void send_ul_dcch_msg(uint32_t lcid, const asn1::rrc_nr::ul_dcch_msg_s& msg); @@ -193,14 +193,6 @@ private: uint32_t sim_measurement_carrier_freq_r15; srsran::timer_handler::unique_timer sim_measurement_timer; - /// RRC states (3GPP 38.331 v15.5.1 Sec 4.2.1) - enum rrc_nr_state_t { - RRC_NR_STATE_IDLE = 0, - RRC_NR_STATE_CONNECTED, - RRC_NR_STATE_CONNECTED_INACTIVE, - RRC_NR_STATE_N_ITEMS, - }; - const static char* rrc_nr_state_text[RRC_NR_STATE_N_ITEMS]; rrc_nr_state_t state = RRC_NR_STATE_IDLE; uint8_t transaction_id = 0; diff --git a/srsue/hdr/stack/rrc_nr/rrc_nr_metrics.h b/srsue/hdr/stack/rrc_nr/rrc_nr_metrics.h new file mode 100644 index 000000000..a224f9708 --- /dev/null +++ b/srsue/hdr/stack/rrc_nr/rrc_nr_metrics.h @@ -0,0 +1,32 @@ +/** + * + * \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 SRSUE_RRC_NR_METRICS_H +#define SRSUE_RRC_NR_METRICS_H + +namespace srsue { + +/// RRC states (3GPP 38.331 v15.5.1 Sec 4.2.1) +enum rrc_nr_state_t { + RRC_NR_STATE_IDLE = 0, + RRC_NR_STATE_CONNECTED, + RRC_NR_STATE_CONNECTED_INACTIVE, + RRC_NR_STATE_N_ITEMS, +}; + +struct rrc_nr_metrics_t { + rrc_nr_state_t state; +}; + +} // namespace srsue + +#endif // SRSUE_RRC_NR_METRICS_H diff --git a/srsue/hdr/stack/upper/nas.h b/srsue/hdr/stack/upper/nas.h index fb51f7504..a6d3a61a4 100644 --- a/srsue/hdr/stack/upper/nas.h +++ b/srsue/hdr/stack/upper/nas.h @@ -89,9 +89,9 @@ public: void timer_expired(uint32_t timeout_id) override; private: - rrc_interface_nas* rrc = nullptr; - usim_interface_nas* usim = nullptr; - gw_interface_nas* gw = nullptr; + rrc_interface_nas* rrc = nullptr; + usim_interface_nas* usim = nullptr; + gw_interface_nas* gw = nullptr; bool running = false; @@ -117,10 +117,10 @@ private: typedef std::pair eps_bearer_map_pair_t; eps_bearer_map_t eps_bearer; - bool have_guti = false; - bool have_ctxt = false; - bool auth_request = false; - uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS; + bool have_guti = false; + bool have_ctxt = false; + bool auth_request = false; + uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS; const uint32_t max_attach_attempts = 5; // Sec. 5.5.1.2.6 uint32_t attach_attempt_counter = 0; @@ -153,8 +153,8 @@ private: const uint8_t ue_svn_oct2 = 0x3; // Security - bool eia_caps[8] = {}; - bool eea_caps[8] = {}; + bool eia_caps[8] = {}; + bool eea_caps[8] = {}; // Airplane mode simulation typedef enum { DISABLED = 0, ENABLED } airplane_mode_state_t; diff --git a/srsue/hdr/stack/upper/nas_5g.h b/srsue/hdr/stack/upper/nas_5g.h index a721da520..aca631d6a 100644 --- a/srsue/hdr/stack/upper/nas_5g.h +++ b/srsue/hdr/stack/upper/nas_5g.h @@ -66,7 +66,9 @@ public: void run_tti(); // Stack+RRC interface - bool is_registered(); + bool is_registered(); + int get_k_amf(srsran::as_key_t& k_amf); + uint32_t get_ul_nas_count(); int write_pdu(srsran::unique_byte_buffer_t pdu); @@ -88,8 +90,8 @@ private: usim_interface_nas* usim = nullptr; gw_interface_nas* gw = nullptr; - bool running = false; - + bool running = false; + bool has_sec_ctxt = false; bool initial_sec_command = false; srsran::nas_5g::mobile_identity_5gs_t::guti_5g_s guti_5g; @@ -102,6 +104,8 @@ private: bool ia5g_caps[8] = {}; bool ea5g_caps[8] = {}; + void set_k_gnb_count(uint32_t count); + // TS 23.003 Sec. 6.2.2 IMEISV's last two octets are Software Version Number (SVN) // which identifies the software version number of the mobile equipment const uint8_t ue_svn_oct1 = 0x5; diff --git a/srsue/hdr/stack/upper/nas_base.h b/srsue/hdr/stack/upper/nas_base.h index a59192545..12925fd41 100644 --- a/srsue/hdr/stack/upper/nas_base.h +++ b/srsue/hdr/stack/upper/nas_base.h @@ -69,6 +69,7 @@ protected: struct nas_5g_sec_ctxt { uint8_t ksi; uint8_t k_amf[32]; + uint32_t k_gnb_count; }; nas_sec_base_ctxt ctxt_base = {}; @@ -79,7 +80,7 @@ protected: // Security void - integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac); + integrity_generate(uint8_t* key_128, uint32_t count, uint8_t direction, uint8_t* msg, uint32_t msg_len, uint8_t* mac); bool integrity_check(srsran::byte_buffer_t* pdu); void cipher_encrypt(srsran::byte_buffer_t* pdu); void cipher_decrypt(srsran::byte_buffer_t* pdu); diff --git a/srsue/hdr/stack/upper/usim_base.h b/srsue/hdr/stack/upper/usim_base.h index d72dccf87..6772ba0a4 100644 --- a/srsue/hdr/stack/upper/usim_base.h +++ b/srsue/hdr/stack/upper/usim_base.h @@ -106,6 +106,7 @@ public: void restore_keys_from_failed_ho(srsran::as_security_config_t* as_ctx) final; // NR RRC interface + void generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) final; bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) final; bool update_nr_context(srsran::as_security_config_t* sec_cfg) final; @@ -149,6 +150,8 @@ protected: uint8_t k_enb_initial[KEY_LEN] = {}; uint8_t auts[AKA_AUTS_LEN] = {}; + srsran::as_key_t k_gnb_initial = {}; + // Current K_eNB context (K_eNB, NH and NCC) srsran::k_enb_context_t k_enb_ctx = {}; srsran::k_gnb_context_t k_gnb_ctx = {}; diff --git a/srsue/hdr/ue_metrics_interface.h b/srsue/hdr/ue_metrics_interface.h index e11fb996a..33ec22088 100644 --- a/srsue/hdr/ue_metrics_interface.h +++ b/srsue/hdr/ue_metrics_interface.h @@ -31,6 +31,7 @@ #include "srsran/system/sys_metrics.h" #include "stack/mac/mac_metrics.h" #include "stack/rrc/rrc_metrics.h" +#include "stack/rrc_nr/rrc_nr_metrics.h" #include "stack/upper/gw_metrics.h" #include "stack/upper/nas_metrics.h" @@ -43,7 +44,7 @@ typedef struct { srsran::rlc_metrics_t rlc; nas_metrics_t nas; rrc_metrics_t rrc; - rrc_metrics_t rrc_nr; + rrc_nr_metrics_t rrc_nr; } stack_metrics_t; typedef struct { diff --git a/srsue/src/metrics_stdout.cc b/srsue/src/metrics_stdout.cc index 901cbd561..bbb8e78ea 100644 --- a/srsue/src/metrics_stdout.cc +++ b/srsue/src/metrics_stdout.cc @@ -190,7 +190,7 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per return; } - if (metrics.stack.rrc.state != RRC_STATE_CONNECTED) { + if (metrics.stack.rrc.state != RRC_STATE_CONNECTED && metrics.stack.rrc_nr.state != RRC_NR_STATE_CONNECTED) { fmt::print("--- disconnected ---\n"); return; } @@ -202,25 +202,30 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per display_neighbours |= metrics.stack.rrc.neighbour_cells.size() > 0; } - bool is_nr = metrics.phy_nr.nof_active_cc > 0; + bool has_lte = metrics.phy.nof_active_cc > 0; + bool has_nr = metrics.phy_nr.nof_active_cc > 0; // print table header every 10 reports if (++n_reports > 10) { - print_table(display_neighbours, is_nr); + print_table(display_neighbours, has_nr); } // also print table header if neighbours are added/removed in between if (display_neighbours != table_has_neighbours) { - print_table(display_neighbours, is_nr); + print_table(display_neighbours, has_nr); } - for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { - set_metrics_helper(metrics.phy, metrics.stack.mac, metrics.stack.rrc, display_neighbours, r, false, !is_nr); + if (has_lte) { + for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) { + set_metrics_helper(metrics.phy, metrics.stack.mac, metrics.stack.rrc, display_neighbours, r, false, !has_nr); + } } - for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) { - // Assumption LTE is followed by the NR carriers. - set_metrics_helper(metrics.phy_nr, metrics.stack.mac_nr, metrics.stack.rrc, display_neighbours, r, true, !is_nr); + if (has_nr) { + for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) { + // Assumption LTE is followed by the NR carriers. + set_metrics_helper(metrics.phy_nr, metrics.stack.mac_nr, metrics.stack.rrc, display_neighbours, r, true, !has_nr); + } } if (metrics.rf.rf_error) { diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index 4737fba78..527a38bcc 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -351,12 +351,12 @@ bool phy::cell_select(phy_cell_t cell) // This function executes one part of the procedure immediatly and returns to continue in the background. // When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH -// processing. -bool phy::cell_search() +// processing. If a valid EARFCN (>0) is given, this is used for cell search. +bool phy::cell_search(int earfcn) { sfsync.scell_sync_stop(); if (sfsync.cell_search_init()) { - cmd_worker_cell.add_cmd([this]() { + cmd_worker_cell.add_cmd([this, earfcn]() { // Wait SYNC transitions to IDLE sfsync.wait_idle(); @@ -364,7 +364,7 @@ bool phy::cell_search() reset(); phy_cell_t found_cell = {}; - rrc_interface_phy_lte::cell_search_ret_t ret = sfsync.cell_search_start(&found_cell); + rrc_interface_phy_lte::cell_search_ret_t ret = sfsync.cell_search_start(&found_cell, earfcn); stack->cell_search_complete(ret, found_cell); }); } else { diff --git a/srsue/src/phy/sync.cc b/srsue/src/phy/sync.cc index 851f7e699..19d8e98bd 100644 --- a/srsue/src/phy/sync.cc +++ b/srsue/src/phy/sync.cc @@ -188,10 +188,10 @@ void sync::reset() * */ -/* A call to cell_search() finds the strongest cell in the set of supported EARFCNs. When the first cell is found, - * returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not found in the - * current frequency it moves to the next one and the next call to cell_search() will look in the next EARFCN in the - * set. If no cells are found in any frequency it returns 0. If error returns -1. +/* A call to cell_search() finds the strongest cell at a given EARFCN or in the set of supported EARFCNs. When the first + * cell is found, returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not + * found in the current frequency it moves to the next one and the next call to cell_search() will look in the next + * EARFCN in the set. If no cells are found in any frequency it returns 0. If error returns -1. * * The first part of the procedure (call to _init()) moves the PHY To IDLE, ensuring that no UL/DL/PRACH will happen * @@ -206,7 +206,6 @@ bool sync::cell_search_init() } // Move state to IDLE - Info("Cell Search: Start EARFCN index=%u/%zd", cellsearch_earfcn_index, worker_com->args->dl_earfcn_list.size()); phy_state.go_idle(); // Stop all intra-frequency measurement before changing frequency @@ -217,10 +216,16 @@ bool sync::cell_search_init() return true; } -rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* found_cell) +rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* found_cell, int earfcn) { std::unique_lock ul(rrc_mutex); + if (earfcn < 0) { + Info("Cell Search: Start EARFCN index=%u/%zd", cellsearch_earfcn_index, worker_com->args->dl_earfcn_list.size()); + } else { + Info("Cell Search: Start EARFCN=%d", earfcn); + } + rrc_interface_phy_lte::cell_search_ret_t ret = {}; ret.found = rrc_interface_phy_lte::cell_search_ret_t::ERROR; ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS; @@ -238,16 +243,20 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou Info("SYNC: Setting Cell Search sampling rate"); } - try { - if (current_earfcn != (int)worker_com->args->dl_earfcn_list.at(cellsearch_earfcn_index)) { - current_earfcn = (int)worker_com->args->dl_earfcn_list[cellsearch_earfcn_index]; - Info("Cell Search: changing frequency to EARFCN=%d", current_earfcn); - set_frequency(); + if (earfcn < 0) { + try { + if (current_earfcn != (int)worker_com->args->dl_earfcn_list.at(cellsearch_earfcn_index)) { + current_earfcn = (int)worker_com->args->dl_earfcn_list[cellsearch_earfcn_index]; + } + } catch (const std::out_of_range& oor) { + Error("Index %d is not a valid EARFCN element.", cellsearch_earfcn_index); + return ret; } - } catch (const std::out_of_range& oor) { - Error("Index %d is not a valid EARFCN element.", cellsearch_earfcn_index); - return ret; + } else { + current_earfcn = earfcn; } + Info("Cell Search: changing frequency to EARFCN=%d", current_earfcn); + set_frequency(); // Move to CELL SEARCH and wait to finish Info("Cell Search: Setting Cell search state"); @@ -275,7 +284,7 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou } cellsearch_earfcn_index++; - if (cellsearch_earfcn_index >= worker_com->args->dl_earfcn_list.size()) { + if (cellsearch_earfcn_index >= worker_com->args->dl_earfcn_list.size() or earfcn < 0) { Info("Cell Search: No more frequencies in the current EARFCN set"); cellsearch_earfcn_index = 0; ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS; diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc index 993b23905..a8219ee66 100644 --- a/srsue/src/phy/sync_sa.cc +++ b/srsue/src/phy/sync_sa.cc @@ -224,6 +224,11 @@ void sync_sa::run_state_idle() void sync_sa::run_state_cell_search() { + // Initialise buffer + if (cell_search_nof_trials == 0) { + srsran_vec_cf_zero(rx_buffer, slot_sz); + } + // Receive samples srsran::rf_buffer_t rf_buffer = {}; rf_buffer.set_nof_samples(slot_sz); diff --git a/srsue/src/phy/test/ue_phy_test.cc b/srsue/src/phy/test/ue_phy_test.cc index daf38f1ed..cbe5c5657 100644 --- a/srsue/src/phy/test/ue_phy_test.cc +++ b/srsue/src/phy/test/ue_phy_test.cc @@ -517,7 +517,7 @@ int main(int argc, char** argv) phy_test->start(); // 1. Cell search - TESTASSERT(phy_test->get_phy_interface_rrc()->cell_search()); + TESTASSERT(phy_test->get_phy_interface_rrc()->cell_search(-1)); TESTASSERT(phy_test->get_stack()->wait_cell_search(default_timeout)); TESTASSERT(phy_test->get_stack()->cell_search_ret.found == srsue::rrc_interface_phy_lte::cell_search_ret_t::CELL_FOUND); diff --git a/srsue/src/stack/rrc/phy_controller.cc b/srsue/src/stack/rrc/phy_controller.cc index 42ce10f8a..6062b3bd1 100644 --- a/srsue/src/stack/rrc/phy_controller.cc +++ b/srsue/src/stack/rrc/phy_controller.cc @@ -175,13 +175,13 @@ void phy_controller::selecting_cell::wait_in_sync::enter(selecting_cell* f) *************************************/ //! Searches for a cell in the current frequency and retrieves SIB1 if not retrieved yet -bool phy_controller::start_cell_search(srsran::event_observer observer) +bool phy_controller::start_cell_search(srsran::event_observer observer, int earfcn) { if (is_in_state()) { fsmInfo("Cell search already launched."); return true; } - trigger(cell_search_cmd{}); + trigger(cell_search_cmd{earfcn}); if (not is_in_state()) { fsmWarning("Failed to launch cell search"); return false; @@ -195,10 +195,10 @@ void phy_controller::cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t trigger(cell_srch_res{cs_ret, found_cell}); } -void phy_controller::searching_cell::enter(phy_controller* f) +void phy_controller::searching_cell::enter(phy_controller* f, const cell_search_cmd& ev) { otherfsmInfo(f, "Initiating Cell search"); - f->phy->cell_search(); + f->phy->cell_search(ev.earfcn); } void phy_controller::handle_cell_search_res(searching_cell& s, const cell_srch_res& result) diff --git a/srsue/src/stack/rrc/rrc.cc b/srsue/src/stack/rrc/rrc.cc index b829476b7..18a1eafec 100644 --- a/srsue/src/stack/rrc/rrc.cc +++ b/srsue/src/stack/rrc/rrc.cc @@ -1162,8 +1162,9 @@ void rrc::handle_rrc_con_reconfig(uint32_t lcid, const rrc_conn_recfg_s& reconfi } /* Actions upon reception of RRCConnectionRelease 5.3.8.3 */ -void rrc::rrc_connection_release(const std::string& cause) +void rrc::handle_rrc_connection_release(const asn1::rrc::rrc_conn_release_s& release) { + std::string cause = release.crit_exts.c1().rrc_conn_release_r8().release_cause.to_string(); // Save idleModeMobilityControlInfo, etc. srsran::console("Received RRC Connection Release (releaseCause: %s)\n", cause.c_str()); @@ -1173,6 +1174,36 @@ void rrc::rrc_connection_release(const std::string& cause) // delay actions by 60ms as per 5.3.8.3 task_sched.defer_callback(60, [this]() { start_go_idle(); }); + + uint32_t earfcn = 0; + if (release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info_present) { + switch (release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type()) { + case asn1::rrc::redirected_carrier_info_c::types_opts::options::eutra: + earfcn = release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.eutra(); + break; + default: + srsran::console("Ignoring RedirectedCarrierInfo with unsupported type (%s)\n", + release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string()); + break; + } + if (earfcn != 0) { + srsran::console("RedirectedCarrierInfo present (type %s, earfcn: %d) - Redirecting\n", + release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string(), + earfcn); + logger.info("RedirectedCarrierInfo present (type %s, earfcn: %d) - Redirecting", + release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string(), + earfcn); + + // delay actions by 60ms as per 5.3.8.3 + task_sched.defer_callback(60, [this, earfcn]() { start_rrc_redirect(earfcn); }); + } + } +} + +void rrc::start_rrc_redirect(uint32_t new_dl_earfcn) +{ + cell_search_earfcn = (int)new_dl_earfcn; + plmn_search(); } /// TS 36.331, 5.3.12 - UE actions upon leaving RRC_CONNECTED @@ -1827,7 +1858,7 @@ void rrc::parse_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu) handle_ue_capability_enquiry(c1->ue_cap_enquiry()); break; case dl_dcch_msg_type_c::c1_c_::types::rrc_conn_release: - rrc_connection_release(c1->rrc_conn_release().crit_exts.c1().rrc_conn_release_r8().release_cause.to_string()); + handle_rrc_connection_release(c1->rrc_conn_release()); break; case dl_dcch_msg_type_c::c1_c_::types::ue_info_request_r9: transaction_id = c1->ue_info_request_r9().rrc_transaction_id; diff --git a/srsue/src/stack/rrc/rrc_cell.cc b/srsue/src/stack/rrc/rrc_cell.cc index fd89cab7c..5ed18e0b6 100644 --- a/srsue/src/stack/rrc/rrc_cell.cc +++ b/srsue/src/stack/rrc/rrc_cell.cc @@ -80,6 +80,12 @@ void meas_cell_eutra::set_sib13(const asn1::rrc::sib_type13_r9_s& sib13_) has_valid_sib13 = true; } +void meas_cell_nr::set_sib1(const asn1::rrc_nr::sib1_s& sib1_) +{ + sib1 = sib1_; + has_valid_sib1 = true; +} + bool meas_cell::is_sib_scheduled(uint32_t sib_index) const { return sib_info_map.find(sib_index) != sib_info_map.end(); diff --git a/srsue/src/stack/rrc/rrc_procedures.cc b/srsue/src/stack/rrc/rrc_procedures.cc index 0749c8fe9..f496db851 100644 --- a/srsue/src/stack/rrc/rrc_procedures.cc +++ b/srsue/src/stack/rrc/rrc_procedures.cc @@ -50,7 +50,7 @@ proc_outcome_t rrc::cell_search_proc::init() { Info("Starting..."); state = state_t::phy_cell_search; - if (not rrc_ptr->phy_ctrl->start_cell_search(rrc_ptr->cell_searcher)) { + if (not rrc_ptr->phy_ctrl->start_cell_search(rrc_ptr->cell_searcher, rrc_ptr->cell_search_earfcn)) { Warning("Failed to initiate Cell Search."); return proc_outcome_t::error; } @@ -1265,11 +1265,14 @@ proc_outcome_t rrc::go_idle_proc::step() void rrc::go_idle_proc::then(const srsran::proc_state_t& result) { - if (rrc_ptr->nas->is_registered() and not rrc_ptr->cell_reselector.launch()) { - rrc_ptr->logger.error("Failed to initiate a Cell Reselection procedure..."); - return; + // only start cell reselection if no RRC redirect is present (redirect will trigger a cell search) + if (rrc_ptr->cell_search_earfcn < 0) { + if (rrc_ptr->nas->is_registered() and not rrc_ptr->cell_reselector.launch()) { + rrc_ptr->logger.error("Failed to initiate a Cell Reselection procedure..."); + return; + } + rrc_ptr->callback_list.add_proc(rrc_ptr->cell_reselector); } - rrc_ptr->callback_list.add_proc(rrc_ptr->cell_reselector); } /************************************** @@ -1371,7 +1374,7 @@ proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause) { // Save Current RNTI before MAC Reset uint16_t crnti = rrc_ptr->mac->get_crnti(); - size_t nof_scells_active = rrc_ptr->phy_ctrl->current_config_scells().count(); + size_t nof_scells_active = rrc_ptr->phy_ctrl->current_config_scells().count(); // 5.3.7.1 - Conditions for Reestablishment procedure if (not rrc_ptr->security_is_activated or rrc_ptr->state != RRC_STATE_CONNECTED or crnti == SRSRAN_INVALID_RNTI) { @@ -1393,9 +1396,9 @@ proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause) reest_cellid = rrc_ptr->meas_cells.find_cell(reest_source_freq, reest_source_pci)->get_cell_id(); Info("Starting... cause: \"%s\", UE context: {C-RNTI=0x%x, PCI=%d, CELL ID=%d}", - reest_cause == asn1::rrc::reest_cause_opts::recfg_fail - ? "Reconfiguration failure" - : cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure" : "Other failure", + reest_cause == asn1::rrc::reest_cause_opts::recfg_fail ? "Reconfiguration failure" + : cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure" + : "Other failure", reest_rnti, reest_source_pci, reest_cellid); diff --git a/srsue/src/stack/rrc/test/rrc_meas_test.cc b/srsue/src/stack/rrc/test/rrc_meas_test.cc index 10c444a85..48625c0e7 100644 --- a/srsue/src/stack/rrc/test/rrc_meas_test.cc +++ b/srsue/src/stack/rrc/test/rrc_meas_test.cc @@ -49,7 +49,7 @@ public: void set_config_mbsfn_sib2(srsran::mbsfn_sf_cfg_t* cfg_list, uint32_t nof_cfgs) override {} void set_config_mbsfn_sib13(const srsran::sib13_t& sib13) override {} void set_config_mbsfn_mcch(const srsran::mcch_msg_t& mcch) override {} - bool cell_search() override { return true; } + bool cell_search(int earfcn) override { return true; } bool cell_is_camping() override { return true; } void deactivate_scells() override {} bool cell_select(phy_cell_t cell) override diff --git a/srsue/src/stack/rrc/test/rrc_phy_ctrl_test.cc b/srsue/src/stack/rrc/test/rrc_phy_ctrl_test.cc index afbbca024..4ec6e6527 100644 --- a/srsue/src/stack/rrc/test/rrc_phy_ctrl_test.cc +++ b/srsue/src/stack/rrc/test/rrc_phy_ctrl_test.cc @@ -90,7 +90,7 @@ int test_phy_ctrl_fsm() TESTASSERT(phy_ctrl.is_in_sync()); // TEST: Correct initiation of Cell Search state - TESTASSERT(phy_ctrl.start_cell_search(csearch_tester)); + TESTASSERT(phy_ctrl.start_cell_search(csearch_tester, -1)); TESTASSERT(not phy_ctrl.is_in_sync()); // TEST: Cell Search only listens to a cell search result event diff --git a/srsue/src/stack/rrc_nr/rrc_nr.cc b/srsue/src/stack/rrc_nr/rrc_nr.cc index 208baac9f..fe8fdb11a 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr.cc @@ -34,7 +34,7 @@ using namespace asn1; using namespace srsran; namespace srsue { -const char* rrc_nr::rrc_nr_state_text[] = {"IDLE", "CONNECTED", "CONNECTED-INACTIVE"}; +const static char* rrc_nr_state_text[] = {"IDLE", "CONNECTED", "CONNECTED-INACTIVE"}; rrc_nr::rrc_nr(srsran::task_sched_handle task_sched_) : logger(srslog::fetch_basic_logger("RRC-NR")), @@ -71,9 +71,15 @@ int rrc_nr::init(phy_interface_rrc_nr* phy_, stack = stack_; args = args_; - plmn_is_selected = true; // short-cut SA test - + // allocate RRC timers t300 = task_sched.get_unique_timer(); + t301 = task_sched.get_unique_timer(); + t302 = task_sched.get_unique_timer(); + t304 = task_sched.get_unique_timer(); + t310 = task_sched.get_unique_timer(); + t311 = task_sched.get_unique_timer(); + + plmn_is_selected = true; // short-cut SA test running = true; sim_measurement_timer = task_sched.get_unique_timer(); @@ -104,7 +110,11 @@ void rrc_nr::init_core_less() pdcp->add_bearer(args.coreless.drb_lcid, pdcp_cnfg); return; } -void rrc_nr::get_metrics(rrc_nr_metrics_t& m) {} + +void rrc_nr::get_metrics(rrc_nr_metrics_t& m) +{ + m.state = state; +} const char* rrc_nr::get_rb_name(uint32_t lcid) { @@ -255,7 +265,7 @@ void rrc_nr::decode_dl_ccch(unique_byte_buffer_t pdu) case dl_ccch_msg_type_c::c1_c_::types::rrc_reject: { // 5.3.15 const auto& reject = c1->rrc_reject(); - srsran::console("Received RRC Reject"); + srsran::console("Received RRC Reject\n"); t300.stop(); @@ -275,7 +285,6 @@ void rrc_nr::decode_dl_ccch(unique_byte_buffer_t pdu) task_sched.defer_task([this, rrc_setup_copy]() { handle_rrc_setup(rrc_setup_copy); }); break; } - default: logger.error("The provided DL-CCCH message type is not recognized"); break; @@ -349,6 +358,11 @@ void rrc_nr::handle_sib1(const sib1_s& sib1) { logger.info("SIB1 received, CellID=%d", meas_cells.serving_cell().get_cell_id() & 0xfff); + meas_cells.serving_cell().set_sib1(sib1); + + // TODO: config basic config and remove early exit + return; + // clang-format off // unhandled fields: // - cellSelectionInfo @@ -611,7 +625,7 @@ void rrc_nr::send_setup_request(srsran::nr_establishment_cause_t cause) for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes random_id |= ((uint64_t)rand() & 0xFF) << i * 8; } - rrc_setup_req->ue_id.random_value().from_number(random_id); + rrc_setup_req->ue_id.random_value().from_number(random_id, rrc_setup_req->ue_id.random_value().length()); rrc_setup_req->establishment_cause = (establishment_cause_opts::options)cause; send_ul_ccch_msg(ul_ccch_msg); @@ -692,6 +706,16 @@ void rrc_nr::send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg) send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); } +void rrc_nr::send_rrc_reconfig_complete() +{ + logger.debug("Preparing RRC Connection Reconfig Complete"); + + asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; + auto& rrc_reconfig_complete = ul_dcch_msg.msg.set_c1().set_rrc_recfg_complete().crit_exts.set_rrc_recfg_complete(); + + send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg); +} + // EUTRA-RRC interface int rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) { @@ -1903,12 +1927,26 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg) return false; } - if (!(drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::eps_bearer_id)) { - logger.error("CN association type not supported %s ", drb_cfg.cn_assoc.type().to_string()); + if (drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::eps_bearer_id) { + // register EPS bearer over NR PDCP + uint32_t eps_bearer_id = drb_cfg.cn_assoc.eps_bearer_id(); + drb_eps_bearer_id[drb_cfg.drb_id] = eps_bearer_id; + stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::nr, lcid); + } else if (drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::sdap_cfg) { + const auto& sdap_cfg = drb_cfg.cn_assoc.sdap_cfg(); + if (sdap_cfg.sdap_hdr_dl == asn1::rrc_nr::sdap_cfg_s::sdap_hdr_dl_opts::present || + sdap_cfg.sdap_hdr_ul == asn1::rrc_nr::sdap_cfg_s::sdap_hdr_ul_opts::present) { + logger.error("SDAP currently not supported."); + return false; + } + // TODO: configure SDAP accordingly + uint32_t pdu_session_id = drb_cfg.cn_assoc.sdap_cfg().pdu_session; + // Register PDU session as "EPS bearer" in bearer manager + stack->add_eps_bearer(pdu_session_id, srsran::srsran_rat_t::nr, lcid); + } else { + logger.error("CN association type not supported %s", drb_cfg.cn_assoc.type().to_string()); return false; } - uint32_t eps_bearer_id = drb_cfg.cn_assoc.eps_bearer_id(); - drb_eps_bearer_id[drb_cfg.drb_id] = eps_bearer_id; if (drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl_present && drb_cfg.pdcp_cfg.drb.pdcp_sn_size_ul_present && (drb_cfg.pdcp_cfg.drb.pdcp_sn_size_ul.to_number() != drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl.to_number())) { @@ -1919,9 +1957,6 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg) srsran::pdcp_config_t pdcp_cfg = make_drb_pdcp_config_t(drb_cfg.drb_id, true, drb_cfg.pdcp_cfg); pdcp->add_bearer(lcid, pdcp_cfg); - // register EPS bearer over NR PDCP - stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::nr, lcid); - return true; } @@ -2094,12 +2129,11 @@ void rrc_nr::handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_ // Security helper used by Security Mode Command and Mobility handling routines void rrc_nr::generate_as_keys() { - uint8_t k_asme[32] = {}; - // FIXME: need to add - // nas->get_k_asme(k_asme, 32); - logger.debug(k_asme, 32, "UE K_asme"); - // logger.debug("Generating K_enb with UL NAS COUNT: %d", nas->get_k_enb_count()); - // usim->generate_as_keys(k_asme, nas->get_k_enb_count(), &sec_cfg); + as_key_t k_amf = {}; + nas->get_k_amf(k_amf); + logger.debug(k_amf.data(), 32, "UE K_amf"); + logger.debug("Generating K_gnb with UL NAS COUNT: %d", nas->get_ul_nas_count()); + usim->generate_nr_as_keys(k_amf, nas->get_ul_nas_count(), &sec_cfg); logger.info(sec_cfg.k_rrc_enc.data(), 32, "RRC encryption key - k_rrc_enc"); logger.info(sec_cfg.k_rrc_int.data(), 32, "RRC integrity key - k_rrc_int"); logger.info(sec_cfg.k_up_enc.data(), 32, "UP encryption key - k_up_enc"); diff --git a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc index ae74e0f51..c7745bce5 100644 --- a/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc +++ b/srsue/src/stack/rrc_nr/rrc_nr_procedures.cc @@ -100,23 +100,6 @@ proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator } } - if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size() > 0) { - srsran::unique_byte_buffer_t nas_sdu; - for (uint32_t i = 0; i < rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size(); ++i) { - nas_sdu = srsran::make_byte_buffer(); - if (nas_sdu != nullptr) { - memcpy(nas_sdu->msg, - rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].data(), - rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size()); - nas_sdu->N_bytes = rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size(); - rrc_handle.write_sdu(std::move(nas_sdu)); - } else { - rrc_handle.logger.error("Couldn't allocate SDU in %s.", __FUNCTION__); - return proc_outcome_t::error; - } - } - } - if (rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg_present) { Info("Applying Radio Bearer Cfg."); if (!rrc_handle.apply_radio_bearer_cfg(rrc_nr_reconf.crit_exts.rrc_recfg().radio_bearer_cfg)) { @@ -124,6 +107,25 @@ proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator } } + rrc_handle.send_rrc_reconfig_complete(); + + // Handle NAS messages + if (rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size() > 0) { + for (uint32_t i = 0; i < rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size(); ++i) { + srsran::unique_byte_buffer_t nas_pdu = srsran::make_byte_buffer(); + if (nas_pdu != nullptr) { + memcpy(nas_pdu->msg, + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].data(), + rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size()); + nas_pdu->N_bytes = rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size(); + rrc_handle.nas->write_pdu(std::move(nas_pdu)); + } else { + rrc_handle.logger.error("Couldn't allocate SDU in %s.", __FUNCTION__); + return proc_outcome_t::error; + } + } + } + return proc_outcome_t::success; } @@ -317,7 +319,7 @@ srsran::proc_outcome_t rrc_nr::connection_setup_proc::init(const asn1::rrc_nr::r { Info("Starting..."); - if (dedicated_info_nas_) { + if (dedicated_info_nas_ == nullptr) { logger.error("Connection Setup Failed, no dedicatedInfoNAS available"); return proc_outcome_t::error; } @@ -436,6 +438,17 @@ rrc_nr::cell_selection_proc::handle_cell_search_result(const rrc_interface_phy_n // until cell selection is done, update PHY config to take the last found PCI rrc_handle.phy_cfg.carrier.pci = result.pci; + // Until SI acquisition is implemented, provide hard-coded SIB for now + uint8_t msg[] = {0x74, 0x81, 0x01, 0x70, 0x10, 0x40, 0x04, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x33, 0x60, 0x38, + 0x05, 0x01, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x06, 0x6c, 0x6d, 0x92, 0x21, 0xf3, 0x70, 0x40, 0x20, + 0x00, 0x00, 0x80, 0x80, 0x00, 0x41, 0x06, 0x80, 0xa0, 0x90, 0x9c, 0x20, 0x08, 0x55, 0x19, 0x40, + 0x00, 0x00, 0x33, 0xa1, 0xc6, 0xd9, 0x22, 0x40, 0x00, 0x00, 0x20, 0xb8, 0x94, 0x63, 0xc0, 0x09, + 0x28, 0x44, 0x1b, 0x7e, 0xad, 0x8e, 0x1d, 0x00, 0x9e, 0x2d, 0xa3, 0x0a}; + srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer(); + memcpy(pdu->msg, msg, sizeof(msg)); + pdu->N_bytes = sizeof(msg); + rrc_handle.write_pdu_bcch_dlsch(std::move(pdu)); + if (not rrc_handle.phy->start_cell_select(cs_args)) { Error("Could not set start cell search."); return proc_outcome_t::error; diff --git a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc index 7e490bdd9..f8fbc9fd1 100644 --- a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc +++ b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc @@ -118,11 +118,14 @@ class dummy_eutra : public rrc_eutra_interface_rrc_nr class dummy_nas : public nas_5g_interface_rrc_nr { - int write_pdu(srsran::unique_byte_buffer_t pdu) { return SRSRAN_SUCCESS; }; + int write_pdu(srsran::unique_byte_buffer_t pdu) { return SRSRAN_SUCCESS; }; + int get_k_amf(srsran::as_key_t& k_amf) { return SRSRAN_SUCCESS; }; + uint32_t get_ul_nas_count() { return SRSRAN_SUCCESS; }; }; class dummy_sim : public usim_interface_rrc_nr { + void generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg){}; bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) { return true; } bool update_nr_context(srsran::as_security_config_t* sec_cfg) { return true; } }; diff --git a/srsue/src/stack/ue_stack_lte.cc b/srsue/src/stack/ue_stack_lte.cc index 9dc5f8269..e1ceb6071 100644 --- a/srsue/src/stack/ue_stack_lte.cc +++ b/srsue/src/stack/ue_stack_lte.cc @@ -237,8 +237,6 @@ int ue_stack_lte::init(const stack_args_t& args_) args.rrc_nr); rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc); - args.nas_5g.ia5g = "0,1,2,3"; - args.nas_5g.ea5g = "0,1,2,3"; nas_5g.init(usim.get(), &rrc_nr, gw, args.nas_5g); running = true; @@ -339,7 +337,13 @@ bool ue_stack_lte::disable_data() bool ue_stack_lte::start_service_request() { if (running) { - ue_task_queue.try_push([this]() { nas.start_service_request(srsran::establishment_cause_t::mo_data); }); + ue_task_queue.try_push([this]() { + if (args.attach_on_nr) { + nas_5g.start_service_request(); + } else { + nas.start_service_request(srsran::establishment_cause_t::mo_data); + } + }); } return true; } @@ -355,6 +359,7 @@ bool ue_stack_lte::get_metrics(stack_metrics_t* metrics) rlc.get_metrics(metrics.rlc, metrics.mac[0].nof_tti); nas.get_metrics(&metrics.nas); rrc.get_metrics(metrics.rrc); + rrc_nr.get_metrics(metrics.rrc_nr); pending_stack_metrics.push(metrics); }); // wait for result diff --git a/srsue/src/stack/upper/nas_5g.cc b/srsue/src/stack/upper/nas_5g.cc index 6a4a26348..d30f016ed 100644 --- a/srsue/src/stack/upper/nas_5g.cc +++ b/srsue/src/stack/upper/nas_5g.cc @@ -302,6 +302,11 @@ int nas_5g::send_registration_request() } } + if (has_sec_ctxt) { + set_k_gnb_count(ctxt_base.tx_count); + ctxt_base.tx_count++; + } + state.set_registered_initiated(); return SRSRAN_SUCCESS; @@ -462,6 +467,7 @@ int nas_5g::send_security_mode_complete(const srsran::nas_5g::security_mode_comm pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes); } + has_sec_ctxt = true; logger.info("Sending Security Mode Complete"); rrc_nr->write_sdu(std::move(pdu)); ctxt_base.tx_count++; @@ -807,6 +813,7 @@ 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"); + has_sec_ctxt = false; ctxt_base.rx_count++; state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); @@ -854,6 +861,7 @@ int nas_5g::handle_authentication_request(authentication_request_t& authenticati return SRSRAN_ERROR; } + initial_sec_command = true; uint8_t res_star[16]; logger.info(authentication_request.authentication_parameter_rand.rand.data(), @@ -897,6 +905,7 @@ int nas_5g::handle_authentication_request(authentication_request_t& authenticati int nas_5g::handle_authentication_reject(srsran::nas_5g::authentication_reject_t& authentication_reject) { logger.info("Handling Authentication Reject"); + has_sec_ctxt = false; ctxt_base.rx_count++; state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); return SRSRAN_SUCCESS; @@ -920,6 +929,7 @@ int nas_5g::handle_service_accept(srsran::nas_5g::service_accept_t& service_acce int nas_5g::handle_service_reject(srsran::nas_5g::service_reject_t& service_reject) { logger.info("Handling Service Reject"); + has_sec_ctxt = false; ctxt_base.rx_count++; return SRSRAN_SUCCESS; } @@ -941,10 +951,8 @@ int nas_5g::handle_security_mode_command(security_mode_command_t& security_m return SRSRAN_ERROR; } - initial_sec_command = false; // TODO - if (initial_sec_command) { - ctxt_base.rx_count = 0; + set_k_gnb_count(0); ctxt_base.tx_count = 0; initial_sec_command = false; } @@ -1115,6 +1123,27 @@ void nas_5g::get_metrics(nas_5g_metrics_t& metrics) metrics.state = state.get_state(); } +int nas_5g::get_k_amf(as_key_t& k_amf) +{ + if (not has_sec_ctxt) { + logger.error("K_amf requested before a valid NAS security context was established"); + return SRSRAN_ERROR; + } + + std::copy(std::begin(ctxt_5g.k_amf), std::end(ctxt_5g.k_amf), k_amf.begin()); + return SRSRAN_SUCCESS; +} + +uint32_t nas_5g::get_ul_nas_count() +{ + return ctxt_5g.k_gnb_count; +} + +void nas_5g::set_k_gnb_count(uint32_t count) +{ + ctxt_5g.k_gnb_count = count; +} + /******************************************************************************* * Helpers ******************************************************************************/ diff --git a/srsue/src/stack/upper/usim_base.cc b/srsue/src/stack/upper/usim_base.cc index 95522dc8b..0177568c1 100644 --- a/srsue/src/stack/upper/usim_base.cc +++ b/srsue/src/stack/upper/usim_base.cc @@ -231,6 +231,7 @@ void usim_base::generate_nas_keys(uint8_t* k_asme, /* * RRC Interface */ + void usim_base::generate_as_keys(uint8_t* k_asme_, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) { if (!initiated) { @@ -376,6 +377,42 @@ bool usim_base::generate_nas_keys_5g(uint8_t* k_amf, * NR RRC Interface */ +void usim_base::generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) +{ + if (!initiated) { + logger.error("USIM not initiated!"); + return; + } + + logger.info("Generating NR AS Keys. NAS UL COUNT %d", count_ul); + logger.debug(k_amf.data(), 32, "K_amf"); + + // Generate K_gnb + srsran::security_generate_k_gnb(k_amf, count_ul, k_gnb_ctx.k_gnb); + logger.info(k_gnb_ctx.k_gnb.data(), 32, "K_gnb"); + + // Save initial k_gnb + k_gnb_initial = k_gnb_ctx.k_gnb; + + security_generate_k_nr_rrc(k_gnb_ctx.k_gnb.data(), + sec_cfg->cipher_algo, + sec_cfg->integ_algo, + sec_cfg->k_rrc_enc.data(), + sec_cfg->k_rrc_int.data()); + + security_generate_k_nr_up(k_gnb_ctx.k_gnb.data(), + sec_cfg->cipher_algo, + sec_cfg->integ_algo, + sec_cfg->k_up_enc.data(), + sec_cfg->k_up_int.data()); + + logger.info(k_gnb_ctx.k_gnb.data(), 32, "Initial K_gnb"); + logger.info(sec_cfg->k_rrc_int.data(), sec_cfg->k_rrc_int.size(), "NR K_RRC_int"); + logger.info(sec_cfg->k_rrc_enc.data(), sec_cfg->k_rrc_enc.size(), "NR K_RRC_enc"); + logger.info(sec_cfg->k_up_int.data(), sec_cfg->k_up_int.size(), "NR K_UP_int"); + logger.info(sec_cfg->k_up_enc.data(), sec_cfg->k_up_enc.size(), "NR K_UP_enc"); +} + bool usim_base::generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) { if (!initiated) { diff --git a/srsue/src/test/metrics_test.cc b/srsue/src/test/metrics_test.cc index c5dfa8585..12d36609e 100644 --- a/srsue/src/test/metrics_test.cc +++ b/srsue/src/test/metrics_test.cc @@ -20,7 +20,7 @@ */ #include "srsran/common/metrics_hub.h" -#include "srsran/srsran.h" +#include "srsran/common/test_common.h" #include "srsue/hdr/metrics_csv.h" #include "srsue/hdr/metrics_stdout.h" #include "srsue/hdr/ue_metrics_interface.h" @@ -34,10 +34,12 @@ using namespace srsue; namespace srsue { -char* csv_file_name = NULL; +static char* csv_file_name = NULL; +static float period = 1.0; +static int duration_s = 5; // fake classes -class ue_dummy : public ue_metrics_interface +class eutra_ue_dummy : public ue_metrics_interface { public: bool get_metrics(ue_metrics_t* m) @@ -72,6 +74,51 @@ public: m->phy.nof_active_cc = 1; m->phy.ch[0].rsrp = -10.0f; m->phy.ch[0].pathloss = 32; + + m->stack.rrc.state = (rand() % 2 == 0) ? RRC_STATE_CONNECTED : RRC_STATE_IDLE; + + return true; + } +}; + +class nsa_ue_dummy : public ue_metrics_interface +{ +public: + bool get_metrics(ue_metrics_t* m) + { + *m = {}; + + // fill dummy values + m->rf.rf_o = 10; + m->phy.nof_active_cc = 1; + m->phy.ch[0].rsrp = -10.0f; + m->phy.ch[0].pathloss = 74; + m->stack.mac[0].rx_pkts = 100; + m->stack.mac[0].rx_errors = 0; + m->stack.mac[0].rx_brate = 200; + m->stack.mac[0].nof_tti = 1; + + m->phy.info[1].pci = UINT32_MAX; + m->stack.mac[1].rx_pkts = 100; + m->stack.mac[1].rx_errors = 100; + m->stack.mac[1].rx_brate = 150; + m->stack.mac[1].nof_tti = 1; + + // random neighbour cells + if (rand() % 2 == 0) { + phy_meas_t neighbor = {}; + neighbor.pci = 8; + neighbor.rsrp = -33; + m->stack.rrc.neighbour_cells.push_back(neighbor); + m->stack.rrc.neighbour_cells.push_back(neighbor); // need to add twice since we use CA + } + + m->phy.nof_active_cc = 1; + m->phy.ch[0].rsrp = -10.0f; + m->phy.ch[0].pathloss = 32; + + // NR + m->phy_nr.nof_active_cc = 1; m->stack.mac_nr[0].rx_pkts = 100; m->stack.mac_nr[0].rx_errors = 2; m->stack.mac_nr[0].rx_brate = 223; @@ -83,6 +130,31 @@ public: } }; +class sa_ue_dummy : public ue_metrics_interface +{ +public: + bool get_metrics(ue_metrics_t* m) + { + *m = {}; + + // fill dummy values + m->rf.rf_o = 10; + m->phy.nof_active_cc = 0; + + // NR + m->phy_nr.nof_active_cc = 1; + m->phy_nr.info[0].pci = 501; + m->stack.mac_nr[0].rx_pkts = 100; + m->stack.mac_nr[0].rx_errors = 2; + m->stack.mac_nr[0].rx_brate = 223; + m->stack.mac_nr[0].nof_tti = 1; + + m->stack.rrc_nr.state = (rand() % 2 == 0) ? RRC_NR_STATE_CONNECTED : RRC_NR_STATE_IDLE; + + return true; + } +}; + } // namespace srsue void usage(char* prog) @@ -110,11 +182,35 @@ void parse_args(int argc, char** argv) } } +int ue_test(std::string name, ue_metrics_interface* ue) +{ + // the default metrics type for stdout output + metrics_stdout metrics_screen; + metrics_screen.set_ue_handle(ue); + + // the CSV file writer + metrics_csv metrics_file(csv_file_name); + metrics_file.set_ue_handle(ue); + + // create metrics hub and register metrics for stdout + srsran::metrics_hub metricshub; + metricshub.init(ue, period); + metricshub.add_listener(&metrics_screen); + metricshub.add_listener(&metrics_file); + + // enable printing + metrics_screen.toggle_print(true); + + std::cout << "Running " << name << " UE test for " << duration_s << " seconds .." << std::endl; + usleep(duration_s * 1e6); + + metricshub.stop(); + + return SRSRAN_SUCCESS; +} + int main(int argc, char** argv) { - float period = 1.0; - ue_dummy ue; - if (argc < 3) { usage(argv[0]); exit(-1); @@ -122,26 +218,14 @@ int main(int argc, char** argv) parse_args(argc, argv); - // the default metrics type for stdout output - metrics_stdout metrics_screen; - metrics_screen.set_ue_handle(&ue); + eutra_ue_dummy eutra_ue; + TESTASSERT_SUCCESS(ue_test("EUTRA", &eutra_ue)); - // the CSV file writer - metrics_csv metrics_file(csv_file_name); - metrics_file.set_ue_handle(&ue); + nsa_ue_dummy nsa_ue; + TESTASSERT_SUCCESS(ue_test("NSA", &nsa_ue)); - // create metrics hub and register metrics for stdout - srsran::metrics_hub metricshub; - metricshub.init(&ue, period); - metricshub.add_listener(&metrics_screen); - metricshub.add_listener(&metrics_file); + sa_ue_dummy sa_ue; + TESTASSERT_SUCCESS(ue_test("SA", &sa_ue)); - // enable printing - metrics_screen.toggle_print(true); - - std::cout << "Running for 5 seconds .." << std::endl; - usleep(5e6); - - metricshub.stop(); - return 0; + return SRSRAN_SUCCESS; } diff --git a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h index f906dcbd3..f8b40d1d5 100644 --- a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h +++ b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h @@ -78,7 +78,7 @@ public: void set_mch_period_stop(uint32_t stop) override{}; // Cell search and selection procedures - bool cell_search() override; + bool cell_search(int earfcn) override; bool cell_select(phy_cell_t cell) override; bool cell_is_camping() override; diff --git a/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc b/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc index ca34e0f27..c0545e6ea 100644 --- a/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc +++ b/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc @@ -96,7 +96,7 @@ void lte_ttcn3_phy::meas_stop() {} // configured by the SS, including the ones that we should not even detect because // their power is too weak. The cell search should only report the cells that // are actually visible though. -bool lte_ttcn3_phy::cell_search() +bool lte_ttcn3_phy::cell_search(int earfcn) { std::lock_guard lock(phy_mutex); diff --git a/srsue/src/ue.cc b/srsue/src/ue.cc index aa2dc1a42..b365b4eb0 100644 --- a/srsue/src/ue.cc +++ b/srsue/src/ue.cc @@ -71,7 +71,7 @@ int ue::init(const all_args_t& args_) return SRSRAN_ERROR; } - std::unique_ptr gw_ptr(new gw(srslog::fetch_basic_logger("GW"))); + std::unique_ptr gw_ptr(new gw(srslog::fetch_basic_logger("GW", false))); if (!gw_ptr) { srsran::console("Error creating a GW instance.\n"); return SRSRAN_ERROR; @@ -285,6 +285,11 @@ int ue::parse_args(const all_args_t& args_) return SRSRAN_ERROR; } + // Update NAS-5G args + args.stack.nas_5g.ia5g = args.stack.nas.eia; + args.stack.nas_5g.ea5g = args.stack.nas.eea; + args.stack.nas_5g.pdu_session_cfgs.push_back({args.stack.nas.apn_name}); + return SRSRAN_SUCCESS; }