Merge branch 'next' into agpl_next

master
Codebot 3 years ago committed by Your Name
commit fb75a5ef0e

@ -70,6 +70,7 @@ struct pdcch_cfg_common_s;
struct pdcch_cfg_s; struct pdcch_cfg_s;
struct pdsch_cfg_common_s; struct pdsch_cfg_common_s;
struct pucch_cfg_common_s; struct pucch_cfg_common_s;
struct pucch_cfg_s;
struct pusch_cfg_common_s; struct pusch_cfg_common_s;
struct mib_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_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); 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); 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); 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, void fill_phy_carrier_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg,
srsran_carrier_nr_t* carrier_nr); srsran_carrier_nr_t* carrier_nr);

@ -106,6 +106,7 @@ struct k_enb_context_t {
}; };
struct k_gnb_context_t { struct k_gnb_context_t {
as_key_t k_gnb;
as_key_t sk_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_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_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, uint8_t security_generate_k_nb_star_common(uint8_t fc,

@ -84,8 +84,10 @@ public:
{ {
std::unique_lock<std::mutex> lock(mutex); std::unique_lock<std::mutex> lock(mutex);
// Pop first element // If the FIFO is not empty pop first element
fifo.pop_front(); if (not fifo.empty()) {
fifo.pop_front();
}
// Notify release // Notify release
cvar.notify_all(); cvar.notify_all();

@ -38,7 +38,7 @@ class pdu_session_cfg_t
{ {
public: public:
std::string apn_name; std::string apn_name;
apn_types apn_type; apn_types apn_type = ipv4;
std::string apn_user; std::string apn_user;
std::string apn_pass; std::string apn_pass;
}; };
@ -67,13 +67,15 @@ public:
class nas_5g_interface_rrc_nr class nas_5g_interface_rrc_nr
{ {
public: 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 class nas_5g_interface_procedures
{ {
public: public:
virtual int send_registration_request() = 0; virtual int send_registration_request() = 0;
virtual int send_pdu_session_establishment_request(uint32_t transaction_identity, virtual int send_pdu_session_establishment_request(uint32_t transaction_identity,
uint16_t pdu_session_id, uint16_t pdu_session_id,
const pdu_session_cfg_t& pdu_session) = 0; const pdu_session_cfg_t& pdu_session) = 0;

@ -105,7 +105,6 @@ struct phy_args_t {
srsran::channel::args_t dl_channel_args; srsran::channel::args_t dl_channel_args;
srsran::channel::args_t ul_channel_args; srsran::channel::args_t ul_channel_args;
}; };
/* RAT agnostic Interface MAC -> PHY */ /* RAT agnostic Interface MAC -> PHY */
@ -167,7 +166,7 @@ public:
virtual void meas_stop() = 0; virtual void meas_stop() = 0;
/* Cell search and selection procedures */ /* 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_select(phy_cell_t cell) = 0;
virtual bool cell_is_camping() = 0; virtual bool cell_is_camping() = 0;
}; };

@ -33,24 +33,24 @@ enum auth_result_t { AUTH_OK, AUTH_FAILED, AUTH_SYNCH_FAILURE };
class usim_interface_nas class usim_interface_nas
{ {
public: public:
virtual std::string get_imsi_str() = 0; virtual std::string get_imsi_str() = 0;
virtual std::string get_imei_str() = 0; virtual std::string get_imei_str() = 0;
virtual bool get_imsi_vec(uint8_t* imsi_, uint32_t n) = 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_imei_vec(uint8_t* imei_, uint32_t n) = 0;
virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0; virtual bool get_home_plmn_id(srsran::plmn_id_t* home_plmn_id) = 0;
// Get the home mcc as bytes array // 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 // 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 // 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, virtual auth_result_t generate_authentication_response(uint8_t* rand,
uint8_t* autn_enb, uint8_t* autn_enb,
uint16_t mcc, uint16_t mcc,
uint16_t mnc, uint16_t mnc,
uint8_t* res, uint8_t* res,
int* res_len, int* res_len,
uint8_t* k_asme) = 0; uint8_t* k_asme) = 0;
virtual auth_result_t generate_authentication_response_5g(uint8_t* rand, virtual auth_result_t generate_authentication_response_5g(uint8_t* rand,
uint8_t* autn_enb, uint8_t* autn_enb,
@ -86,8 +86,10 @@ public:
class usim_interface_rrc_nr class usim_interface_rrc_nr
{ {
public: public:
virtual bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) = 0; virtual void
virtual bool update_nr_context(srsran::as_security_config_t* sec_cfg) = 0; 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 } // namespace srsue

@ -85,7 +85,7 @@ class phy_dummy_interface : public phy_interface_rrc_lte
void meas_stop() override {} void meas_stop() override {}
/* Cell search and selection procedures */ /* 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_select(phy_cell_t cell) override { return true; }
bool cell_is_camping() override { return false; } bool cell_is_camping() override { return false; }
}; };

@ -98,7 +98,7 @@ public:
} else { } else {
encryption_direction = direction; 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) void enable_security_timed(srsran_direction_t direction, uint32_t sn)

@ -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) { if (srsran_csi_hl_report_cfg.type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) {
srsran_csi_hl_report_cfg.periodic.period = const auto& csi_periodic = csi_report_cfg.report_cfg_type.periodic();
csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type().to_number(); srsran_csi_hl_report_cfg.periodic.period = csi_periodic.report_slot_cfg.type().to_number();
switch (csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type()) { switch (csi_periodic.report_slot_cfg.type()) {
case csi_report_periodicity_and_offset_c::types_opts::slots4: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots5: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots8: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots10: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots16: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots20: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots40: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots80: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots160: 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; break;
case csi_report_periodicity_and_offset_c::types_opts::slots320: 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; break;
default: default:
asn1::log_warning("Invalid option for report_slot_cfg %s", asn1::log_warning("Invalid option for report_slot_cfg %s", csi_periodic.report_slot_cfg.type().to_string());
csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type().to_string());
return false; 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()); asn1::log_warning("Invalid option for cqi_table %s", csi_report_cfg.cqi_table.to_string());
return false; return false;
} }
*in_srsran_csi_hl_report_cfg = srsran_csi_hl_report_cfg; *in_srsran_csi_hl_report_cfg = srsran_csi_hl_report_cfg;
return true; 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_t srsran_pucch_nr_resource = {};
srsran_pucch_nr_resource.starting_prb = pucch_res.start_prb; 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()) { switch (pucch_res.format.type()) {
case pucch_res_s::format_c_::types_opts::format0: case pucch_res_s::format_c_::types_opts::format0:
srsran_pucch_nr_resource.format = SRSRAN_PUCCH_NR_FORMAT_0; 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])) { if (not make_phy_csi_report(csi_rep, &csi_hl->reports[i])) {
return false; 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<pucch_format_cfg_s>::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<pucch_format_cfg_s>::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) 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++) { for (uint32_t i = 0; i < pdsch_cfg.pdsch_time_domain_alloc_list.size(); i++) {

@ -24,7 +24,6 @@
#include "srsran/common/s3g.h" #include "srsran/common/s3g.h"
#include "srsran/common/ssl.h" #include "srsran/common/ssl.h"
#include "srsran/config.h" #include "srsran/config.h"
#include <arpa/inet.h> #include <arpa/inet.h>
#ifdef HAVE_MBEDTLS #ifdef HAVE_MBEDTLS
@ -204,6 +203,32 @@ uint8_t security_generate_k_amf(const uint8_t* k_seaf,
return SRSRAN_SUCCESS; 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<uint8_t> 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<uint8_t> 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) 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) { if (k_asme == NULL || k_enb == NULL) {

@ -637,10 +637,23 @@ int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
return SRSRAN_ERROR; 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 // 7.3.3.2 Modulation
int8_t llr[PBCH_NR_E]; int8_t llr[PBCH_NR_E];
srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, symbols, llr, PBCH_NR_M); 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 // TS 38.211 7.3.3 Physical broadcast channel
// 7.3.3.1 Scrambling // 7.3.3.1 Scrambling
pbch_nr_scramble_rx(cfg, cfg->ssb_idx, llr, llr); pbch_nr_scramble_rx(cfg, cfg->ssb_idx, llr, llr);

@ -641,6 +641,10 @@ if(RF_FOUND)
target_link_libraries(prach_test_usrp srsran_rf srsran_phy pthread) target_link_libraries(prach_test_usrp srsran_rf srsran_phy pthread)
endif(RF_FOUND) 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 # NR
######################################################################## ########################################################################

@ -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.
* - <tt>-N num</tt>: sets the number of experiments to \c num.
* - <tt>-n num</tt>: sets the total number of UL PRBs to \c num.
* - <tt>-f num</tt>: sets the preamble format to \c num (for now, format 0 only).
* - <tt>-s val</tt>: sets the nominal SNR to \c val dB.
* - <tt>-v </tt>: 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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#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");
}

@ -885,7 +885,7 @@ int rf_skiq_send_timed(void* h,
} }
int rf_skiq_send_timed_multi(void* h_, int rf_skiq_send_timed_multi(void* h_,
void** data_, void* data_[SRSRAN_MAX_PORTS],
int nsamples, int nsamples,
time_t secs, time_t secs,
double frac_secs, double frac_secs,

@ -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++) { for (int j = 0; j < decim_factor; j++, n++) {
avg += ptr[n]; avg += ptr[n];
} }
dst[i] = avg; dst[i] = avg; // divide by decim_factor later via scale
} }
rf_zmq_info(handler->id, 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); pthread_mutex_lock(&handler->rx_gain_mutex);
float scale = srsran_convert_dB_to_amplitude(handler->rx_gain); float scale = srsran_convert_dB_to_amplitude(handler->rx_gain);
pthread_mutex_unlock(&handler->rx_gain_mutex); 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++) { for (uint32_t c = 0; c < handler->nof_channels; c++) {
if (buffers[c]) { if (buffers[c]) {
srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples); srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples);

@ -26,18 +26,22 @@
#include <complex.h> #include <complex.h>
#include <pthread.h> #include <pthread.h>
#include <srsran/phy/common/phy_common.h> #include <srsran/phy/common/phy_common.h>
#include <srsran/phy/utils/vector.h>
#include <stdlib.h> #include <stdlib.h>
#include <zmq.h> #include <zmq.h>
#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 NUM_SF (500)
#define SF_LEN (1920) #define SF_LEN (1920)
#define RF_BUFFER_SIZE (SF_LEN * NUM_SF) #define RF_BUFFER_SIZE (SF_LEN * NUM_SF)
#define TX_OFFSET_MS (4) #define TX_OFFSET_MS (4)
static cf_t ue_rx_buffer[RF_BUFFER_SIZE]; static cf_t ue_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE];
static cf_t enb_tx_buffer[RF_BUFFER_SIZE]; static cf_t enb_tx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE];
static cf_t enb_rx_buffer[RF_BUFFER_SIZE]; static cf_t enb_rx_buffer[NOF_RX_ANT][RF_BUFFER_SIZE];
static srsran_rf_t ue_radio, enb_radio; static srsran_rf_t ue_radio, enb_radio;
pthread_t rx_thread; pthread_t rx_thread;
@ -62,13 +66,15 @@ void* ue_rx_thread_function(void* args)
uint32_t num_rxed_samps = 0; uint32_t num_rxed_samps = 0;
for (uint32_t i = 0; i < num_slots; ++i) { for (uint32_t i = 0; i < num_slots; ++i) {
void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; 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); 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("received %d samples.\n", num_rxed_samps);
printf("closing ue norf device\n"); printf("closing ue zmq device\n");
srsran_rf_close(&ue_radio); srsran_rf_close(&ue_radio);
return NULL; return NULL;
@ -87,17 +93,24 @@ void enb_tx_function(const char* tx_args, bool timed_tx)
} }
// generate random tx data // generate random tx data
for (int i = 0; i < RF_BUFFER_SIZE; i++) { for (int c = 0; c < NOF_RX_ANT; c++) {
enb_tx_buffer[i] = ((float)rand() / (float)RAND_MAX) + _Complex_I * ((float)rand() / (float)RAND_MAX); 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 // send data subframe per subframe
uint32_t num_txed_samples = 0; uint32_t num_txed_samples = 0;
// initial transmission without ts // initial transmission without ts
void* data_ptr[SRSRAN_MAX_PORTS] = {NULL}; void* data_ptr[SRSRAN_MAX_PORTS] = {NULL};
data_ptr[0] = &enb_tx_buffer[num_txed_samples]; cf_t tx_buffer[NOF_RX_ANT][SF_LEN];
int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false); 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; num_txed_samples += SF_LEN;
// from here on, all transmissions are timed relative to the last rx time // 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) { for (uint32_t i = 0; i < NUM_SF - ((timed_tx) ? TX_OFFSET_MS : 1); ++i) {
// first recv samples // 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); srsran_rf_recv_with_time_multi(&enb_radio, data_ptr, SF_LEN, true, &rx_time.full_secs, &rx_time.frac_secs);
// prepare data buffer // 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) { 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 // 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 // wait for rx thread
pthread_join(rx_thread, NULL); pthread_join(rx_thread, NULL);
// subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx) // channel-wise comparison
for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) { for (int c = 0; c < NOF_RX_ANT; c++) {
uint32_t sf_offet = 0; // subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx)
if (timed_tx && i >= 1) { for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) {
// for timed transmission, the enb inserts 3 zero subframes after the first untimed tx uint32_t sf_offet = 0;
sf_offet = (TX_OFFSET_MS - 1) * SF_LEN; 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); #if PRINT_SAMPLES
srsran_vec_fprint_c(stdout, &enb_tx_buffer[i * SF_LEN], 3); // print first 10 samples for each SF
printf("ue_rx_buffer sf%d:\n", i); printf("enb_tx_buffer sf%d:\n", i);
srsran_vec_fprint_c(stdout, &ue_rx_buffer[sf_offet + i * SF_LEN], 3); 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 #endif
if (memcmp(&ue_rx_buffer[sf_offet + i * SF_LEN], &enb_tx_buffer[i * SF_LEN], SF_LEN) != 0) { #if COMPARE_BITS
fprintf(stderr, "data mismatch in subframe %d\n", i); int d = memcmp(&ue_rx_buffer[sf_offet + i * SF_LEN], &enb_tx_buffer[i * SF_LEN], SF_LEN);
goto exit; 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() int main()
{ {
// two Rx ports // // two Rx ports
if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) { // if (param_test("rx_port=ipc://dl0,rx_port1=ipc://dl1", 2)) {
fprintf(stderr, "Param test failed!\n"); // fprintf(stderr, "Param test failed!\n");
return SRSRAN_ERROR; // return SRSRAN_ERROR;
} // }
// multiple rx ports, no channel index provided // // 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)) { // 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"); // fprintf(stderr, "Param test failed!\n");
return SRSRAN_ERROR; // return SRSRAN_ERROR;
} // }
// One Rx, one Tx and all generic options // // One Rx, one Tx and all generic options
if (param_test("rx_port0=tcp://" // 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", // "localhost:2000,rx_format=sc16,tx_format=sc16,tx_type=pub,rx_type=sub,base_srate=1.92e6,id=test",
1)) { // 1)) {
fprintf(stderr, "Param test failed!\n"); // fprintf(stderr, "Param test failed!\n");
return SRSRAN_ERROR; // return SRSRAN_ERROR;
} // }
// 1 port, 2 antennas, MIMO freq config // // 1 port, 2 antennas, MIMO freq config
if (param_test( // if (param_test(
"tx_port0=tcp://*:2001,tx_port1=tcp://*:2003,rx_port0=tcp://localhost:2000,rx_port1=tcp://" // "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", // "localhost:2002,id=ue,base_srate=23.04e6,tx_freq0=2510e6,tx_freq1=2510e6,rx_freq0=2630e6,,rx_freq1=2630e6",
2)) { // 2)) {
fprintf(stderr, "Param test failed!\n"); // fprintf(stderr, "Param test failed!\n");
return SRSRAN_ERROR; // return SRSRAN_ERROR;
} // }
#if NOF_RX_ANT == 1
// single tx, single rx with continuous transmissions (no timed tx) using IPC transport // 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) != 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) { SRSRAN_SUCCESS) {
fprintf(stderr, "Single tx, single rx test failed!\n"); fprintf(stderr, "Single tx, single rx test failed!\n");
return -1; return -1;
} }
#endif
// two trx radios with continous tx (no timed tx) using TCP transport for both directions // up to 4 trx radios with continous tx (no decimation, no timed tx)
if (run_test("tx_port=tcp://*:5554,rx_port=tcp://" if (run_test("tx_port=tcp://*:5554,tx_port=tcp://*:5556,tx_port=tcp://*:5558,tx_port=tcp://*:5560,rx_port=tcp://"
"localhost:5555,id=ue,base_srate=1.92e6,log_trx_timeout=true,trx_timeout_ms=1000", "localhost:5555,rx_port=tcp://localhost:5557,rx_port=tcp://localhost:5559,rx_port=tcp://"
"rx_port=tcp://localhost:5554,tx_port=tcp://*:5555,id=enb,base_srate=1.92e6", "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) { false) != SRSRAN_SUCCESS) {
fprintf(stderr, "Two TRx radio test failed!\n"); fprintf(stderr, "Multi TRx radio test 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)
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, "Multi TRx radio test with timed tx failed!\n");
return -1; return -1;
} }
// two trx radios with continous tx (no timed tx) using TCP for UL (UE tx) and IPC for eNB DL (eNB tx) // 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,rx_port=ipc://dl,id=ue,base_srate=1.92e6", // with decimation 23.04e6 <-> 1.92e6
"rx_port=tcp://localhost:5554,tx_port=ipc://dl,id=enb,base_srate=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) { true) != SRSRAN_SUCCESS) {
fprintf(stderr, "Two TRx radio test with timed tx failed!\n"); fprintf(stderr, "Multi TRx radio test with timed tx and decimation failed!\n");
return -1; return -1;
} }

@ -46,7 +46,7 @@
/* /*
* Default NR-PBCH DMRS normalised correlation (RSRP/EPRE) threshold * 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) static int ssb_init_corr(srsran_ssb_t* q)
{ {
@ -865,8 +865,9 @@ static int ssb_pss_search(srsran_ssb_t* q,
// Find maximum // Find maximum
uint32_t peak_idx = srsran_vec_max_abs_ci(q->tmp_time, q->corr_window); 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) // Average power, take total power of the frequency domain signal after filtering, skip correlation window if
float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz); // 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)) { if (!isnormal(avg_pwr_corr)) {
continue; continue;
} }
@ -974,6 +975,11 @@ int srsran_ssb_csi_search(srsran_ssb_t* q,
t_offset = 0; 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 // Demodulate
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, in, t_offset, coarse_cfo_hz, ssb_grid) < SRSRAN_SUCCESS) { 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; return SRSRAN_SUCCESS;
} }
static int ssb_select_pbch(srsran_ssb_t* q, static int ssb_select_pbch(srsran_ssb_t* q,
uint32_t N_id, uint32_t N_id,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
uint32_t* found_n_hf, uint32_t* found_n_hf,
uint32_t* found_ssb_idx_4lsb) uint32_t* found_ssb_idx_4lsb,
srsran_dmrs_pbch_meas_t* pbch_meas)
{ {
// Prepare PBCH DMRS configuration // Prepare PBCH DMRS configuration
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {}; srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
@ -1089,6 +1096,7 @@ static int ssb_select_pbch(srsran_ssb_t* q,
// Save findings // Save findings
*found_n_hf = best_n_hf; *found_n_hf = best_n_hf;
*found_ssb_idx_4lsb = best_ssb_idx; *found_ssb_idx_4lsb = best_ssb_idx;
*pbch_meas = best_meas;
return SRSRAN_SUCCESS; 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; 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 // Search for PSS in time domain
uint32_t N_id_2 = 0; uint32_t N_id_2 = 0;
uint32_t t_offset = 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; 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 // Demodulate
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, in, t_offset, coarse_cfo_hz, ssb_grid) < SRSRAN_SUCCESS) { 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); uint32_t N_id = SRSRAN_NID_NR(N_id_1, N_id_2);
// Select the most suitable SSB candidate // Select the most suitable SSB candidate
uint32_t n_hf = 0; uint32_t n_hf = 0;
uint32_t ssb_idx = 0; uint32_t ssb_idx = 0;
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) { 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"); ERROR("Error selecting PBCH");
return SRSRAN_ERROR; 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 = {}; srsran_pbch_msg_nr_t pbch_msg = {};
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, &pbch_msg) < SRSRAN_SUCCESS) { if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, &pbch_msg) < SRSRAN_SUCCESS) {
ERROR("Error decoding PBCH"); ERROR("Error decoding PBCH");
return SRSRAN_ERROR; 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 = {}; srsran_csi_trs_measurements_t measurements = {};
if (ssb_measure(q, ssb_grid, N_id, &measurements) < SRSRAN_SUCCESS) { if (ssb_measure(q, ssb_grid, N_id, &measurements) < SRSRAN_SUCCESS) {
ERROR("Error measuring"); ERROR("Error measuring");
@ -1336,6 +1364,9 @@ int srsran_ssb_find(srsran_ssb_t* q,
return SRSRAN_ERROR; 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 // 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); 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; 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 // Demodulate
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {}; cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, q->sf_buffer, t_offset, 0.0f, ssb_grid) < SRSRAN_SUCCESS) { 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 // Select the most suitable SSB candidate
uint32_t n_hf = 0; uint32_t n_hf = 0;
uint32_t ssb_idx = 0; // SSB candidate index uint32_t ssb_idx = 0; // SSB candidate index
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) { 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"); ERROR("Error selecting PBCH");
return SRSRAN_ERROR; 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 // Calculate the SSB offset in the subframe
uint32_t ssb_offset = srsran_ssb_candidate_sf_offset(q, ssb_idx); uint32_t ssb_offset = srsran_ssb_candidate_sf_offset(q, ssb_idx);

@ -29,6 +29,9 @@
#include <srsran/phy/utils/random.h> #include <srsran/phy/utils/random.h>
#include <stdlib.h> #include <stdlib.h>
#define SSB_DECODE_TEST_PCI_STRIDE 53
#define SSB_DECODE_TEST_SSB_STRIDE 3
// NR parameters // NR parameters
static uint32_t carrier_nof_prb = 52; static uint32_t carrier_nof_prb = 52;
static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz; 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; 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 // For benchmarking purposes
uint64_t t_encode_usec = 0; uint64_t t_encode_usec = 0;
@ -147,8 +150,8 @@ static int test_case_1(srsran_ssb_t* ssb)
// For each PCI... // For each PCI...
uint64_t count = 0; uint64_t count = 0;
for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += 23) { 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++, count++) { for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx += SSB_DECODE_TEST_SSB_STRIDE, count++) {
struct timeval t[3] = {}; struct timeval t[3] = {};
// Build PBCH message // Build PBCH message
@ -158,7 +161,7 @@ static int test_case_1(srsran_ssb_t* ssb)
// Print encoded PBCH message // Print encoded PBCH message
char str[512] = {}; char str[512] = {};
srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str)); 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 // Initialise baseband
srsran_vec_cf_zero(buffer, hf_len); srsran_vec_cf_zero(buffer, hf_len);
@ -184,7 +187,7 @@ static int test_case_1(srsran_ssb_t* ssb)
// Print decoded PBCH message // Print decoded PBCH message
srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str)); 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 // Assert PBCH message CRC
TESTASSERT(pbch_msg_rx.crc); TESTASSERT(pbch_msg_rx.crc);
@ -200,7 +203,7 @@ static int test_case_1(srsran_ssb_t* ssb)
// Print decoded PBCH message // Print decoded PBCH message
srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str)); 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 // Assert PBCH message CRC
TESTASSERT(res.pbch_msg.crc); TESTASSERT(res.pbch_msg.crc);
@ -209,11 +212,11 @@ static int test_case_1(srsran_ssb_t* ssb)
} }
if (!count) { if (!count) {
ERROR("Error in test case 1: undefined division"); ERROR("Error in test case true: undefined division");
return SRSRAN_ERROR; 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_encode_usec / (double)(count),
(double)t_decode_usec / (double)(count), (double)t_decode_usec / (double)(count),
(double)t_search_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; 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 main(int argc, char** argv)
{ {
int ret = SRSRAN_ERROR; int ret = SRSRAN_ERROR;
@ -257,7 +331,12 @@ int main(int argc, char** argv)
goto clean_exit; 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"); ERROR("test case failed");
goto clean_exit; goto clean_exit;
} }

@ -167,6 +167,26 @@ int test_generate_k_nas()
return SRSRAN_SUCCESS; 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() int test_generate_k_enb()
{ {
auto& logger = srslog::fetch_basic_logger("LOG", false); 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_up_keys() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_enb_star() == SRSRAN_SUCCESS); TESTASSERT(test_generate_k_enb_star() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_nh() == 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_res_star() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_ausf() == SRSRAN_SUCCESS); TESTASSERT(test_generate_k_ausf() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_seaf() == SRSRAN_SUCCESS); TESTASSERT(test_generate_k_seaf() == SRSRAN_SUCCESS);

@ -116,7 +116,7 @@ public:
// Stack interface // Stack interface
bool 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_); void start_pcap(srsran::s1ap_pcap* pcap_);
private: private:
@ -270,6 +270,7 @@ private:
void void
set_state(s1ap_proc_id_t state, const erab_id_list& erabs_updated, const erab_item_list& erabs_failed_to_update); 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 = {}; ue_ctxt_t ctxt = {};
uint16_t stream_id = 1; uint16_t stream_id = 1;

@ -785,6 +785,14 @@ bool s1ap::handle_initialctxtsetuprequest(const init_context_setup_request_s& ms
return false; 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 // Setup UE ctxt in RRC
if (not rrc->setup_ue_ctxt(u->ctxt.rnti, msg)) { if (not rrc->setup_ue_ctxt(u->ctxt.rnti, msg)) {
return false; 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_ptr = users.find_ue_enbid(enb_id);
ue* user_mme_ptr = nullptr; ue* user_mme_ptr = nullptr;
cause_c cause; 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 != nullptr) {
if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) { if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) {
// No ID inconsistency found // No ID inconsistency found

@ -92,8 +92,8 @@ struct sched_nr_cell_cfg_t {
uint32_t pci; uint32_t pci;
uint32_t dl_cell_nof_prb; uint32_t dl_cell_nof_prb;
uint32_t ul_cell_nof_prb; uint32_t ul_cell_nof_prb;
asn1::copy_ptr<asn1::rrc_nr::dl_cfg_common_sib_s> dl_cfg_common; asn1::rrc_nr::dl_cfg_common_sib_s dl_cfg_common;
asn1::copy_ptr<asn1::rrc_nr::ul_cfg_common_sib_s> ul_cfg_common; asn1::rrc_nr::ul_cfg_common_sib_s ul_cfg_common;
srsran::optional<asn1::rrc_nr::tdd_ul_dl_cfg_common_s> tdd_ul_dl_cfg_common; srsran::optional<asn1::rrc_nr::tdd_ul_dl_cfg_common_s> tdd_ul_dl_cfg_common;
ssb_positions_in_burst_t ssb_positions_in_burst; ssb_positions_in_burst_t ssb_positions_in_burst;
uint32_t ssb_periodicity_ms = 0; uint32_t ssb_periodicity_ms = 0;
@ -107,8 +107,6 @@ struct sched_nr_cell_cfg_t {
double dl_center_frequency_hz; double dl_center_frequency_hz;
double ul_center_frequency_hz; double ul_center_frequency_hz;
double ssb_center_freq_hz; double ssb_center_freq_hz;
uint32_t offset_to_carrier;
srsran_subcarrier_spacing_t scs;
}; };
class sched_nr_interface class sched_nr_interface

@ -46,6 +46,7 @@
namespace srsenb { namespace srsenb {
class enb_bearer_manager; class enb_bearer_manager;
class du_config_manager;
enum class rrc_nr_state_t { RRC_IDLE, RRC_INACTIVE, RRC_CONNECTED }; 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; asn1::rrc_nr::sp_cell_cfg_s base_sp_cell_cfg;
// vars // vars
std::unique_ptr<du_config_manager> du_cfg;
struct cell_ctxt_t { struct cell_ctxt_t {
asn1::rrc_nr::mib_s mib;
asn1::rrc_nr::sib1_s sib1; asn1::rrc_nr::sib1_s sib1;
asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_l_ sibs; asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_l_ sibs;
srsran::unique_byte_buffer_t mib_buffer = nullptr; srsran::unique_byte_buffer_t mib_buffer = nullptr;

@ -41,17 +41,19 @@ struct rrc_nr_cfg_sr_t {
// Cell/Sector configuration for NR cells // Cell/Sector configuration for NR cells
struct rrc_cell_cfg_nr_t { 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.) 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 tac; // Tracking area code
uint32_t dl_arfcn; // DL freq already included in phy_cell uint32_t dl_arfcn; // DL freq already included in phy_cell
uint32_t ul_arfcn; // UL freq also 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 dl_absolute_freq_point_a; // derived from DL ARFCN
uint32_t ul_absolute_freq_point_a; // derived from UL 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 band; uint32_t coreset0_idx; // Table 13-{1,...15} row index
uint32_t coreset0_idx; // Table 13-{1,...15} row index srsran_duplex_mode_t duplex_mode;
srsran_duplex_mode_t duplex_mode; double ssb_freq_hz;
srsran_ssb_cfg_t ssb_cfg; 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_cfg_nr_t> rrc_cell_list_nr_t; typedef std::vector<rrc_cell_cfg_nr_t> rrc_cell_list_nr_t;

@ -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<std::unique_ptr<du_cell_config> > cells;
};
} // namespace srsenb
#endif // SRSRAN_RRC_NR_DU_MANAGER_H

@ -117,6 +117,9 @@ private:
/// Update MAC based on ASN1 message /// Update MAC based on ASN1 message
int update_mac(const asn1::rrc_nr::cell_group_cfg_s& cell_group_config, bool is_config_complete); 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_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig);
int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config); int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config);

@ -133,7 +133,7 @@ public:
for (ue_event_t& ev : current_slot_ue_events) { for (ue_event_t& ev : current_slot_ue_events) {
auto ue_it = ues.find(ev.rnti); auto ue_it = ues.find(ev.rnti);
if (ue_it == ues.end()) { 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; ev.rnti = SRSRAN_INVALID_RNTI;
} else if (ue_it->second->has_ca()) { } else if (ue_it->second->has_ca()) {
// events specific to existing UEs with CA // events specific to existing UEs with CA
@ -161,7 +161,7 @@ public:
} }
auto ue_it = ues.find(ev.rnti); auto ue_it = ues.find(ev.rnti);
if (ue_it == ues.end()) { 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; ev.rnti = SRSRAN_INVALID_RNTI;
} else if (not ue_it->second->has_ca() and ue_it->second->carriers[cc] != nullptr) { } else if (not ue_it->second->has_ca() and ue_it->second->carriers[cc] != nullptr) {
ev.callback(*ue_it->second, evlogger); ev.callback(*ue_it->second, evlogger);
@ -174,7 +174,7 @@ public:
if (ue_it != ues.end() and ue_it->second->carriers[cc] != nullptr) { if (ue_it != ues.end() and ue_it->second->carriers[cc] != nullptr) {
ev.callback(*ue_it->second->carriers[cc], evlogger); ev.callback(*ue_it->second->carriers[cc], evlogger);
} else { } 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);
} }
} }
} }

@ -149,15 +149,11 @@ cell_config_manager::cell_config_manager(uint32_t cc_,
carrier.dl_center_frequency_hz = cell.dl_center_frequency_hz; carrier.dl_center_frequency_hz = cell.dl_center_frequency_hz;
carrier.ul_center_frequency_hz = cell.ul_center_frequency_hz; carrier.ul_center_frequency_hz = cell.ul_center_frequency_hz;
carrier.ssb_center_freq_hz = cell.ssb_center_freq_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.nof_prb = cell.dl_cell_nof_prb;
carrier.start = 0; // TODO: Check carrier.start = 0; // TODO: Check
carrier.max_mimo_layers = cell.nof_layers; 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.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.scs = (srsran_subcarrier_spacing_t)cell.dl_cfg_common->init_dl_bwp.generic_params.subcarrier_spacing.value;
}
// TDD-UL-DL-ConfigCommon // TDD-UL-DL-ConfigCommon
duplex.mode = SRSRAN_DUPLEX_MODE_FDD; duplex.mode = SRSRAN_DUPLEX_MODE_FDD;

@ -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) void make_mib_cfg(const sched_nr_cell_cfg_t& cfg, srsran_mib_nr_t* mib)
{ {
*mib = {}; *mib = {};
mib->scs_common = cfg.scs; 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->ssb_offset = 6; // TODO
mib->dmrs_typeA_pos = (srsran_dmrs_sch_typeA_pos_t)cfg.dmrs_type_a_position.value; 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->coreset0_idx = cfg.pdcch_cfg_sib1.ctrl_res_set_zero;
mib->ss0_idx = cfg.pdcch_cfg_sib1.search_space_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->scs = (srsran_subcarrier_spacing_t)cfg.ssb_scs.value;
ssb->pattern = SRSRAN_SSB_PATTERN_A; 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
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) {
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;
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);
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.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.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.ssb_center_freq_hz = cfg.ssb_center_freq_hz;
ue_phy_cfg.carrier.offset_to_carrier = cfg.offset_to_carrier; 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 = cfg.scs; ue_phy_cfg.carrier.scs =
ue_phy_cfg.carrier.nof_prb = cfg.dl_cell_nof_prb; (srsran_subcarrier_spacing_t)cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value;
ue_phy_cfg.carrier.max_mimo_layers = cfg.nof_layers; 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); make_ssb_cfg(cfg, &ue_phy_cfg.ssb);
// remove UE-specific SearchSpaces (they will be added later via RRC) // remove UE-specific SearchSpaces (they will be added later via RRC)

@ -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) 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); return slot_ue(*carriers[cc], pdcch_slot);
} }

@ -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.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.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.ssb_center_freq_hz = phy_cfg.carrier.ssb_center_freq_hz;
cell_cfg.offset_to_carrier = phy_cfg.carrier.offset_to_carrier; cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list.resize(1);
cell_cfg.scs = phy_cfg.carrier.scs; cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing =
cell_cfg.dl_cell_nof_prb = phy_cfg.carrier.nof_prb; (asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.carrier.scs;
cell_cfg.nof_layers = phy_cfg.carrier.max_mimo_layers; cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier =
cell_cfg.ssb_periodicity_ms = phy_cfg.ssb.periodicity_ms; 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) { 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]); cell_cfg.ssb_positions_in_burst.in_one_group.set(i, phy_cfg.ssb.position_in_burst[i]);
} }

@ -22,7 +22,7 @@ set(SOURCES rrc_nr_config_utils.cc)
add_library(srsgnb_rrc_config_utils STATIC ${SOURCES}) add_library(srsgnb_rrc_config_utils STATIC ${SOURCES})
target_link_libraries(srsgnb_rrc_config_utils srsran_phy) 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}) add_library(srsgnb_rrc STATIC ${SOURCES})
target_link_libraries(srsgnb_rrc srsgnb_rrc_config_utils) target_link_libraries(srsgnb_rrc srsgnb_rrc_config_utils)

@ -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 /// 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) 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; 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) { 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 { } else {
csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 7; 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; auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list;
// item 0 // item 0
nzp_csi_res[0].nzp_csi_rs_res_id = 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.set_row2();
nzp_csi_res[0].res_map.freq_domain_alloc.other().from_number(0b100000); 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::p2; 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.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.density.set_one();
nzp_csi_res[0].res_map.freq_band.start_rb = 0; nzp_csi_res[0].res_map.freq_band.start_rb = 0;
nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52; 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_res[0];
nzp_csi_res[1].nzp_csi_rs_res_id = 1; 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.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.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.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(); 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[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; 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(); 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();
imres.push_back(0); 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].bwp_id = 0;
csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].res_type.value = csi_res_cfg_s::res_type_opts::periodic; csi_meas_cfg.csi_res_cfg_to_add_mod_list[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; uint32_t j = 0, j2 = 0;
for (uint32_t i = 0; i < out.res_to_add_mod_list.size(); ++i) { 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].pucch_res_id = i;
out.res_to_add_mod_list[i].intra_slot_freq_hop_present = true; out.res_to_add_mod_list[i].intra_slot_freq_hop_present = false;
out.res_to_add_mod_list[i].second_hop_prb_present = true;
if (i < 8 or i == 16) { if (i < 8 or i == 16) {
out.res_to_add_mod_list[i].start_prb = 51; 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].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.set_format1().init_cyclic_shift = (4 * (j % 3));
out.res_to_add_mod_list[i].format.format1().nrof_symbols = 14; 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++; j++;
} else if (i < 15) { } else if (i < 15) {
out.res_to_add_mod_list[i].start_prb = 1; 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].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.set_format2().nrof_prbs = 1;
out.res_to_add_mod_list[i].format.format2().nrof_symbols = 2; 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++; j2++;
} else { } else {
out.res_to_add_mod_list[i].start_prb = 50; 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].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.set_format2().nrof_prbs = 1;
out.res_to_add_mod_list[i].format.format2().nrof_symbols = 2; 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.set_setup();
out.format2.setup().max_code_rate_present = true; out.format2.setup().max_code_rate_present = true;
out.format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25; 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 // SR resources
out.sched_request_res_to_add_mod_list.resize(1); 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.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_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.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_present = true;
serv_common.pci = cell_cfg.phy_cell.carrier.pci; serv_common.pci = cell_cfg.phy_cell.carrier.pci;
serv_common.ssb_periodicity_serving_cell_present = true; 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)) { serv_common.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_s::ssb_periodicity_serving_cell_opts::ms10;
get_logger(cfg).error("Config Error: Invalid SSB periodicity = %d\n", cell_cfg.ssb_cfg.periodicity_ms);
return SRSRAN_ERROR;
}
// Fill SSB config // Fill SSB config
serv_common.ssb_positions_in_burst_present = true; 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) { if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) {
// TDD UL-DL config // TDD UL-DL config
serv_common.tdd_ul_dl_cfg_common_present = true; serv_common.tdd_ul_dl_cfg_common_present = true;
auto& tdd_config = serv_common.tdd_ul_dl_cfg_common; fill_tdd_ul_dl_config_common(cfg.cell_list[cc], serv_common.tdd_ul_dl_cfg_common);
tdd_config.ref_subcarrier_spacing = subcarrier_spacing_e::khz15;
tdd_config.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10;
tdd_config.pattern1.nrof_dl_slots = 6;
tdd_config.pattern1.nrof_dl_symbols = 0;
tdd_config.pattern1.nrof_ul_slots = 4;
tdd_config.pattern1.nrof_ul_symbols = 0;
} }
serv_common.ul_cfg_common_present = true; 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); fill_sp_cell_cfg_from_enb_cfg(cfg, cc, out.sp_cell_cfg);
out.sp_cell_cfg.recfg_with_sync_present = false; 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; 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.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_present = true;
cfg.freq_info_dl.freq_band_list[0].freq_band_ind_nr = cell_cfg.band; 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); 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; 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.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.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 = cfg.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing =
(subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs; (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; 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); 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.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 = cfg.freq_info_ul.scs_specific_carrier_list[0].subcarrier_spacing =
(subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs; (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; 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; 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); 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_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; 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) 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.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag = 0;
sib1.serving_cell_cfg_common_present = true; 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_present = true;
sib1.ue_timers_and_consts.t300.value = ue_timers_and_consts_s::t300_opts::ms1000; sib1.ue_timers_and_consts.t300.value = ue_timers_and_consts_s::t300_opts::ms1000;

@ -23,6 +23,7 @@
#include "srsenb/hdr/common/common_enb.h" #include "srsenb/hdr/common/common_enb.h"
#include "srsgnb/hdr/stack/rrc/cell_asn1_config.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_config_utils.h"
#include "srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h"
#include "srsgnb/hdr/stack/rrc/rrc_nr_ue.h" #include "srsgnb/hdr/stack/rrc/rrc_nr_ue.h"
#include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h" #include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h"
#include "srsran/asn1/rrc_nr_utils.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); cell.ssb_absolute_freq_point);
} }
du_cfg = std::make_unique<du_config_manager>(cfg);
for (uint32_t i = 0; i < cfg.cell_list.size(); ++i) {
du_cfg->add_cell();
}
// Generate cell config structs // Generate cell config structs
cell_ctxt.reset(new cell_ctxt_t{}); cell_ctxt.reset(new cell_ctxt_t{});
if (cfg.is_standalone) { 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); 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"); 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) { if (not cfg.is_standalone) {
// Fill rrc_nr_cfg with UE-specific search spaces and coresets // Fill rrc_nr_cfg with UE-specific search spaces and coresets
asn1_pdcch = asn1_pdcch =
&base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup(); &base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup();
} else { } 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"); 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.pdcch = cfg.cell_list[0].phy_cell.pdcch;
common_cfg.prach = cfg.cell_list[0].phy_cell.prach; common_cfg.prach = cfg.cell_list[0].phy_cell.prach;
common_cfg.duplex_mode = cfg.cell_list[0].duplex_mode; 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) { if (phy->set_common_cfg(common_cfg) < SRSRAN_SUCCESS) {
logger.error("Couldn't set common PHY config"); logger.error("Couldn't set common PHY config");
return; return;
@ -302,54 +314,48 @@ void rrc_nr::config_phy()
void rrc_nr::config_mac() void rrc_nr::config_mac()
{ {
uint32_t cc = 0;
// Fill MAC scheduler configuration for SIBs // Fill MAC scheduler configuration for SIBs
// TODO: use parsed cell NR cfg configuration // TODO: use parsed cell NR cfg configuration
srsran::phy_cfg_nr_default_t::reference_cfg_t ref_args{}; 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_TDD_CUSTOM_6_4
: srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD; : srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD;
std::vector<sched_nr_cell_cfg_t> sched_cells_cfg(1, get_default_cell_cfg(srsran::phy_cfg_nr_default_t{ref_args})); std::vector<sched_nr_cell_cfg_t> 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 // Derive cell config from rrc_nr_cfg_t
cell.bwps[0].pdcch = cfg.cell_list[0].phy_cell.pdcch; cell.bwps[0].pdcch = cfg.cell_list[cc].phy_cell.pdcch;
cell.pci = cfg.cell_list[0].phy_cell.carrier.pci; cell.pci = cfg.cell_list[cc].phy_cell.carrier.pci;
cell.nof_layers = cfg.cell_list[0].phy_cell.carrier.max_mimo_layers; cell.nof_layers = cfg.cell_list[cc].phy_cell.carrier.max_mimo_layers;
cell.dl_cell_nof_prb = cfg.cell_list[0].phy_cell.carrier.nof_prb; cell.dl_cell_nof_prb = cfg.cell_list[cc].phy_cell.carrier.nof_prb;
cell.ul_cell_nof_prb = cfg.cell_list[0].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[0].phy_cell.carrier.dl_center_frequency_hz; cell.dl_center_frequency_hz = cfg.cell_list[cc].phy_cell.carrier.dl_center_frequency_hz;
cell.ul_center_frequency_hz = cfg.cell_list[0].phy_cell.carrier.ul_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[0].phy_cell.carrier.ssb_center_freq_hz; cell.ssb_center_freq_hz = cfg.cell_list[cc].phy_cell.carrier.ssb_center_freq_hz;
cell.offset_to_carrier = cfg.cell_list[0].phy_cell.carrier.offset_to_carrier; cell.dmrs_type_a_position = du_cfg->cell(cc).mib.dmrs_type_a_position;
cell.scs = cfg.cell_list[0].phy_cell.carrier.scs; 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) { if (not cfg.is_standalone) {
const serving_cell_cfg_common_s& serv_cell = base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common; const serving_cell_cfg_common_s& serv_cell = base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common;
// Derive cell config from ASN1 // 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); 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."); 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_positions_in_burst.in_one_group.set(0, true);
cell.ssb_periodicity_ms = serv_cell.ssb_periodicity_serving_cell.to_number(); cell.ssb_periodicity_ms = serv_cell.ssb_periodicity_serving_cell.to_number();
cell.ssb_scs = serv_cell.ssb_subcarrier_spacing; cell.ssb_scs = serv_cell.ssb_subcarrier_spacing;
cell.ss_pbch_block_power = serv_cell.ss_pbch_block_pwr;
} else { } 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)); 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.ssb_positions_in_burst = du_cfg->cell(cc).serv_cell_cfg_common().ssb_positions_in_burst;
cell.ul_cfg_common.reset(new ul_cfg_common_sib_s{serv_cell.ul_cfg_common}); cell.ssb_periodicity_ms = du_cfg->cell(cc).serv_cell_cfg_common().ssb_periodicity_serving_cell.to_number();
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_scs.value = (subcarrier_spacing_e::options)cfg.cell_list[0].phy_cell.carrier.scs; 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 // Set SIB1 and SI messages
cell.sibs.resize(cell_ctxt->sib_buffer.size()); cell.sibs.resize(cell_ctxt->sib_buffer.size());
@ -373,26 +379,6 @@ void rrc_nr::config_mac()
int32_t rrc_nr::generate_sibs() 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) { if (not cfg.is_standalone) {
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }

@ -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; (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, int ret = srsran_coreset_zero(cell.phy_cell.carrier.pci,
ssb_pointA_freq_offset_Hz, ssb_pointA_freq_offset_Hz,
cell.ssb_cfg.scs, cell.ssb_scs,
cell.phy_cell.carrier.scs, cell.phy_cell.carrier.scs,
cell.coreset0_idx, cell.coreset0_idx,
&cell.phy_cell.pdcch.coreset[0]); &cell.phy_cell.pdcch.coreset[0]);
@ -131,7 +131,7 @@ int derive_ssb_params(bool is_sa,
srsran_subcarrier_spacing_t pdcch_scs, srsran_subcarrier_spacing_t pdcch_scs,
uint32_t coreset0_idx, uint32_t coreset0_idx,
uint32_t nof_prb, uint32_t nof_prb,
srsran_ssb_cfg_t& ssb) rrc_cell_cfg_nr_t& cell)
{ {
// Verify essential parameters are specified and valid // Verify essential parameters are specified and valid
ERROR_IF_NOT(dl_arfcn > 0, "Invalid DL ARFCN=%d", dl_arfcn); 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); 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); 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 // derive SSB pattern and scs
ssb.pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz); cell.ssb_pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz);
if (ssb.pattern == SRSRAN_SSB_PATTERN_A) { if (cell.ssb_pattern == SRSRAN_SSB_PATTERN_A) {
// 15kHz SSB SCS // 15kHz SSB SCS
ssb.scs = srsran_subcarrier_spacing_15kHz; cell.ssb_scs = srsran_subcarrier_spacing_15kHz;
} else { } else {
// try to optain SSB pattern for same band with 30kHz SCS // try to optain SSB pattern for same band with 30kHz SCS
ssb.pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_30kHz); cell.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) { if (cell.ssb_pattern == SRSRAN_SSB_PATTERN_B || cell.ssb_pattern == SRSRAN_SSB_PATTERN_C) {
// SSB SCS is 30 kHz // SSB SCS is 30 kHz
ssb.scs = srsran_subcarrier_spacing_30kHz; cell.ssb_scs = srsran_subcarrier_spacing_30kHz;
} else { } else {
srsran_terminate("Can't derive SSB pattern from band %d", band); 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; int coreset0_rb_offset = 0;
if (is_sa) { if (is_sa) {
// Get offset in RBs between CORESET#0 and SSB // 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"); ERROR_IF_NOT(coreset0_rb_offset >= 0, "Failed to compute RB offset between CORESET#0 and SSB");
} else { } else {
// TODO: Verify if specified SSB frequency is valid // TODO: Verify if specified SSB frequency is valid
} }
uint32_t ssb_abs_freq_point = 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, ERROR_IF_NOT(ssb_abs_freq_point > 0,
"Can't derive SSB freq point for dl_arfcn=%d and band %d", "Can't derive SSB freq point for dl_arfcn=%d and band %d",
band_helper.freq_to_nr_arfcn(dl_freq_hz), band_helper.freq_to_nr_arfcn(dl_freq_hz),
band); band);
// Convert to frequency for PHY // Convert to frequency for PHY
ssb.ssb_freq_hz = band_helper.nr_arfcn_to_freq(ssb_abs_freq_point); cell.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;
return SRSRAN_SUCCESS; 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.phy_cell.carrier.scs,
cell.coreset0_idx, cell.coreset0_idx,
cell.phy_cell.carrier.nof_prb, cell.phy_cell.carrier.nof_prb,
cell.ssb_cfg); cell);
cell.phy_cell.carrier.ssb_center_freq_hz = cell.ssb_cfg.ssb_freq_hz; 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_cfg.ssb_freq_hz); cell.ssb_absolute_freq_point = band_helper.freq_to_nr_arfcn(cell.ssb_freq_hz);
// Derive remaining config params // Derive remaining config params
if (is_sa) { 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) int check_nr_cell_cfg_valid(const rrc_cell_cfg_nr_t& cell, bool is_sa)
{ {
// verify SSB params are consistent // 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)); HANDLE_ERROR(check_nr_phy_cell_cfg_valid(cell.phy_cell));
if (is_sa) { if (is_sa) {

@ -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<du_cell_config> obj = std::make_unique<du_cell_config>();
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

@ -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_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_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_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) void nr_security_context::regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_arfcn)

@ -1117,20 +1117,8 @@ void rrc_nr::ue::handle_rrc_setup_complete(const asn1::rrc_nr::rrc_setup_complet
/// TS 38.331, SecurityModeCommand /// TS 38.331, SecurityModeCommand
void rrc_nr::ue::send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu) 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. // apply selected security config and enable integrity on SRB1 before generating security mode command
// When we start using PDCP-NR we can avoid this step. update_as_security(srb_to_lcid(srsran::nr_srb::srb1), true, false);
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));
if (nas_pdu != nullptr) { if (nas_pdu != nullptr) {
nas_pdu_queue.push_back(std::move(nas_pdu)); 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 /// TS 38.331, SecurityModeComplete
void rrc_nr::ue::handle_security_mode_complete(const asn1::rrc_nr::security_mode_complete_s& msg) 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->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(); send_rrc_reconfiguration();
// Note: Skip UE capabilities // 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); parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg);
// enable security config
if (sec_ctx.is_as_sec_cfg_valid()) { if (sec_ctx.is_as_sec_cfg_valid()) {
srsran::nr_as_security_config_t tmp_cnfg = sec_ctx.get_as_sec_cfg(); update_as_security(rlc_bearer->lc_ch_id);
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));
} }
} }
@ -1399,6 +1422,10 @@ int rrc_nr::ue::update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radi
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg); 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; 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 // 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 { } else {
auto& pdcch = cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(); 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) { 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.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.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}); 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); parent->mac->ue_cfg(rnti, uecfg);
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;

@ -179,6 +179,7 @@ void test_rrc_sa_connection()
SRSRAN_SUCCESS); SRSRAN_SUCCESS);
TESTASSERT_SUCCESS(rrc_obj.add_user(0x4601, 0)); 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_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); test_rrc_nr_info_transfer(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601);

@ -62,7 +62,7 @@ public:
void meas_stop() final {} void meas_stop() final {}
/* Cell search and selection procedures */ /* 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_select(phy_cell_t cell) final { return false; }
bool cell_is_camping() final { return false; } bool cell_is_camping() final { return false; }

@ -58,8 +58,6 @@ public:
bool start(const cfg_t& cfg); bool start(const cfg_t& cfg);
ret_t run_slot(const cf_t* buffer, uint32_t slot_sz); ret_t run_slot(const cf_t* buffer, uint32_t slot_sz);
void reset();
private: private:
srslog::basic_logger& logger; srslog::basic_logger& logger;
srsran_ssb_t ssb = {}; srsran_ssb_t ssb = {};

@ -77,7 +77,7 @@ public:
/********** RRC INTERFACE ********************/ /********** RRC INTERFACE ********************/
bool cell_search() final; bool cell_search(int earfcn) final;
bool cell_select(phy_cell_t cell) 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() // Sets the new PHY configuration for the given CC. The configuration is applied in the background. The notify()

@ -77,7 +77,7 @@ public:
// RRC interface for controling the SYNC state // RRC interface for controling the SYNC state
bool cell_search_init(); 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_init(phy_cell_t cell);
bool cell_select_start(phy_cell_t cell); bool cell_select_start(phy_cell_t cell);
bool cell_is_camping(); bool cell_is_camping();

@ -49,7 +49,9 @@ public:
struct cell_sel_cmd { struct cell_sel_cmd {
phy_cell_t phy_cell; phy_cell_t phy_cell;
}; };
struct cell_search_cmd {}; struct cell_search_cmd {
int earfcn;
};
struct in_sync_ev { struct in_sync_ev {
static const bool log_verbose = false; static const bool log_verbose = false;
}; };
@ -61,7 +63,7 @@ public:
// PHY procedures interfaces // PHY procedures interfaces
bool start_cell_select(const phy_cell_t& phy_cell, srsran::event_observer<bool> observer = {}); bool start_cell_select(const phy_cell_t& phy_cell, srsran::event_observer<bool> observer = {});
bool start_cell_search(srsran::event_observer<cell_srch_res> observer); bool start_cell_search(srsran::event_observer<cell_srch_res> observer, int earfcn);
void cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t found_cell); void cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t found_cell);
void cell_selection_completed(bool outcome); void cell_selection_completed(bool outcome);
void in_sync(); void in_sync();
@ -126,7 +128,7 @@ public:
// clang-format on // clang-format on
}; };
struct searching_cell { struct searching_cell {
void enter(phy_controller* f); void enter(phy_controller* f, const cell_search_cmd& ev);
}; };
private: private:

@ -220,6 +220,9 @@ private:
meas_cell_list<meas_cell_nr> meas_cells_nr; meas_cell_list<meas_cell_nr> 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; bool initiated = false;
asn1::rrc::reest_cause_e m_reest_cause = asn1::rrc::reest_cause_e::nulltype; asn1::rrc::reest_cause_e m_reest_cause = asn1::rrc::reest_cause_e::nulltype;
uint16_t m_reest_rnti = 0; uint16_t m_reest_rnti = 0;
@ -337,7 +340,8 @@ private:
bool con_reconfig_ho(const asn1::rrc::rrc_conn_recfg_s& reconfig); bool con_reconfig_ho(const asn1::rrc::rrc_conn_recfg_s& reconfig);
void ho_failed(); void ho_failed();
void start_go_idle(); 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_push_cmd();
void radio_link_failure_process(); void radio_link_failure_process();
void leave_connected(); void leave_connected();

@ -24,6 +24,7 @@
#include "../rrc/rrc_cell.h" #include "../rrc/rrc_cell.h"
#include "rrc_nr_config.h" #include "rrc_nr_config.h"
#include "rrc_nr_metrics.h"
#include "srsran/adt/circular_map.h" #include "srsran/adt/circular_map.h"
#include "srsran/asn1/rrc_nr.h" #include "srsran/asn1/rrc_nr.h"
#include "srsran/asn1/rrc_nr_utils.h" #include "srsran/asn1/rrc_nr_utils.h"
@ -44,8 +45,6 @@ class usim_interface_rrc_nr;
class pdcp_interface_rrc; class pdcp_interface_rrc;
class rlc_interface_rrc; class rlc_interface_rrc;
struct rrc_nr_metrics_t {};
class rrc_nr final : public rrc_interface_phy_nr, class rrc_nr final : public rrc_interface_phy_nr,
public rrc_interface_pdcp, public rrc_interface_pdcp,
public rrc_interface_rlc, public rrc_interface_rlc,
@ -146,6 +145,7 @@ private:
// senders // senders
void send_setup_request(srsran::nr_establishment_cause_t cause); void send_setup_request(srsran::nr_establishment_cause_t cause);
void send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg); 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_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_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); 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; uint32_t sim_measurement_carrier_freq_r15;
srsran::timer_handler::unique_timer sim_measurement_timer; 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; rrc_nr_state_t state = RRC_NR_STATE_IDLE;
uint8_t transaction_id = 0; uint8_t transaction_id = 0;

@ -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

@ -89,9 +89,9 @@ public:
void timer_expired(uint32_t timeout_id) override; void timer_expired(uint32_t timeout_id) override;
private: private:
rrc_interface_nas* rrc = nullptr; rrc_interface_nas* rrc = nullptr;
usim_interface_nas* usim = nullptr; usim_interface_nas* usim = nullptr;
gw_interface_nas* gw = nullptr; gw_interface_nas* gw = nullptr;
bool running = false; bool running = false;
@ -117,10 +117,10 @@ private:
typedef std::pair<uint8_t, eps_bearer_t> eps_bearer_map_pair_t; typedef std::pair<uint8_t, eps_bearer_t> eps_bearer_map_pair_t;
eps_bearer_map_t eps_bearer; eps_bearer_map_t eps_bearer;
bool have_guti = false; bool have_guti = false;
bool have_ctxt = false; bool have_ctxt = false;
bool auth_request = false; bool auth_request = false;
uint8_t current_sec_hdr = LIBLTE_MME_SECURITY_HDR_TYPE_PLAIN_NAS; 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 const uint32_t max_attach_attempts = 5; // Sec. 5.5.1.2.6
uint32_t attach_attempt_counter = 0; uint32_t attach_attempt_counter = 0;
@ -153,8 +153,8 @@ private:
const uint8_t ue_svn_oct2 = 0x3; const uint8_t ue_svn_oct2 = 0x3;
// Security // Security
bool eia_caps[8] = {}; bool eia_caps[8] = {};
bool eea_caps[8] = {}; bool eea_caps[8] = {};
// Airplane mode simulation // Airplane mode simulation
typedef enum { DISABLED = 0, ENABLED } airplane_mode_state_t; typedef enum { DISABLED = 0, ENABLED } airplane_mode_state_t;

@ -66,7 +66,9 @@ public:
void run_tti(); void run_tti();
// Stack+RRC interface // 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); int write_pdu(srsran::unique_byte_buffer_t pdu);
@ -88,8 +90,8 @@ private:
usim_interface_nas* usim = nullptr; usim_interface_nas* usim = nullptr;
gw_interface_nas* gw = nullptr; gw_interface_nas* gw = nullptr;
bool running = false; bool running = false;
bool has_sec_ctxt = false;
bool initial_sec_command = false; bool initial_sec_command = false;
srsran::nas_5g::mobile_identity_5gs_t::guti_5g_s guti_5g; srsran::nas_5g::mobile_identity_5gs_t::guti_5g_s guti_5g;
@ -102,6 +104,8 @@ private:
bool ia5g_caps[8] = {}; bool ia5g_caps[8] = {};
bool ea5g_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) // 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 // which identifies the software version number of the mobile equipment
const uint8_t ue_svn_oct1 = 0x5; const uint8_t ue_svn_oct1 = 0x5;

@ -69,6 +69,7 @@ protected:
struct nas_5g_sec_ctxt { struct nas_5g_sec_ctxt {
uint8_t ksi; uint8_t ksi;
uint8_t k_amf[32]; uint8_t k_amf[32];
uint32_t k_gnb_count;
}; };
nas_sec_base_ctxt ctxt_base = {}; nas_sec_base_ctxt ctxt_base = {};
@ -79,7 +80,7 @@ protected:
// Security // Security
void 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); bool integrity_check(srsran::byte_buffer_t* pdu);
void cipher_encrypt(srsran::byte_buffer_t* pdu); void cipher_encrypt(srsran::byte_buffer_t* pdu);
void cipher_decrypt(srsran::byte_buffer_t* pdu); void cipher_decrypt(srsran::byte_buffer_t* pdu);

@ -106,6 +106,7 @@ public:
void restore_keys_from_failed_ho(srsran::as_security_config_t* as_ctx) final; void restore_keys_from_failed_ho(srsran::as_security_config_t* as_ctx) final;
// NR RRC interface // 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 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; 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 k_enb_initial[KEY_LEN] = {};
uint8_t auts[AKA_AUTS_LEN] = {}; uint8_t auts[AKA_AUTS_LEN] = {};
srsran::as_key_t k_gnb_initial = {};
// Current K_eNB context (K_eNB, NH and NCC) // Current K_eNB context (K_eNB, NH and NCC)
srsran::k_enb_context_t k_enb_ctx = {}; srsran::k_enb_context_t k_enb_ctx = {};
srsran::k_gnb_context_t k_gnb_ctx = {}; srsran::k_gnb_context_t k_gnb_ctx = {};

@ -31,6 +31,7 @@
#include "srsran/system/sys_metrics.h" #include "srsran/system/sys_metrics.h"
#include "stack/mac/mac_metrics.h" #include "stack/mac/mac_metrics.h"
#include "stack/rrc/rrc_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/gw_metrics.h"
#include "stack/upper/nas_metrics.h" #include "stack/upper/nas_metrics.h"
@ -43,7 +44,7 @@ typedef struct {
srsran::rlc_metrics_t rlc; srsran::rlc_metrics_t rlc;
nas_metrics_t nas; nas_metrics_t nas;
rrc_metrics_t rrc; rrc_metrics_t rrc;
rrc_metrics_t rrc_nr; rrc_nr_metrics_t rrc_nr;
} stack_metrics_t; } stack_metrics_t;
typedef struct { typedef struct {

@ -190,7 +190,7 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per
return; 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"); fmt::print("--- disconnected ---\n");
return; 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; 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 // print table header every 10 reports
if (++n_reports > 10) { 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 // also print table header if neighbours are added/removed in between
if (display_neighbours != table_has_neighbours) { 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++) { if (has_lte) {
set_metrics_helper(metrics.phy, metrics.stack.mac, metrics.stack.rrc, display_neighbours, r, false, !is_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, !has_nr);
}
} }
for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) { if (has_nr) {
// Assumption LTE is followed by the NR carriers. for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) {
set_metrics_helper(metrics.phy_nr, metrics.stack.mac_nr, metrics.stack.rrc, display_neighbours, r, true, !is_nr); // 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) { if (metrics.rf.rf_error) {

@ -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. // 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 // When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH
// processing. // processing. If a valid EARFCN (>0) is given, this is used for cell search.
bool phy::cell_search() bool phy::cell_search(int earfcn)
{ {
sfsync.scell_sync_stop(); sfsync.scell_sync_stop();
if (sfsync.cell_search_init()) { if (sfsync.cell_search_init()) {
cmd_worker_cell.add_cmd([this]() { cmd_worker_cell.add_cmd([this, earfcn]() {
// Wait SYNC transitions to IDLE // Wait SYNC transitions to IDLE
sfsync.wait_idle(); sfsync.wait_idle();
@ -364,7 +364,7 @@ bool phy::cell_search()
reset(); reset();
phy_cell_t found_cell = {}; 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); stack->cell_search_complete(ret, found_cell);
}); });
} else { } else {

@ -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, /* A call to cell_search() finds the strongest cell at a given EARFCN or in the set of supported EARFCNs. When the first
* returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not found in the * cell is found, returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not
* current frequency it moves to the next one and the next call to cell_search() will look in the next EARFCN in the * found in the current frequency it moves to the next one and the next call to cell_search() will look in the next
* set. If no cells are found in any frequency it returns 0. If error returns -1. * 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 * 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 // 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(); phy_state.go_idle();
// Stop all intra-frequency measurement before changing frequency // Stop all intra-frequency measurement before changing frequency
@ -217,10 +216,16 @@ bool sync::cell_search_init()
return true; 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<std::mutex> ul(rrc_mutex); std::unique_lock<std::mutex> 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 = {}; rrc_interface_phy_lte::cell_search_ret_t ret = {};
ret.found = rrc_interface_phy_lte::cell_search_ret_t::ERROR; 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; 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"); Info("SYNC: Setting Cell Search sampling rate");
} }
try { if (earfcn < 0) {
if (current_earfcn != (int)worker_com->args->dl_earfcn_list.at(cellsearch_earfcn_index)) { try {
current_earfcn = (int)worker_com->args->dl_earfcn_list[cellsearch_earfcn_index]; if (current_earfcn != (int)worker_com->args->dl_earfcn_list.at(cellsearch_earfcn_index)) {
Info("Cell Search: changing frequency to EARFCN=%d", current_earfcn); current_earfcn = (int)worker_com->args->dl_earfcn_list[cellsearch_earfcn_index];
set_frequency(); }
} 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) { } else {
Error("Index %d is not a valid EARFCN element.", cellsearch_earfcn_index); current_earfcn = earfcn;
return ret;
} }
Info("Cell Search: changing frequency to EARFCN=%d", current_earfcn);
set_frequency();
// Move to CELL SEARCH and wait to finish // Move to CELL SEARCH and wait to finish
Info("Cell Search: Setting Cell search state"); 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++; 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"); Info("Cell Search: No more frequencies in the current EARFCN set");
cellsearch_earfcn_index = 0; cellsearch_earfcn_index = 0;
ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS; ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS;

@ -224,6 +224,11 @@ void sync_sa::run_state_idle()
void sync_sa::run_state_cell_search() 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 // Receive samples
srsran::rf_buffer_t rf_buffer = {}; srsran::rf_buffer_t rf_buffer = {};
rf_buffer.set_nof_samples(slot_sz); rf_buffer.set_nof_samples(slot_sz);

@ -517,7 +517,7 @@ int main(int argc, char** argv)
phy_test->start(); phy_test->start();
// 1. Cell search // 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()->wait_cell_search(default_timeout));
TESTASSERT(phy_test->get_stack()->cell_search_ret.found == TESTASSERT(phy_test->get_stack()->cell_search_ret.found ==
srsue::rrc_interface_phy_lte::cell_search_ret_t::CELL_FOUND); srsue::rrc_interface_phy_lte::cell_search_ret_t::CELL_FOUND);

@ -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 //! Searches for a cell in the current frequency and retrieves SIB1 if not retrieved yet
bool phy_controller::start_cell_search(srsran::event_observer<cell_srch_res> observer) bool phy_controller::start_cell_search(srsran::event_observer<cell_srch_res> observer, int earfcn)
{ {
if (is_in_state<searching_cell>()) { if (is_in_state<searching_cell>()) {
fsmInfo("Cell search already launched."); fsmInfo("Cell search already launched.");
return true; return true;
} }
trigger(cell_search_cmd{}); trigger(cell_search_cmd{earfcn});
if (not is_in_state<searching_cell>()) { if (not is_in_state<searching_cell>()) {
fsmWarning("Failed to launch cell search"); fsmWarning("Failed to launch cell search");
return false; 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}); 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"); 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) void phy_controller::handle_cell_search_res(searching_cell& s, const cell_srch_res& result)

@ -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 */ /* 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. // Save idleModeMobilityControlInfo, etc.
srsran::console("Received RRC Connection Release (releaseCause: %s)\n", cause.c_str()); 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 // delay actions by 60ms as per 5.3.8.3
task_sched.defer_callback(60, [this]() { start_go_idle(); }); 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 /// 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()); handle_ue_capability_enquiry(c1->ue_cap_enquiry());
break; break;
case dl_dcch_msg_type_c::c1_c_::types::rrc_conn_release: 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; break;
case dl_dcch_msg_type_c::c1_c_::types::ue_info_request_r9: case dl_dcch_msg_type_c::c1_c_::types::ue_info_request_r9:
transaction_id = c1->ue_info_request_r9().rrc_transaction_id; transaction_id = c1->ue_info_request_r9().rrc_transaction_id;

@ -80,6 +80,12 @@ void meas_cell_eutra::set_sib13(const asn1::rrc::sib_type13_r9_s& sib13_)
has_valid_sib13 = true; 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 bool meas_cell::is_sib_scheduled(uint32_t sib_index) const
{ {
return sib_info_map.find(sib_index) != sib_info_map.end(); return sib_info_map.find(sib_index) != sib_info_map.end();

@ -50,7 +50,7 @@ proc_outcome_t rrc::cell_search_proc::init()
{ {
Info("Starting..."); Info("Starting...");
state = state_t::phy_cell_search; 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."); Warning("Failed to initiate Cell Search.");
return proc_outcome_t::error; 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) 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()) { // only start cell reselection if no RRC redirect is present (redirect will trigger a cell search)
rrc_ptr->logger.error("Failed to initiate a Cell Reselection procedure..."); if (rrc_ptr->cell_search_earfcn < 0) {
return; 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 // Save Current RNTI before MAC Reset
uint16_t crnti = rrc_ptr->mac->get_crnti(); 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 // 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) { 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(); 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}", Info("Starting... cause: \"%s\", UE context: {C-RNTI=0x%x, PCI=%d, CELL ID=%d}",
reest_cause == asn1::rrc::reest_cause_opts::recfg_fail reest_cause == asn1::rrc::reest_cause_opts::recfg_fail ? "Reconfiguration failure"
? "Reconfiguration failure" : cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure"
: cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure" : "Other failure", : "Other failure",
reest_rnti, reest_rnti,
reest_source_pci, reest_source_pci,
reest_cellid); reest_cellid);

@ -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_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_sib13(const srsran::sib13_t& sib13) override {}
void set_config_mbsfn_mcch(const srsran::mcch_msg_t& mcch) 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; } bool cell_is_camping() override { return true; }
void deactivate_scells() override {} void deactivate_scells() override {}
bool cell_select(phy_cell_t cell) override bool cell_select(phy_cell_t cell) override

@ -90,7 +90,7 @@ int test_phy_ctrl_fsm()
TESTASSERT(phy_ctrl.is_in_sync()); TESTASSERT(phy_ctrl.is_in_sync());
// TEST: Correct initiation of Cell Search state // 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()); TESTASSERT(not phy_ctrl.is_in_sync());
// TEST: Cell Search only listens to a cell search result event // TEST: Cell Search only listens to a cell search result event

@ -34,7 +34,7 @@ using namespace asn1;
using namespace srsran; using namespace srsran;
namespace srsue { 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_) : rrc_nr::rrc_nr(srsran::task_sched_handle task_sched_) :
logger(srslog::fetch_basic_logger("RRC-NR")), logger(srslog::fetch_basic_logger("RRC-NR")),
@ -71,9 +71,15 @@ int rrc_nr::init(phy_interface_rrc_nr* phy_,
stack = stack_; stack = stack_;
args = args_; args = args_;
plmn_is_selected = true; // short-cut SA test // allocate RRC timers
t300 = task_sched.get_unique_timer(); 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; running = true;
sim_measurement_timer = task_sched.get_unique_timer(); 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); pdcp->add_bearer(args.coreless.drb_lcid, pdcp_cnfg);
return; 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) 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: { case dl_ccch_msg_type_c::c1_c_::types::rrc_reject: {
// 5.3.15 // 5.3.15
const auto& reject = c1->rrc_reject(); const auto& reject = c1->rrc_reject();
srsran::console("Received RRC Reject"); srsran::console("Received RRC Reject\n");
t300.stop(); 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); }); task_sched.defer_task([this, rrc_setup_copy]() { handle_rrc_setup(rrc_setup_copy); });
break; break;
} }
default: default:
logger.error("The provided DL-CCCH message type is not recognized"); logger.error("The provided DL-CCCH message type is not recognized");
break; 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); 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 // clang-format off
// unhandled fields: // unhandled fields:
// - cellSelectionInfo // - 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 for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes
random_id |= ((uint64_t)rand() & 0xFF) << i * 8; 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; rrc_setup_req->establishment_cause = (establishment_cause_opts::options)cause;
send_ul_ccch_msg(ul_ccch_msg); 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); 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 // EUTRA-RRC interface
int rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu) 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; return false;
} }
if (!(drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::eps_bearer_id)) { 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()); // 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; 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 && 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())) { (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); 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); 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; 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 // Security helper used by Security Mode Command and Mobility handling routines
void rrc_nr::generate_as_keys() void rrc_nr::generate_as_keys()
{ {
uint8_t k_asme[32] = {}; as_key_t k_amf = {};
// FIXME: need to add nas->get_k_amf(k_amf);
// nas->get_k_asme(k_asme, 32); logger.debug(k_amf.data(), 32, "UE K_amf");
logger.debug(k_asme, 32, "UE K_asme"); logger.debug("Generating K_gnb with UL NAS COUNT: %d", nas->get_ul_nas_count());
// logger.debug("Generating K_enb with UL NAS COUNT: %d", nas->get_k_enb_count()); usim->generate_nr_as_keys(k_amf, nas->get_ul_nas_count(), &sec_cfg);
// usim->generate_as_keys(k_asme, nas->get_k_enb_count(), &sec_cfg);
logger.info(sec_cfg.k_rrc_enc.data(), 32, "RRC encryption key - k_rrc_enc"); 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_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"); logger.info(sec_cfg.k_up_enc.data(), 32, "UP encryption key - k_up_enc");

@ -100,16 +100,25 @@ proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator
} }
} }
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)) {
return proc_outcome_t::error;
}
}
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) { 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) { 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(); srsran::unique_byte_buffer_t nas_pdu = srsran::make_byte_buffer();
if (nas_sdu != nullptr) { if (nas_pdu != nullptr) {
memcpy(nas_sdu->msg, 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].data(),
rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size()); 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(); nas_pdu->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)); rrc_handle.nas->write_pdu(std::move(nas_pdu));
} else { } else {
rrc_handle.logger.error("Couldn't allocate SDU in %s.", __FUNCTION__); rrc_handle.logger.error("Couldn't allocate SDU in %s.", __FUNCTION__);
return proc_outcome_t::error; return proc_outcome_t::error;
@ -117,13 +126,6 @@ proc_outcome_t rrc_nr::connection_reconf_no_ho_proc::init(const reconf_initiator
} }
} }
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)) {
return proc_outcome_t::error;
}
}
return proc_outcome_t::success; 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..."); Info("Starting...");
if (dedicated_info_nas_) { if (dedicated_info_nas_ == nullptr) {
logger.error("Connection Setup Failed, no dedicatedInfoNAS available"); logger.error("Connection Setup Failed, no dedicatedInfoNAS available");
return proc_outcome_t::error; 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 // until cell selection is done, update PHY config to take the last found PCI
rrc_handle.phy_cfg.carrier.pci = result.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)) { if (not rrc_handle.phy->start_cell_select(cs_args)) {
Error("Could not set start cell search."); Error("Could not set start cell search.");
return proc_outcome_t::error; return proc_outcome_t::error;

@ -118,11 +118,14 @@ class dummy_eutra : public rrc_eutra_interface_rrc_nr
class dummy_nas : public nas_5g_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 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 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; } bool update_nr_context(srsran::as_security_config_t* sec_cfg) { return true; }
}; };

@ -237,8 +237,6 @@ int ue_stack_lte::init(const stack_args_t& args_)
args.rrc_nr); args.rrc_nr);
rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc); 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); nas_5g.init(usim.get(), &rrc_nr, gw, args.nas_5g);
running = true; running = true;
@ -339,7 +337,13 @@ bool ue_stack_lte::disable_data()
bool ue_stack_lte::start_service_request() bool ue_stack_lte::start_service_request()
{ {
if (running) { 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; 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); rlc.get_metrics(metrics.rlc, metrics.mac[0].nof_tti);
nas.get_metrics(&metrics.nas); nas.get_metrics(&metrics.nas);
rrc.get_metrics(metrics.rrc); rrc.get_metrics(metrics.rrc);
rrc_nr.get_metrics(metrics.rrc_nr);
pending_stack_metrics.push(metrics); pending_stack_metrics.push(metrics);
}); });
// wait for result // wait for result

@ -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(); state.set_registered_initiated();
return SRSRAN_SUCCESS; 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); pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes);
} }
has_sec_ctxt = true;
logger.info("Sending Security Mode Complete"); logger.info("Sending Security Mode Complete");
rrc_nr->write_sdu(std::move(pdu)); rrc_nr->write_sdu(std::move(pdu));
ctxt_base.tx_count++; 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) int nas_5g::handle_registration_reject(registration_reject_t& registration_reject)
{ {
logger.info("Handling Registration Reject"); logger.info("Handling Registration Reject");
has_sec_ctxt = false;
ctxt_base.rx_count++; ctxt_base.rx_count++;
state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); 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; return SRSRAN_ERROR;
} }
initial_sec_command = true;
uint8_t res_star[16]; uint8_t res_star[16];
logger.info(authentication_request.authentication_parameter_rand.rand.data(), 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) int nas_5g::handle_authentication_reject(srsran::nas_5g::authentication_reject_t& authentication_reject)
{ {
logger.info("Handling Authentication Reject"); logger.info("Handling Authentication Reject");
has_sec_ctxt = false;
ctxt_base.rx_count++; ctxt_base.rx_count++;
state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search); state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search);
return SRSRAN_SUCCESS; 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) int nas_5g::handle_service_reject(srsran::nas_5g::service_reject_t& service_reject)
{ {
logger.info("Handling Service Reject"); logger.info("Handling Service Reject");
has_sec_ctxt = false;
ctxt_base.rx_count++; ctxt_base.rx_count++;
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
@ -941,10 +951,8 @@ int nas_5g::handle_security_mode_command(security_mode_command_t& security_m
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
initial_sec_command = false; // TODO
if (initial_sec_command) { if (initial_sec_command) {
ctxt_base.rx_count = 0; set_k_gnb_count(0);
ctxt_base.tx_count = 0; ctxt_base.tx_count = 0;
initial_sec_command = false; initial_sec_command = false;
} }
@ -1115,6 +1123,27 @@ void nas_5g::get_metrics(nas_5g_metrics_t& metrics)
metrics.state = state.get_state(); 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 * Helpers
******************************************************************************/ ******************************************************************************/

@ -231,6 +231,7 @@ void usim_base::generate_nas_keys(uint8_t* k_asme,
/* /*
* RRC Interface * RRC Interface
*/ */
void usim_base::generate_as_keys(uint8_t* k_asme_, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) void usim_base::generate_as_keys(uint8_t* k_asme_, uint32_t count_ul, srsran::as_security_config_t* sec_cfg)
{ {
if (!initiated) { if (!initiated) {
@ -376,6 +377,42 @@ bool usim_base::generate_nas_keys_5g(uint8_t* k_amf,
* NR RRC Interface * 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) bool usim_base::generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg)
{ {
if (!initiated) { if (!initiated) {

@ -20,7 +20,7 @@
*/ */
#include "srsran/common/metrics_hub.h" #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_csv.h"
#include "srsue/hdr/metrics_stdout.h" #include "srsue/hdr/metrics_stdout.h"
#include "srsue/hdr/ue_metrics_interface.h" #include "srsue/hdr/ue_metrics_interface.h"
@ -34,10 +34,12 @@ using namespace srsue;
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 // fake classes
class ue_dummy : public ue_metrics_interface class eutra_ue_dummy : public ue_metrics_interface
{ {
public: public:
bool get_metrics(ue_metrics_t* m) bool get_metrics(ue_metrics_t* m)
@ -72,6 +74,51 @@ public:
m->phy.nof_active_cc = 1; m->phy.nof_active_cc = 1;
m->phy.ch[0].rsrp = -10.0f; m->phy.ch[0].rsrp = -10.0f;
m->phy.ch[0].pathloss = 32; 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_pkts = 100;
m->stack.mac_nr[0].rx_errors = 2; m->stack.mac_nr[0].rx_errors = 2;
m->stack.mac_nr[0].rx_brate = 223; 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 } // namespace srsue
void usage(char* prog) void usage(char* prog)
@ -110,38 +182,50 @@ void parse_args(int argc, char** argv)
} }
} }
int main(int argc, char** argv) int ue_test(std::string name, ue_metrics_interface* ue)
{ {
float period = 1.0;
ue_dummy ue;
if (argc < 3) {
usage(argv[0]);
exit(-1);
}
parse_args(argc, argv);
// the default metrics type for stdout output // the default metrics type for stdout output
metrics_stdout metrics_screen; metrics_stdout metrics_screen;
metrics_screen.set_ue_handle(&ue); metrics_screen.set_ue_handle(ue);
// the CSV file writer // the CSV file writer
metrics_csv metrics_file(csv_file_name); metrics_csv metrics_file(csv_file_name);
metrics_file.set_ue_handle(&ue); metrics_file.set_ue_handle(ue);
// create metrics hub and register metrics for stdout // create metrics hub and register metrics for stdout
srsran::metrics_hub<ue_metrics_t> metricshub; srsran::metrics_hub<ue_metrics_t> metricshub;
metricshub.init(&ue, period); metricshub.init(ue, period);
metricshub.add_listener(&metrics_screen); metricshub.add_listener(&metrics_screen);
metricshub.add_listener(&metrics_file); metricshub.add_listener(&metrics_file);
// enable printing // enable printing
metrics_screen.toggle_print(true); metrics_screen.toggle_print(true);
std::cout << "Running for 5 seconds .." << std::endl; std::cout << "Running " << name << " UE test for " << duration_s << " seconds .." << std::endl;
usleep(5e6); usleep(duration_s * 1e6);
metricshub.stop(); metricshub.stop();
return 0;
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
if (argc < 3) {
usage(argv[0]);
exit(-1);
}
parse_args(argc, argv);
eutra_ue_dummy eutra_ue;
TESTASSERT_SUCCESS(ue_test("EUTRA", &eutra_ue));
nsa_ue_dummy nsa_ue;
TESTASSERT_SUCCESS(ue_test("NSA", &nsa_ue));
sa_ue_dummy sa_ue;
TESTASSERT_SUCCESS(ue_test("SA", &sa_ue));
return SRSRAN_SUCCESS;
} }

@ -78,7 +78,7 @@ public:
void set_mch_period_stop(uint32_t stop) override{}; void set_mch_period_stop(uint32_t stop) override{};
// Cell search and selection procedures // 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_select(phy_cell_t cell) override;
bool cell_is_camping() override; bool cell_is_camping() override;

@ -96,7 +96,7 @@ void lte_ttcn3_phy::meas_stop() {}
// configured by the SS, including the ones that we should not even detect because // 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 // their power is too weak. The cell search should only report the cells that
// are actually visible though. // are actually visible though.
bool lte_ttcn3_phy::cell_search() bool lte_ttcn3_phy::cell_search(int earfcn)
{ {
std::lock_guard<std::mutex> lock(phy_mutex); std::lock_guard<std::mutex> lock(phy_mutex);

@ -71,7 +71,7 @@ int ue::init(const all_args_t& args_)
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }
std::unique_ptr<gw> gw_ptr(new gw(srslog::fetch_basic_logger("GW"))); std::unique_ptr<gw> gw_ptr(new gw(srslog::fetch_basic_logger("GW", false)));
if (!gw_ptr) { if (!gw_ptr) {
srsran::console("Error creating a GW instance.\n"); srsran::console("Error creating a GW instance.\n");
return SRSRAN_ERROR; return SRSRAN_ERROR;
@ -285,6 +285,11 @@ int ue::parse_args(const all_args_t& args_)
return SRSRAN_ERROR; 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; return SRSRAN_SUCCESS;
} }

Loading…
Cancel
Save