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 pdsch_cfg_common_s;
struct pucch_cfg_common_s;
struct pucch_cfg_s;
struct pusch_cfg_common_s;
struct mib_s;
@ -144,6 +145,7 @@ void fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg
bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch);
bool fill_phy_pdsch_cfg_common(const asn1::rrc_nr::pdsch_cfg_common_s& pdsch_cfg, srsran_sch_hl_cfg_nr_t* pdsch);
void fill_phy_pucch_cfg_common(const asn1::rrc_nr::pucch_cfg_common_s& pucch_cfg, srsran_pucch_nr_common_cfg_t* pucch);
bool fill_phy_pucch_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch_nr_hl_cfg_t* pucch);
bool fill_phy_pusch_cfg_common(const asn1::rrc_nr::pusch_cfg_common_s& pusch_cfg, srsran_sch_hl_cfg_nr_t* pusch);
void fill_phy_carrier_cfg(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg,
srsran_carrier_nr_t* carrier_nr);

@ -106,6 +106,7 @@ struct k_enb_context_t {
};
struct k_gnb_context_t {
as_key_t k_gnb;
as_key_t sk_gnb;
};
@ -189,6 +190,8 @@ uint8_t security_generate_k_amf(const uint8_t* k_seaf,
uint8_t security_generate_k_seaf(const uint8_t* k_ausf, const char* serving_network_name, uint8_t* k_seaf);
uint8_t security_generate_k_gnb(const as_key_t& k_amf, const uint32_t nas_count, as_key_t& k_gnb);
uint8_t security_generate_k_enb(const uint8_t* k_asme, const uint32_t nas_count, uint8_t* k_enb);
uint8_t security_generate_k_nb_star_common(uint8_t fc,

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

@ -38,7 +38,7 @@ class pdu_session_cfg_t
{
public:
std::string apn_name;
apn_types apn_type;
apn_types apn_type = ipv4;
std::string apn_user;
std::string apn_pass;
};
@ -68,6 +68,8 @@ class nas_5g_interface_rrc_nr
{
public:
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

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

@ -86,6 +86,8 @@ public:
class usim_interface_rrc_nr
{
public:
virtual void
generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) = 0;
virtual bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) = 0;
virtual bool update_nr_context(srsran::as_security_config_t* sec_cfg) = 0;
};

@ -85,7 +85,7 @@ class phy_dummy_interface : public phy_interface_rrc_lte
void meas_stop() override {}
/* Cell search and selection procedures */
bool cell_search() override { return true; }
bool cell_search(int earfcn) override { return true; }
bool cell_select(phy_cell_t cell) override { return true; }
bool cell_is_camping() override { return false; }
};

@ -98,7 +98,7 @@ public:
} else {
encryption_direction = direction;
}
logger.debug("LCID=%d encryption=%s", lcid, srsran_direction_text[integrity_direction]);
logger.debug("LCID=%d, encryption=%s", lcid, srsran_direction_text[integrity_direction]);
}
void enable_security_timed(srsran_direction_t direction, uint32_t sn)

@ -580,42 +580,41 @@ bool make_phy_csi_report(const csi_report_cfg_s& csi_report_cfg,
}
if (srsran_csi_hl_report_cfg.type == SRSRAN_CSI_REPORT_TYPE_PERIODIC) {
srsran_csi_hl_report_cfg.periodic.period =
csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type().to_number();
switch (csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type()) {
const auto& csi_periodic = csi_report_cfg.report_cfg_type.periodic();
srsran_csi_hl_report_cfg.periodic.period = csi_periodic.report_slot_cfg.type().to_number();
switch (csi_periodic.report_slot_cfg.type()) {
case csi_report_periodicity_and_offset_c::types_opts::slots4:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots4();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots4();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots5:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots5();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots5();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots8:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots8();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots8();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots10:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots10();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots10();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots16:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots16();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots16();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots20:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots20();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots20();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots40:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots40();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots40();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots80:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots80();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots80();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots160:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots160();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots160();
break;
case csi_report_periodicity_and_offset_c::types_opts::slots320:
srsran_csi_hl_report_cfg.periodic.offset = csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.slots320();
srsran_csi_hl_report_cfg.periodic.offset = csi_periodic.report_slot_cfg.slots320();
break;
default:
asn1::log_warning("Invalid option for report_slot_cfg %s",
csi_report_cfg.report_cfg_type.periodic().report_slot_cfg.type().to_string());
asn1::log_warning("Invalid option for report_slot_cfg %s", csi_periodic.report_slot_cfg.type().to_string());
return false;
}
}
@ -699,6 +698,7 @@ bool make_phy_csi_report(const csi_report_cfg_s& csi_report_cfg,
asn1::log_warning("Invalid option for cqi_table %s", csi_report_cfg.cqi_table.to_string());
return false;
}
*in_srsran_csi_hl_report_cfg = srsran_csi_hl_report_cfg;
return true;
}
@ -805,6 +805,10 @@ bool make_phy_res_config(const pucch_res_s& pucch_res,
{
srsran_pucch_nr_resource_t srsran_pucch_nr_resource = {};
srsran_pucch_nr_resource.starting_prb = pucch_res.start_prb;
srsran_pucch_nr_resource.intra_slot_hopping = pucch_res.intra_slot_freq_hop_present;
if (pucch_res.second_hop_prb_present) {
srsran_pucch_nr_resource.second_hop_prb = pucch_res.second_hop_prb;
}
switch (pucch_res.format.type()) {
case pucch_res_s::format_c_::types_opts::format0:
srsran_pucch_nr_resource.format = SRSRAN_PUCCH_NR_FORMAT_0;
@ -1646,6 +1650,21 @@ bool make_csi_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_ce
if (not make_phy_csi_report(csi_rep, &csi_hl->reports[i])) {
return false;
}
if (csi_rep.report_cfg_type.type().value == csi_report_cfg_s::report_cfg_type_c_::types_opts::periodic) {
const auto& pucch_setup = serv_cell.ul_cfg.init_ul_bwp.pucch_cfg.setup();
srsran_pucch_nr_resource_t& resource = csi_hl->reports[i].periodic.resource;
uint32_t pucch_resource_id = csi_rep.report_cfg_type.periodic().pucch_csi_res_list[0].pucch_res;
const auto& asn1_resource = pucch_setup.res_to_add_mod_list[pucch_resource_id];
uint32_t format2_rate = 0;
if (pucch_setup.format2_present and
pucch_setup.format2.type().value == asn1::setup_release_c<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)
{
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/ssl.h"
#include "srsran/config.h"
#include <arpa/inet.h>
#ifdef HAVE_MBEDTLS
@ -204,6 +203,32 @@ uint8_t security_generate_k_amf(const uint8_t* k_seaf,
return SRSRAN_SUCCESS;
}
uint8_t security_generate_k_gnb(const as_key_t& k_amf, const uint32_t nas_count_, as_key_t& k_gnb)
{
if (k_amf.empty()) {
log_error("Invalid inputs");
return SRSRAN_ERROR;
}
// NAS Count
std::vector<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)
{
if (k_asme == NULL || k_enb == NULL) {

@ -637,10 +637,23 @@ int srsran_pbch_nr_decode(srsran_pbch_nr_t* q,
return SRSRAN_ERROR;
}
// Avoid NAN getting into the demodulator
for (uint32_t i = 0; i < PBCH_NR_M; i++) {
if (isnan(__real__ symbols[i]) || isnan(__imag__ symbols[i])) {
symbols[i] = 0.0f;
}
}
// 7.3.3.2 Modulation
int8_t llr[PBCH_NR_E];
srsran_demod_soft_demodulate_b(SRSRAN_MOD_QPSK, symbols, llr, PBCH_NR_M);
// If all LLR are zero, no message could be received
if (srsran_vec_avg_power_bf(llr, PBCH_NR_E) == 0) {
SRSRAN_MEM_ZERO(msg, srsran_pbch_msg_nr_t, 1);
return SRSRAN_SUCCESS;
}
// TS 38.211 7.3.3 Physical broadcast channel
// 7.3.3.1 Scrambling
pbch_nr_scramble_rx(cfg, cfg->ssb_idx, llr, llr);

@ -641,6 +641,10 @@ if(RF_FOUND)
target_link_libraries(prach_test_usrp srsran_rf srsran_phy pthread)
endif(RF_FOUND)
add_executable(prach_nr_test_perf EXCLUDE_FROM_ALL prach_nr_test_perf.c)
target_link_libraries(prach_nr_test_perf srsran_phy)
# this is just for performance evaluation, not for unit testing
########################################################################
# NR
########################################################################

@ -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_,
void** data_,
void* data_[SRSRAN_MAX_PORTS],
int nsamples,
time_t 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++) {
avg += ptr[n];
}
dst[i] = avg;
dst[i] = avg; // divide by decim_factor later via scale
}
rf_zmq_info(handler->id,
@ -810,6 +810,10 @@ int rf_zmq_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool bl
pthread_mutex_lock(&handler->rx_gain_mutex);
float scale = srsran_convert_dB_to_amplitude(handler->rx_gain);
pthread_mutex_unlock(&handler->rx_gain_mutex);
// scale shall also incorporate decim_factor
if (decim_factor > 0) {
scale = scale / decim_factor;
}
for (uint32_t c = 0; c < handler->nof_channels; c++) {
if (buffers[c]) {
srsran_vec_sc_prod_cfc(buffers[c], scale, buffers[c], nsamples);

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

@ -46,7 +46,7 @@
/*
* Default NR-PBCH DMRS normalised correlation (RSRP/EPRE) threshold
*/
#define SSB_PBCH_DMRS_DEFAULT_CORR_THR 0.6f
#define SSB_PBCH_DMRS_DEFAULT_CORR_THR 0.5f
static int ssb_init_corr(srsran_ssb_t* q)
{
@ -865,8 +865,9 @@ static int ssb_pss_search(srsran_ssb_t* q,
// Find maximum
uint32_t peak_idx = srsran_vec_max_abs_ci(q->tmp_time, q->corr_window);
// Average power, skip window if value is invalid (0.0, nan or inf)
float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz);
// Average power, take total power of the frequency domain signal after filtering, skip correlation window if
// value is invalid (0.0, nan or inf)
float avg_pwr_corr = srsran_vec_avg_power_cf(q->tmp_corr, q->corr_sz);
if (!isnormal(avg_pwr_corr)) {
continue;
}
@ -974,6 +975,11 @@ int srsran_ssb_csi_search(srsran_ssb_t* q,
t_offset = 0;
}
// Make sure SSB time offset is in bounded in the input buffer
if (t_offset + q->ssb_sz > nof_samples) {
return SRSRAN_SUCCESS;
}
// Demodulate
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, in, t_offset, coarse_cfo_hz, ssb_grid) < SRSRAN_SUCCESS) {
@ -1047,7 +1053,8 @@ static int ssb_select_pbch(srsran_ssb_t* q,
uint32_t N_id,
const cf_t ssb_grid[SRSRAN_SSB_NOF_RE],
uint32_t* found_n_hf,
uint32_t* found_ssb_idx_4lsb)
uint32_t* found_ssb_idx_4lsb,
srsran_dmrs_pbch_meas_t* pbch_meas)
{
// Prepare PBCH DMRS configuration
srsran_dmrs_pbch_cfg_t pbch_dmrs_cfg = {};
@ -1089,6 +1096,7 @@ static int ssb_select_pbch(srsran_ssb_t* q,
// Save findings
*found_n_hf = best_n_hf;
*found_ssb_idx_4lsb = best_ssb_idx;
*pbch_meas = best_meas;
return SRSRAN_SUCCESS;
}
@ -1186,6 +1194,9 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs
return SRSRAN_ERROR;
}
// Set the SSB search result with default value with PBCH CRC unmatched, meaning no cell is found
SRSRAN_MEM_ZERO(res, srsran_ssb_search_res_t, 1);
// Search for PSS in time domain
uint32_t N_id_2 = 0;
uint32_t t_offset = 0;
@ -1202,6 +1213,11 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs
t_offset = 0;
}
// Make sure SSB time offset is in bounded in the input buffer
if (t_offset + q->ssb_sz > nof_samples) {
return SRSRAN_SUCCESS;
}
// Demodulate
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, in, t_offset, coarse_cfo_hz, ssb_grid) < SRSRAN_SUCCESS) {
@ -1223,18 +1239,30 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs
// Select the most suitable SSB candidate
uint32_t n_hf = 0;
uint32_t ssb_idx = 0;
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) {
srsran_dmrs_pbch_meas_t pbch_meas = {};
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx, &pbch_meas) < SRSRAN_SUCCESS) {
ERROR("Error selecting PBCH");
return SRSRAN_ERROR;
}
// Compute PBCH channel estimates
// Avoid decoding if the selected PBCH DMRS do not reach the minimum threshold
if (pbch_meas.corr < q->args.pbch_dmrs_thr) {
return SRSRAN_SUCCESS;
}
// Decode PBCH
srsran_pbch_msg_nr_t pbch_msg = {};
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, &pbch_msg) < SRSRAN_SUCCESS) {
ERROR("Error decoding PBCH");
return SRSRAN_ERROR;
}
// If PBCH was not decoded, skip measurements
if (!pbch_msg.crc) {
return SRSRAN_SUCCESS;
}
// Perform measurements from PSS and SSS
srsran_csi_trs_measurements_t measurements = {};
if (ssb_measure(q, ssb_grid, N_id, &measurements) < SRSRAN_SUCCESS) {
ERROR("Error measuring");
@ -1336,6 +1364,9 @@ int srsran_ssb_find(srsran_ssb_t* q,
return SRSRAN_ERROR;
}
// Set the PBCH message result with default value (CRC unmatched), meaning no cell is found
SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1);
// Copy tail from previous execution into the start of this
srsran_vec_cf_copy(q->sf_buffer, &q->sf_buffer[q->sf_sz], q->ssb_sz);
@ -1356,6 +1387,11 @@ int srsran_ssb_find(srsran_ssb_t* q,
t_offset = 0;
}
// Make sure SSB time offset is in bounded in the input buffer
if (t_offset > q->sf_sz) {
return SRSRAN_SUCCESS;
}
// Demodulate
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_demodulate(q, q->sf_buffer, t_offset, 0.0f, ssb_grid) < SRSRAN_SUCCESS) {
@ -1372,11 +1408,17 @@ int srsran_ssb_find(srsran_ssb_t* q,
// Select the most suitable SSB candidate
uint32_t n_hf = 0;
uint32_t ssb_idx = 0; // SSB candidate index
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx) < SRSRAN_SUCCESS) {
srsran_dmrs_pbch_meas_t pbch_meas = {};
if (ssb_select_pbch(q, N_id, ssb_grid, &n_hf, &ssb_idx, &pbch_meas) < SRSRAN_SUCCESS) {
ERROR("Error selecting PBCH");
return SRSRAN_ERROR;
}
// Avoid decoding if the selected PBCH DMRS do not reach the minimum threshold
if (pbch_meas.corr < q->args.pbch_dmrs_thr) {
return SRSRAN_SUCCESS;
}
// Calculate the SSB offset in the subframe
uint32_t ssb_offset = srsran_ssb_candidate_sf_offset(q, ssb_idx);

@ -29,6 +29,9 @@
#include <srsran/phy/utils/random.h>
#include <stdlib.h>
#define SSB_DECODE_TEST_PCI_STRIDE 53
#define SSB_DECODE_TEST_SSB_STRIDE 3
// NR parameters
static uint32_t carrier_nof_prb = 52;
static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz;
@ -128,7 +131,7 @@ static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg, uint32_t ssb_idx)
pbch_msg->crc = true;
}
static int test_case_1(srsran_ssb_t* ssb)
static int test_case_true(srsran_ssb_t* ssb)
{
// For benchmarking purposes
uint64_t t_encode_usec = 0;
@ -147,8 +150,8 @@ static int test_case_1(srsran_ssb_t* ssb)
// For each PCI...
uint64_t count = 0;
for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += 23) {
for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx++, count++) {
for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += SSB_DECODE_TEST_PCI_STRIDE) {
for (uint32_t ssb_idx = 0; ssb_idx < ssb->Lmax; ssb_idx += SSB_DECODE_TEST_SSB_STRIDE, count++) {
struct timeval t[3] = {};
// Build PBCH message
@ -158,7 +161,7 @@ static int test_case_1(srsran_ssb_t* ssb)
// Print encoded PBCH message
char str[512] = {};
srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str));
INFO("test_case_1 - encoded pci=%d %s", pci, str);
INFO("test_case_true - encoded pci=%d %s", pci, str);
// Initialise baseband
srsran_vec_cf_zero(buffer, hf_len);
@ -184,7 +187,7 @@ static int test_case_1(srsran_ssb_t* ssb)
// Print decoded PBCH message
srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str));
INFO("test_case_1 - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO");
INFO("test_case_true - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO");
// Assert PBCH message CRC
TESTASSERT(pbch_msg_rx.crc);
@ -200,7 +203,7 @@ static int test_case_1(srsran_ssb_t* ssb)
// Print decoded PBCH message
srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str));
INFO("test_case_1 - found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO");
INFO("test_case_true - found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO");
// Assert PBCH message CRC
TESTASSERT(res.pbch_msg.crc);
@ -209,11 +212,11 @@ static int test_case_1(srsran_ssb_t* ssb)
}
if (!count) {
ERROR("Error in test case 1: undefined division");
ERROR("Error in test case true: undefined division");
return SRSRAN_ERROR;
}
INFO("test_case_1 - %.1f usec/encode; %.1f usec/decode; %.1f usec/decode;",
INFO("test_case_true - %.1f usec/encode; %.1f usec/decode; %.1f usec/decode;",
(double)t_encode_usec / (double)(count),
(double)t_decode_usec / (double)(count),
(double)t_search_usec / (double)(count));
@ -221,6 +224,77 @@ static int test_case_1(srsran_ssb_t* ssb)
return SRSRAN_SUCCESS;
}
static int test_case_false(srsran_ssb_t* ssb)
{
// For benchmarking purposes
uint64_t t_decode_usec = 0;
uint64_t t_search_usec = 0;
// SSB configuration
srsran_ssb_cfg_t ssb_cfg = {};
ssb_cfg.srate_hz = srate_hz;
ssb_cfg.center_freq_hz = carrier_freq_hz;
ssb_cfg.ssb_freq_hz = ssb_freq_hz;
ssb_cfg.scs = ssb_scs;
ssb_cfg.pattern = ssb_pattern;
TESTASSERT(srsran_ssb_set_cfg(ssb, &ssb_cfg) == SRSRAN_SUCCESS);
// For each PCI...
uint32_t count = 0;
for (uint32_t pci = 0; pci < SRSRAN_NOF_NID_NR; pci += SSB_DECODE_TEST_PCI_STRIDE, count++) {
struct timeval t[3] = {};
// Initialise baseband
srsran_vec_cf_zero(buffer, hf_len);
// Channel, as it is zero it only adds noise
srsran_channel_awgn_run_c(&awgn, buffer, buffer, hf_len);
// Decode
gettimeofday(&t[1], NULL);
srsran_pbch_msg_nr_t pbch_msg_rx = {};
TESTASSERT(srsran_ssb_decode_pbch(ssb, pci, false, 0, buffer, &pbch_msg_rx) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_decode_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Print decoded PBCH message
char str[512] = {};
srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str));
INFO("test_case_false - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO");
// Assert PBCH message CRC is not okay
TESTASSERT(!pbch_msg_rx.crc);
// Search
srsran_ssb_search_res_t res = {};
gettimeofday(&t[1], NULL);
TESTASSERT(srsran_ssb_search(ssb, buffer, hf_len, &res) == SRSRAN_SUCCESS);
gettimeofday(&t[2], NULL);
get_time_interval(t);
t_search_usec += t[0].tv_usec + t[0].tv_sec * 1000000UL;
// Print decoded PBCH message
srsran_pbch_msg_info(&res.pbch_msg, str, sizeof(str));
INFO("test_case_false - false found pci=%d %s crc=%s", res.N_id, str, res.pbch_msg.crc ? "OK" : "KO");
// Assert PBCH message CRC
TESTASSERT(!res.pbch_msg.crc);
}
if (!count) {
ERROR("Error in test case true: undefined division");
return SRSRAN_ERROR;
}
INFO("test_case_false - %.1f usec/decode; %.1f usec/decode;",
(double)t_decode_usec / (double)(count),
(double)t_search_usec / (double)(count));
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSRAN_ERROR;
@ -257,7 +331,12 @@ int main(int argc, char** argv)
goto clean_exit;
}
if (test_case_1(&ssb) != SRSRAN_SUCCESS) {
if (test_case_true(&ssb) != SRSRAN_SUCCESS) {
ERROR("test case failed");
goto clean_exit;
}
if (test_case_false(&ssb) != SRSRAN_SUCCESS) {
ERROR("test case failed");
goto clean_exit;
}

@ -167,6 +167,26 @@ int test_generate_k_nas()
return SRSRAN_SUCCESS;
}
int test_generate_k_gnb()
{
auto& logger = srslog::fetch_basic_logger("LOG", false);
as_key_t k_gnb_o;
as_key_t k_gnb = {0x49, 0x3a, 0x16, 0xc5, 0x8b, 0x77, 0xb6, 0x27, 0xfa, 0x3f, 0x1a, 0xc6, 0x34, 0x4c, 0x18, 0x30,
0x39, 0xf0, 0x1b, 0xa0, 0xcb, 0x76, 0x36, 0xbb, 0xcc, 0xc4, 0x36, 0x5b, 0x02, 0x3b, 0xd5, 0x62};
as_key_t k_amf = {0xd6, 0x55, 0xf1, 0x61, 0x42, 0x03, 0x5d, 0x4d, 0x72, 0xca, 0x39, 0x58, 0x3d, 0x22, 0x8d, 0x2d,
0xd2, 0xec, 0x0c, 0xa7, 0x92, 0x9a, 0xd0, 0x07, 0xf5, 0x3b, 0x38, 0x2d, 0x05, 0x54, 0x44, 0x05};
uint32_t nas_ul_count = 0;
TESTASSERT(srsran::security_generate_k_gnb(k_amf, nas_ul_count, k_gnb_o) == SRSRAN_SUCCESS);
TESTASSERT(k_gnb_o == k_gnb);
return SRSRAN_SUCCESS;
}
int test_generate_k_enb()
{
auto& logger = srslog::fetch_basic_logger("LOG", false);
@ -442,7 +462,7 @@ int main(int argc, char** argv)
TESTASSERT(test_generate_up_keys() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_enb_star() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_nh() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_gnb() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_res_star() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_ausf() == SRSRAN_SUCCESS);
TESTASSERT(test_generate_k_seaf() == SRSRAN_SUCCESS);

@ -270,6 +270,7 @@ private:
void
set_state(s1ap_proc_id_t state, const erab_id_list& erabs_updated, const erab_item_list& erabs_failed_to_update);
s1ap_proc_id_t get_state() const { return current_state; }
ue_ctxt_t ctxt = {};
uint16_t stream_id = 1;

@ -785,6 +785,14 @@ bool s1ap::handle_initialctxtsetuprequest(const init_context_setup_request_s& ms
return false;
}
if (u->get_state() == s1ap_proc_id_t::init_context_setup_request) {
logger.warning("Initial Context Setup Request already in progress. Ignoring ICS request.");
asn1::s1ap::cause_c cause;
cause.set_protocol().value = cause_protocol_opts::msg_not_compatible_with_receiver_state;
send_error_indication(cause, msg->enb_ue_s1ap_id.value.value, msg->mme_ue_s1ap_id.value.value);
return false;
}
// Setup UE ctxt in RRC
if (not rrc->setup_ue_ctxt(u->ctxt.rnti, msg)) {
return false;
@ -1999,6 +2007,9 @@ s1ap::ue* s1ap::handle_s1apmsg_ue_id(uint32_t enb_id, uint32_t mme_id)
ue* user_ptr = users.find_ue_enbid(enb_id);
ue* user_mme_ptr = nullptr;
cause_c cause;
logger.info("Checking UE S1 logical connection. eNB UE S1AP ID=%d, MME UE S1AP ID=%d", enb_id, mme_id);
if (user_ptr != nullptr) {
if (user_ptr->ctxt.mme_ue_s1ap_id == mme_id) {
// No ID inconsistency found

@ -92,8 +92,8 @@ struct sched_nr_cell_cfg_t {
uint32_t pci;
uint32_t dl_cell_nof_prb;
uint32_t ul_cell_nof_prb;
asn1::copy_ptr<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::dl_cfg_common_sib_s dl_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;
ssb_positions_in_burst_t ssb_positions_in_burst;
uint32_t ssb_periodicity_ms = 0;
@ -107,8 +107,6 @@ struct sched_nr_cell_cfg_t {
double dl_center_frequency_hz;
double ul_center_frequency_hz;
double ssb_center_freq_hz;
uint32_t offset_to_carrier;
srsran_subcarrier_spacing_t scs;
};
class sched_nr_interface

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

@ -47,11 +47,13 @@ struct rrc_cell_cfg_nr_t {
uint32_t ul_arfcn; // UL freq also in phy_cell
uint32_t dl_absolute_freq_point_a; // derived from DL ARFCN
uint32_t ul_absolute_freq_point_a; // derived from UL ARFCN
uint32_t ssb_absolute_freq_point; // derived from DL ARFCN (SSB arfcn)
uint32_t band;
uint32_t coreset0_idx; // Table 13-{1,...15} row index
srsran_duplex_mode_t duplex_mode;
srsran_ssb_cfg_t ssb_cfg;
double ssb_freq_hz;
uint32_t ssb_absolute_freq_point; // derived from DL ARFCN (SSB arfcn)
srsran_subcarrier_spacing_t ssb_scs;
srsran_ssb_patern_t ssb_pattern;
};
typedef std::vector<rrc_cell_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
int update_mac(const asn1::rrc_nr::cell_group_cfg_s& cell_group_config, bool is_config_complete);
/// Update AS security config on active RB
int update_as_security(uint32_t lcid, bool enable_integrity, bool enable_ciphering);
int pack_rrc_reconfiguration(asn1::dyn_octstring& packed_rrc_reconfig);
int pack_secondary_cell_group_cfg(asn1::dyn_octstring& packed_secondary_cell_config);

@ -133,7 +133,7 @@ public:
for (ue_event_t& ev : current_slot_ue_events) {
auto ue_it = ues.find(ev.rnti);
if (ue_it == ues.end()) {
sched_logger.warning("SCHED: \"%s\" called for inexistent rnti=0x%x.", ev.event_name, ev.rnti);
sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x.", ev.event_name, ev.rnti);
ev.rnti = SRSRAN_INVALID_RNTI;
} else if (ue_it->second->has_ca()) {
// events specific to existing UEs with CA
@ -161,7 +161,7 @@ public:
}
auto ue_it = ues.find(ev.rnti);
if (ue_it == ues.end()) {
sched_logger.warning("SCHED: \"%s\" called for inexistent rnti=0x%x.", ev.event_name, ev.rnti);
sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x.", ev.event_name, ev.rnti);
ev.rnti = SRSRAN_INVALID_RNTI;
} else if (not ue_it->second->has_ca() and ue_it->second->carriers[cc] != nullptr) {
ev.callback(*ue_it->second, evlogger);
@ -174,7 +174,7 @@ public:
if (ue_it != ues.end() and ue_it->second->carriers[cc] != nullptr) {
ev.callback(*ue_it->second->carriers[cc], evlogger);
} else {
sched_logger.warning("SCHED: \"%s\" called for inexistent rnti=0x%x,cc=%d.", ev.event_name, ev.rnti, ev.cc);
sched_logger.warning("SCHED: \"%s\" called for unknown rnti=0x%x,cc=%d.", ev.event_name, ev.rnti, ev.cc);
}
}
}

@ -149,15 +149,11 @@ cell_config_manager::cell_config_manager(uint32_t cc_,
carrier.dl_center_frequency_hz = cell.dl_center_frequency_hz;
carrier.ul_center_frequency_hz = cell.ul_center_frequency_hz;
carrier.ssb_center_freq_hz = cell.ssb_center_freq_hz;
carrier.offset_to_carrier = cell.offset_to_carrier;
carrier.scs = cell.scs;
carrier.nof_prb = cell.dl_cell_nof_prb;
carrier.start = 0; // TODO: Check
carrier.max_mimo_layers = cell.nof_layers;
if (cell.dl_cfg_common.is_present()) {
carrier.offset_to_carrier = cell.dl_cfg_common->freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier;
carrier.scs = (srsran_subcarrier_spacing_t)cell.dl_cfg_common->init_dl_bwp.generic_params.subcarrier_spacing.value;
}
carrier.offset_to_carrier = cell.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier;
carrier.scs = (srsran_subcarrier_spacing_t)cell.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value;
// TDD-UL-DL-ConfigCommon
duplex.mode = SRSRAN_DUPLEX_MODE_FDD;

@ -35,7 +35,7 @@ uint32_t coreset_nof_cces(const srsran_coreset_t& coreset)
void make_mib_cfg(const sched_nr_cell_cfg_t& cfg, srsran_mib_nr_t* mib)
{
*mib = {};
mib->scs_common = cfg.scs;
mib->scs_common = (srsran_subcarrier_spacing_t)cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value;
mib->ssb_offset = 6; // TODO
mib->dmrs_typeA_pos = (srsran_dmrs_sch_typeA_pos_t)cfg.dmrs_type_a_position.value;
mib->coreset0_idx = cfg.pdcch_cfg_sib1.ctrl_res_set_zero;
@ -62,13 +62,11 @@ void make_ssb_cfg(const sched_nr_cell_cfg_t& cfg, srsran::phy_cfg_nr_t::ssb_cfg_
}
ssb->scs = (srsran_subcarrier_spacing_t)cfg.ssb_scs.value;
ssb->pattern = SRSRAN_SSB_PATTERN_A;
if (cfg.dl_cfg_common.is_present()) {
if (cfg.dl_cfg_common->freq_info_dl.freq_band_list.size() > 0 and
cfg.dl_cfg_common->freq_info_dl.freq_band_list[0].freq_band_ind_nr_present) {
uint32_t band = cfg.dl_cfg_common->freq_info_dl.freq_band_list[0].freq_band_ind_nr;
if (cfg.dl_cfg_common.freq_info_dl.freq_band_list.size() > 0 and
cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present) {
uint32_t band = cfg.dl_cfg_common.freq_info_dl.freq_band_list[0].freq_band_ind_nr;
ssb->pattern = srsran::srsran_band_helper::get_ssb_pattern(band, ssb->scs);
}
}
}
srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_cell_cfg_t& cfg)
@ -93,8 +91,9 @@ srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_cell_cfg_t& cfg)
ue_phy_cfg.carrier.dl_center_frequency_hz = cfg.dl_center_frequency_hz;
ue_phy_cfg.carrier.ul_center_frequency_hz = cfg.ul_center_frequency_hz;
ue_phy_cfg.carrier.ssb_center_freq_hz = cfg.ssb_center_freq_hz;
ue_phy_cfg.carrier.offset_to_carrier = cfg.offset_to_carrier;
ue_phy_cfg.carrier.scs = cfg.scs;
ue_phy_cfg.carrier.offset_to_carrier = cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier;
ue_phy_cfg.carrier.scs =
(srsran_subcarrier_spacing_t)cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value;
ue_phy_cfg.carrier.nof_prb = cfg.dl_cell_nof_prb;
ue_phy_cfg.carrier.max_mimo_layers = cfg.nof_layers;
make_ssb_cfg(cfg, &ue_phy_cfg.ssb);

@ -235,7 +235,7 @@ void ue::new_slot(slot_point pdcch_slot)
slot_ue ue::make_slot_ue(slot_point pdcch_slot, uint32_t cc)
{
srsran_assert(carriers[cc] != nullptr, "make_slot_ue() called for inexistent rnti=0x%x,cc=%d", rnti, cc);
srsran_assert(carriers[cc] != nullptr, "make_slot_ue() called for unknown rnti=0x%x,cc=%d", rnti, cc);
return slot_ue(*carriers[cc], pdcch_slot);
}

@ -62,8 +62,13 @@ inline sched_nr_cell_cfg_t get_default_cell_cfg(const srsran::phy_cfg_nr_t& phy_
cell_cfg.dl_center_frequency_hz = phy_cfg.carrier.dl_center_frequency_hz;
cell_cfg.ul_center_frequency_hz = phy_cfg.carrier.ul_center_frequency_hz;
cell_cfg.ssb_center_freq_hz = phy_cfg.carrier.ssb_center_freq_hz;
cell_cfg.offset_to_carrier = phy_cfg.carrier.offset_to_carrier;
cell_cfg.scs = phy_cfg.carrier.scs;
cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list.resize(1);
cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing =
(asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.carrier.scs;
cell_cfg.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier =
phy_cfg.carrier.offset_to_carrier;
cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing =
(asn1::rrc_nr::subcarrier_spacing_opts::options)phy_cfg.carrier.scs;
cell_cfg.dl_cell_nof_prb = phy_cfg.carrier.nof_prb;
cell_cfg.nof_layers = phy_cfg.carrier.max_mimo_layers;
cell_cfg.ssb_periodicity_ms = phy_cfg.ssb.periodicity_ms;

@ -22,7 +22,7 @@ set(SOURCES rrc_nr_config_utils.cc)
add_library(srsgnb_rrc_config_utils STATIC ${SOURCES})
target_link_libraries(srsgnb_rrc_config_utils srsran_phy)
set(SOURCES rrc_nr.cc rrc_nr_ue.cc rrc_nr_security_context.cc cell_asn1_config.cc)
set(SOURCES rrc_nr.cc rrc_nr_ue.cc rrc_nr_security_context.cc cell_asn1_config.cc rrc_nr_du_manager.cc)
add_library(srsgnb_rrc STATIC ${SOURCES})
target_link_libraries(srsgnb_rrc srsgnb_rrc_config_utils)

@ -131,6 +131,18 @@ void set_rach_cfg_common(const srsran_prach_cfg_t& prach_cfg, asn1::rrc_nr::rach
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
void fill_tdd_ul_dl_config_common(const rrc_cell_cfg_nr_t& cfg, asn1::rrc_nr::tdd_ul_dl_cfg_common_s& tdd)
{
srsran_assert(cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD, "This function should only be called for TDD configs");
// TDD UL-DL config
tdd.ref_subcarrier_spacing.value = (asn1::rrc_nr::subcarrier_spacing_opts::options)cfg.phy_cell.carrier.scs;
tdd.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10;
tdd.pattern1.nrof_dl_slots = 6;
tdd.pattern1.nrof_dl_symbols = 0;
tdd.pattern1.nrof_ul_slots = 4;
tdd.pattern1.nrof_ul_symbols = 0;
}
/// Fill list of CSI-ReportConfig with gNB config
int fill_csi_report_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas_cfg)
{
@ -174,7 +186,7 @@ int fill_csi_report_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_me
csi_report.subband_size = asn1::rrc_nr::csi_report_cfg_s::subband_size_opts::value1;
if (cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_FDD) {
csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 9;
csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 1;
} else {
csi_report.report_cfg_type.periodic().report_slot_cfg.slots80() = 7;
}
@ -224,11 +236,11 @@ void fill_nzp_csi_rs_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_m
auto& nzp_csi_res = csi_meas_cfg.nzp_csi_rs_res_to_add_mod_list;
// item 0
nzp_csi_res[0].nzp_csi_rs_res_id = 0;
nzp_csi_res[0].res_map.freq_domain_alloc.set_other();
nzp_csi_res[0].res_map.freq_domain_alloc.other().from_number(0b100000);
nzp_csi_res[0].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p2;
nzp_csi_res[0].res_map.freq_domain_alloc.set_row2();
nzp_csi_res[0].res_map.freq_domain_alloc.row2().from_number(0x800);
nzp_csi_res[0].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res[0].res_map.first_ofdm_symbol_in_time_domain = 4;
nzp_csi_res[0].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::fd_cdm2;
nzp_csi_res[0].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res[0].res_map.density.set_one();
nzp_csi_res[0].res_map.freq_band.start_rb = 0;
nzp_csi_res[0].res_map.freq_band.nrof_rbs = 52;
@ -246,7 +258,7 @@ void fill_nzp_csi_rs_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_m
nzp_csi_res[1] = nzp_csi_res[0];
nzp_csi_res[1].nzp_csi_rs_res_id = 1;
nzp_csi_res[1].res_map.freq_domain_alloc.set_row1();
nzp_csi_res[1].res_map.freq_domain_alloc.row1().from_number(0b0001);
nzp_csi_res[1].res_map.freq_domain_alloc.row1().from_number(0x1);
nzp_csi_res[1].res_map.nrof_ports.value = asn1::rrc_nr::csi_rs_res_map_s::nrof_ports_opts::p1;
nzp_csi_res[1].res_map.cdm_type.value = asn1::rrc_nr::csi_rs_res_map_s::cdm_type_opts::no_cdm;
nzp_csi_res[1].res_map.density.set_three();
@ -425,8 +437,8 @@ void fill_csi_resource_cfg_to_add(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_m
csi_meas_cfg.csi_res_cfg_to_add_mod_list[0].res_type.value = csi_res_cfg_s::res_type_opts::periodic;
csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_res_cfg_id = 1;
auto& imres = csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_rs_res_set_list.set_csi_im_res_set_list();
imres.push_back(0);
auto& im_res = csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].csi_rs_res_set_list.set_csi_im_res_set_list();
im_res.push_back(0);
csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].bwp_id = 0;
csi_meas_cfg.csi_res_cfg_to_add_mod_list[1].res_type.value = csi_res_cfg_s::res_type_opts::periodic;
@ -603,10 +615,10 @@ void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg
uint32_t j = 0, j2 = 0;
for (uint32_t i = 0; i < out.res_to_add_mod_list.size(); ++i) {
out.res_to_add_mod_list[i].pucch_res_id = i;
out.res_to_add_mod_list[i].intra_slot_freq_hop_present = true;
out.res_to_add_mod_list[i].second_hop_prb_present = true;
out.res_to_add_mod_list[i].intra_slot_freq_hop_present = false;
if (i < 8 or i == 16) {
out.res_to_add_mod_list[i].start_prb = 51;
out.res_to_add_mod_list[i].second_hop_prb_present = true;
out.res_to_add_mod_list[i].second_hop_prb = 0;
out.res_to_add_mod_list[i].format.set_format1().init_cyclic_shift = (4 * (j % 3));
out.res_to_add_mod_list[i].format.format1().nrof_symbols = 14;
@ -615,6 +627,7 @@ void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg
j++;
} else if (i < 15) {
out.res_to_add_mod_list[i].start_prb = 1;
out.res_to_add_mod_list[i].second_hop_prb_present = true;
out.res_to_add_mod_list[i].second_hop_prb = 50;
out.res_to_add_mod_list[i].format.set_format2().nrof_prbs = 1;
out.res_to_add_mod_list[i].format.format2().nrof_symbols = 2;
@ -622,6 +635,7 @@ void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg
j2++;
} else {
out.res_to_add_mod_list[i].start_prb = 50;
out.res_to_add_mod_list[i].second_hop_prb_present = true;
out.res_to_add_mod_list[i].second_hop_prb = 1;
out.res_to_add_mod_list[i].format.set_format2().nrof_prbs = 1;
out.res_to_add_mod_list[i].format.format2().nrof_symbols = 2;
@ -637,6 +651,9 @@ void fill_pucch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pucch_cfg
out.format2.set_setup();
out.format2.setup().max_code_rate_present = true;
out.format2.setup().max_code_rate = pucch_max_code_rate_opts::zero_dot25;
// NOTE: IMPORTANT!! The gNB expects the CSI to be reported along with HARQ-ACK
// If simul_harq_ack_csi_present = false, PUCCH might not be decoded properly when CSI is reported
out.format2.setup().simul_harq_ack_csi_present = true;
// SR resources
out.sched_request_res_to_add_mod_list.resize(1);
@ -865,18 +882,15 @@ int fill_serv_cell_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, ser
serv_common.ss_pbch_block_pwr = cell_cfg.phy_cell.pdsch.rs_power;
serv_common.n_timing_advance_offset_present = true;
serv_common.n_timing_advance_offset = asn1::rrc_nr::serving_cell_cfg_common_s::n_timing_advance_offset_opts::n0;
serv_common.n_timing_advance_offset = serving_cell_cfg_common_s::n_timing_advance_offset_opts::n0;
serv_common.n_timing_advance_offset_present = true;
serv_common.dmrs_type_a_position = asn1::rrc_nr::serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2;
serv_common.dmrs_type_a_position = serving_cell_cfg_common_s::dmrs_type_a_position_opts::pos2;
serv_common.pci_present = true;
serv_common.pci = cell_cfg.phy_cell.carrier.pci;
serv_common.ssb_periodicity_serving_cell_present = true;
if (not asn1::number_to_enum(serv_common.ssb_periodicity_serving_cell, cell_cfg.ssb_cfg.periodicity_ms)) {
get_logger(cfg).error("Config Error: Invalid SSB periodicity = %d\n", cell_cfg.ssb_cfg.periodicity_ms);
return SRSRAN_ERROR;
}
serv_common.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_s::ssb_periodicity_serving_cell_opts::ms10;
// Fill SSB config
serv_common.ssb_positions_in_burst_present = true;
@ -897,13 +911,7 @@ int fill_serv_cell_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, ser
if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) {
// TDD UL-DL config
serv_common.tdd_ul_dl_cfg_common_present = true;
auto& tdd_config = serv_common.tdd_ul_dl_cfg_common;
tdd_config.ref_subcarrier_spacing = subcarrier_spacing_e::khz15;
tdd_config.pattern1.dl_ul_tx_periodicity = asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::ms10;
tdd_config.pattern1.nrof_dl_slots = 6;
tdd_config.pattern1.nrof_dl_symbols = 0;
tdd_config.pattern1.nrof_ul_slots = 4;
tdd_config.pattern1.nrof_ul_symbols = 0;
fill_tdd_ul_dl_config_common(cfg.cell_list[cc], serv_common.tdd_ul_dl_cfg_common);
}
serv_common.ul_cfg_common_present = true;
@ -1057,9 +1065,6 @@ int fill_master_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1
fill_sp_cell_cfg_from_enb_cfg(cfg, cc, out.sp_cell_cfg);
out.sp_cell_cfg.recfg_with_sync_present = false;
// The current CSI config make the PUCCH decoding fail. We temporarily disable it until further investigation
out.sp_cell_cfg.sp_cell_cfg_ded.csi_meas_cfg_present = false;
return SRSRAN_SUCCESS;
}
@ -1139,12 +1144,12 @@ void fill_dl_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, dl_cfg_common_sib
cfg.freq_info_dl.freq_band_list.resize(1);
cfg.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present = true;
cfg.freq_info_dl.freq_band_list[0].freq_band_ind_nr = cell_cfg.band;
double ssb_freq_start = cell_cfg.ssb_cfg.ssb_freq_hz - SRSRAN_SSB_BW_SUBC * scs_hz / 2;
double ssb_freq_start = cell_cfg.ssb_freq_hz - SRSRAN_SSB_BW_SUBC * scs_hz / 2;
double offset_point_a_hz = ssb_freq_start - band_helper.nr_arfcn_to_freq(cell_cfg.dl_absolute_freq_point_a);
uint32_t offset_point_a_prbs = offset_point_a_hz / prb_bw;
cfg.freq_info_dl.offset_to_point_a = offset_point_a_prbs;
cfg.freq_info_dl.scs_specific_carrier_list.resize(1);
cfg.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = 0;
cfg.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier;
cfg.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing =
(subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs;
cfg.freq_info_dl.scs_specific_carrier_list[0].carrier_bw = cell_cfg.phy_cell.carrier.nof_prb;
@ -1174,7 +1179,7 @@ void fill_ul_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, ul_cfg_common_sib
band_helper.get_abs_freq_point_a_arfcn(cell_cfg.phy_cell.carrier.nof_prb, cell_cfg.ul_arfcn);
cfg.freq_info_ul.scs_specific_carrier_list.resize(1);
cfg.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = 0;
cfg.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier;
cfg.freq_info_ul.scs_specific_carrier_list[0].subcarrier_spacing =
(subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs;
cfg.freq_info_ul.scs_specific_carrier_list[0].carrier_bw = cell_cfg.phy_cell.carrier.nof_prb;
@ -1210,7 +1215,7 @@ void fill_ul_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, ul_cfg_common_sib
cfg.time_align_timer_common.value = time_align_timer_opts::infinity;
}
void fill_serv_cell_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, serving_cell_cfg_common_sib_s& cfg)
int fill_serv_cell_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, serving_cell_cfg_common_sib_s& cfg)
{
fill_dl_cfg_common_sib(cell_cfg, cfg.dl_cfg_common);
@ -1219,9 +1224,21 @@ void fill_serv_cell_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, serving_ce
cfg.ssb_positions_in_burst.in_one_group.from_number(0x80);
cfg.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::ms20;
cfg.ssb_periodicity_serving_cell.value = serving_cell_cfg_common_sib_s::ssb_periodicity_serving_cell_opts::ms10;
// The time advance offset is not supported by the current PHY
cfg.n_timing_advance_offset_present = true;
cfg.n_timing_advance_offset = serving_cell_cfg_common_sib_s::n_timing_advance_offset_opts::n0;
// TDD UL-DL config
if (cell_cfg.duplex_mode == SRSRAN_DUPLEX_MODE_TDD) {
cfg.tdd_ul_dl_cfg_common_present = true;
fill_tdd_ul_dl_config_common(cell_cfg, cfg.tdd_ul_dl_cfg_common);
}
cfg.ss_pbch_block_pwr = cell_cfg.phy_cell.pdsch.rs_power;
return SRSRAN_SUCCESS;
}
int fill_sib1_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::sib1_s& sib1)
@ -1266,7 +1283,7 @@ int fill_sib1_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1::rrc_nr::s
// sib1.si_sched_info.sched_info_list[0].sib_map_info[0].value_tag = 0;
sib1.serving_cell_cfg_common_present = true;
fill_serv_cell_cfg_common_sib(cell_cfg, sib1.serving_cell_cfg_common);
HANDLE_ERROR(fill_serv_cell_cfg_common_sib(cell_cfg, sib1.serving_cell_cfg_common));
sib1.ue_timers_and_consts_present = true;
sib1.ue_timers_and_consts.t300.value = ue_timers_and_consts_s::t300_opts::ms1000;

@ -23,6 +23,7 @@
#include "srsenb/hdr/common/common_enb.h"
#include "srsgnb/hdr/stack/rrc/cell_asn1_config.h"
#include "srsgnb/hdr/stack/rrc/rrc_nr_config_utils.h"
#include "srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h"
#include "srsgnb/hdr/stack/rrc/rrc_nr_ue.h"
#include "srsgnb/src/stack/mac/test/sched_nr_cfg_generators.h"
#include "srsran/asn1/rrc_nr_utils.h"
@ -77,6 +78,11 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_,
cell.ssb_absolute_freq_point);
}
du_cfg = std::make_unique<du_config_manager>(cfg);
for (uint32_t i = 0; i < cfg.cell_list.size(); ++i) {
du_cfg->add_cell();
}
// Generate cell config structs
cell_ctxt.reset(new cell_ctxt_t{});
if (cfg.is_standalone) {
@ -98,13 +104,13 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_,
int ret = fill_sp_cell_cfg_from_enb_cfg(cfg, UE_PSCELL_CC_IDX, base_sp_cell_cfg);
srsran_assert(ret == SRSRAN_SUCCESS, "Failed to configure cell");
pdcch_cfg_common_s* asn1_pdcch;
const pdcch_cfg_common_s* asn1_pdcch;
if (not cfg.is_standalone) {
// Fill rrc_nr_cfg with UE-specific search spaces and coresets
asn1_pdcch =
&base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup();
} else {
asn1_pdcch = &cell_ctxt->sib1.serving_cell_cfg_common.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup();
asn1_pdcch = &du_cfg->cell(0).serv_cell_cfg_common().dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup();
}
srsran_assert(check_nr_phy_cell_cfg_valid(cfg.cell_list[0].phy_cell) == SRSRAN_SUCCESS, "Invalid PhyCell Config");
@ -293,7 +299,13 @@ void rrc_nr::config_phy()
common_cfg.pdcch = cfg.cell_list[0].phy_cell.pdcch;
common_cfg.prach = cfg.cell_list[0].phy_cell.prach;
common_cfg.duplex_mode = cfg.cell_list[0].duplex_mode;
common_cfg.ssb = cfg.cell_list[0].ssb_cfg;
common_cfg.ssb = {};
common_cfg.ssb.center_freq_hz = cfg.cell_list[0].phy_cell.dl_freq_hz;
common_cfg.ssb.ssb_freq_hz = cfg.cell_list[0].ssb_freq_hz;
common_cfg.ssb.scs = cfg.cell_list[0].ssb_scs;
common_cfg.ssb.pattern = cfg.cell_list[0].ssb_pattern;
common_cfg.ssb.duplex_mode = cfg.cell_list[0].duplex_mode;
common_cfg.ssb.periodicity_ms = du_cfg->cell(0).serv_cell_cfg_common().ssb_periodicity_serving_cell.to_number();
if (phy->set_common_cfg(common_cfg) < SRSRAN_SUCCESS) {
logger.error("Couldn't set common PHY config");
return;
@ -302,54 +314,48 @@ void rrc_nr::config_phy()
void rrc_nr::config_mac()
{
uint32_t cc = 0;
// Fill MAC scheduler configuration for SIBs
// TODO: use parsed cell NR cfg configuration
srsran::phy_cfg_nr_default_t::reference_cfg_t ref_args{};
ref_args.duplex = cfg.cell_list[0].duplex_mode == SRSRAN_DUPLEX_MODE_TDD
ref_args.duplex = cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD
? srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_TDD_CUSTOM_6_4
: srsran::phy_cfg_nr_default_t::reference_cfg_t::R_DUPLEX_FDD;
std::vector<sched_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
cell.bwps[0].pdcch = cfg.cell_list[0].phy_cell.pdcch;
cell.pci = cfg.cell_list[0].phy_cell.carrier.pci;
cell.nof_layers = cfg.cell_list[0].phy_cell.carrier.max_mimo_layers;
cell.dl_cell_nof_prb = cfg.cell_list[0].phy_cell.carrier.nof_prb;
cell.ul_cell_nof_prb = cfg.cell_list[0].phy_cell.carrier.nof_prb;
cell.dl_center_frequency_hz = cfg.cell_list[0].phy_cell.carrier.dl_center_frequency_hz;
cell.ul_center_frequency_hz = cfg.cell_list[0].phy_cell.carrier.ul_center_frequency_hz;
cell.ssb_center_freq_hz = cfg.cell_list[0].phy_cell.carrier.ssb_center_freq_hz;
cell.offset_to_carrier = cfg.cell_list[0].phy_cell.carrier.offset_to_carrier;
cell.scs = cfg.cell_list[0].phy_cell.carrier.scs;
cell.bwps[0].pdcch = cfg.cell_list[cc].phy_cell.pdcch;
cell.pci = cfg.cell_list[cc].phy_cell.carrier.pci;
cell.nof_layers = cfg.cell_list[cc].phy_cell.carrier.max_mimo_layers;
cell.dl_cell_nof_prb = cfg.cell_list[cc].phy_cell.carrier.nof_prb;
cell.ul_cell_nof_prb = cfg.cell_list[cc].phy_cell.carrier.nof_prb;
cell.dl_center_frequency_hz = cfg.cell_list[cc].phy_cell.carrier.dl_center_frequency_hz;
cell.ul_center_frequency_hz = cfg.cell_list[cc].phy_cell.carrier.ul_center_frequency_hz;
cell.ssb_center_freq_hz = cfg.cell_list[cc].phy_cell.carrier.ssb_center_freq_hz;
cell.dmrs_type_a_position = du_cfg->cell(cc).mib.dmrs_type_a_position;
cell.pdcch_cfg_sib1 = du_cfg->cell(cc).mib.pdcch_cfg_sib1;
if (du_cfg->cell(cc).serv_cell_cfg_common().tdd_ul_dl_cfg_common_present) {
cell.tdd_ul_dl_cfg_common.emplace(du_cfg->cell(cc).serv_cell_cfg_common().tdd_ul_dl_cfg_common);
}
cell.dl_cfg_common = du_cfg->cell(cc).serv_cell_cfg_common().dl_cfg_common;
cell.ul_cfg_common = du_cfg->cell(cc).serv_cell_cfg_common().ul_cfg_common;
cell.ss_pbch_block_power = du_cfg->cell(cc).serv_cell_cfg_common().ss_pbch_block_pwr;
if (not cfg.is_standalone) {
const serving_cell_cfg_common_s& serv_cell = base_sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common;
// Derive cell config from ASN1
bool valid_cfg = srsran::make_pdsch_cfg_from_serv_cell(base_sp_cell_cfg.sp_cell_cfg_ded, &cell.bwps[0].pdsch);
srsran_assert(valid_cfg, "Invalid NR cell configuration.");
if (serv_cell.tdd_ul_dl_cfg_common_present) {
cell.tdd_ul_dl_cfg_common.emplace(serv_cell.tdd_ul_dl_cfg_common);
}
cell.ssb_positions_in_burst.in_one_group.set(0, true);
cell.ssb_periodicity_ms = serv_cell.ssb_periodicity_serving_cell.to_number();
cell.ssb_scs = serv_cell.ssb_subcarrier_spacing;
cell.ss_pbch_block_power = serv_cell.ss_pbch_block_pwr;
} 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 = {};
bzero(cell.bwps[0].pdsch.nzp_csi_rs_sets, sizeof(cell.bwps[0].pdsch.nzp_csi_rs_sets));
cell.dl_cfg_common.reset(new dl_cfg_common_sib_s{serv_cell.dl_cfg_common});
cell.ul_cfg_common.reset(new ul_cfg_common_sib_s{serv_cell.ul_cfg_common});
if (serv_cell.tdd_ul_dl_cfg_common_present) {
cell.tdd_ul_dl_cfg_common.emplace(serv_cell.tdd_ul_dl_cfg_common);
}
cell.ssb_positions_in_burst = serv_cell.ssb_positions_in_burst;
cell.ssb_periodicity_ms = serv_cell.ssb_periodicity_serving_cell.to_number();
cell.ssb_positions_in_burst = du_cfg->cell(cc).serv_cell_cfg_common().ssb_positions_in_burst;
cell.ssb_periodicity_ms = du_cfg->cell(cc).serv_cell_cfg_common().ssb_periodicity_serving_cell.to_number();
cell.ssb_scs.value = (subcarrier_spacing_e::options)cfg.cell_list[0].phy_cell.carrier.scs;
cell.ss_pbch_block_power = serv_cell.ss_pbch_block_pwr;
}
cell.dmrs_type_a_position = cell_ctxt->mib.dmrs_type_a_position;
cell.pdcch_cfg_sib1 = cell_ctxt->mib.pdcch_cfg_sib1;
// Set SIB1 and SI messages
cell.sibs.resize(cell_ctxt->sib_buffer.size());
@ -373,26 +379,6 @@ void rrc_nr::config_mac()
int32_t rrc_nr::generate_sibs()
{
// MIB packing
fill_mib_from_enb_cfg(cfg.cell_list[0], cell_ctxt->mib);
bcch_bch_msg_s mib_msg;
mib_msg.msg.set_mib() = cell_ctxt->mib;
{
srsran::unique_byte_buffer_t mib_buf = srsran::make_byte_buffer();
if (mib_buf == nullptr) {
logger.error("Couldn't allocate PDU in %s().", __FUNCTION__);
return SRSRAN_ERROR;
}
asn1::bit_ref bref(mib_buf->msg, mib_buf->get_tailroom());
if (mib_msg.pack(bref) != asn1::SRSASN_SUCCESS) {
logger.error("Couldn't pack mib msg");
return SRSRAN_ERROR;
}
mib_buf->N_bytes = bref.distance_bytes();
logger.debug(mib_buf->msg, mib_buf->N_bytes, "MIB payload (%d B)", mib_buf->N_bytes);
cell_ctxt->mib_buffer = std::move(mib_buf);
}
if (not cfg.is_standalone) {
return SRSRAN_SUCCESS;
}

@ -117,7 +117,7 @@ int derive_coreset0_params(rrc_cell_cfg_nr_t& cell)
(ssb_abs_freq_Hz > pointA_abs_freq_Hz) ? (uint32_t)(ssb_abs_freq_Hz - pointA_abs_freq_Hz) : 0;
int ret = srsran_coreset_zero(cell.phy_cell.carrier.pci,
ssb_pointA_freq_offset_Hz,
cell.ssb_cfg.scs,
cell.ssb_scs,
cell.phy_cell.carrier.scs,
cell.coreset0_idx,
&cell.phy_cell.pdcch.coreset[0]);
@ -131,7 +131,7 @@ int derive_ssb_params(bool is_sa,
srsran_subcarrier_spacing_t pdcch_scs,
uint32_t coreset0_idx,
uint32_t nof_prb,
srsran_ssb_cfg_t& ssb)
rrc_cell_cfg_nr_t& cell)
{
// Verify essential parameters are specified and valid
ERROR_IF_NOT(dl_arfcn > 0, "Invalid DL ARFCN=%d", dl_arfcn);
@ -145,20 +145,17 @@ int derive_ssb_params(bool is_sa,
double dl_freq_hz = band_helper.nr_arfcn_to_freq(dl_arfcn);
uint32_t dl_absolute_freq_point_a = band_helper.get_abs_freq_point_a_arfcn(nof_prb, dl_arfcn);
ssb.center_freq_hz = dl_freq_hz;
ssb.duplex_mode = band_helper.get_duplex_mode(band);
// derive SSB pattern and scs
ssb.pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz);
if (ssb.pattern == SRSRAN_SSB_PATTERN_A) {
cell.ssb_pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz);
if (cell.ssb_pattern == SRSRAN_SSB_PATTERN_A) {
// 15kHz SSB SCS
ssb.scs = srsran_subcarrier_spacing_15kHz;
cell.ssb_scs = srsran_subcarrier_spacing_15kHz;
} else {
// try to optain SSB pattern for same band with 30kHz SCS
ssb.pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_30kHz);
if (ssb.pattern == SRSRAN_SSB_PATTERN_B || ssb.pattern == SRSRAN_SSB_PATTERN_C) {
cell.ssb_pattern = band_helper.get_ssb_pattern(band, srsran_subcarrier_spacing_30kHz);
if (cell.ssb_pattern == SRSRAN_SSB_PATTERN_B || cell.ssb_pattern == SRSRAN_SSB_PATTERN_C) {
// SSB SCS is 30 kHz
ssb.scs = srsran_subcarrier_spacing_30kHz;
cell.ssb_scs = srsran_subcarrier_spacing_30kHz;
} else {
srsran_terminate("Can't derive SSB pattern from band %d", band);
}
@ -168,29 +165,20 @@ int derive_ssb_params(bool is_sa,
int coreset0_rb_offset = 0;
if (is_sa) {
// Get offset in RBs between CORESET#0 and SSB
coreset0_rb_offset = srsran_coreset0_ssb_offset(coreset0_idx, ssb.scs, pdcch_scs);
coreset0_rb_offset = srsran_coreset0_ssb_offset(coreset0_idx, cell.ssb_scs, pdcch_scs);
ERROR_IF_NOT(coreset0_rb_offset >= 0, "Failed to compute RB offset between CORESET#0 and SSB");
} else {
// TODO: Verify if specified SSB frequency is valid
}
uint32_t ssb_abs_freq_point =
band_helper.get_abs_freq_ssb_arfcn(band, ssb.scs, dl_absolute_freq_point_a, coreset0_rb_offset);
band_helper.get_abs_freq_ssb_arfcn(band, cell.ssb_scs, dl_absolute_freq_point_a, coreset0_rb_offset);
ERROR_IF_NOT(ssb_abs_freq_point > 0,
"Can't derive SSB freq point for dl_arfcn=%d and band %d",
band_helper.freq_to_nr_arfcn(dl_freq_hz),
band);
// Convert to frequency for PHY
ssb.ssb_freq_hz = band_helper.nr_arfcn_to_freq(ssb_abs_freq_point);
ssb.periodicity_ms = 10; // TODO: make a param
ssb.beta_pss = 0.0;
ssb.beta_sss = 0.0;
ssb.beta_pbch = 0.0;
ssb.beta_pbch_dmrs = 0.0;
// set by PHY layer in worker_pool::set_common_cfg
ssb.srate_hz = 0.0;
ssb.scaling = 0.0;
cell.ssb_freq_hz = band_helper.nr_arfcn_to_freq(ssb_abs_freq_point);
return SRSRAN_SUCCESS;
}
@ -259,9 +247,9 @@ int set_derived_nr_cell_params(bool is_sa, rrc_cell_cfg_nr_t& cell)
cell.phy_cell.carrier.scs,
cell.coreset0_idx,
cell.phy_cell.carrier.nof_prb,
cell.ssb_cfg);
cell.phy_cell.carrier.ssb_center_freq_hz = cell.ssb_cfg.ssb_freq_hz;
cell.ssb_absolute_freq_point = band_helper.freq_to_nr_arfcn(cell.ssb_cfg.ssb_freq_hz);
cell);
cell.phy_cell.carrier.ssb_center_freq_hz = cell.ssb_freq_hz;
cell.ssb_absolute_freq_point = band_helper.freq_to_nr_arfcn(cell.ssb_freq_hz);
// Derive remaining config params
if (is_sa) {
@ -346,7 +334,6 @@ int set_derived_nr_rrc_params(rrc_nr_cfg_t& rrc_cfg)
int check_nr_cell_cfg_valid(const rrc_cell_cfg_nr_t& cell, bool is_sa)
{
// verify SSB params are consistent
ERROR_IF_NOT(cell.ssb_cfg.center_freq_hz == cell.phy_cell.dl_freq_hz, "Invalid SSB param generation");
HANDLE_ERROR(check_nr_phy_cell_cfg_valid(cell.phy_cell));
if (is_sa) {

@ -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_int.data(), 32, "NR RRC Integrity Key (k_nr_rrc_int)");
logger.info(sec_cfg.k_nr_up_enc.data(), 32, "NR UP Encryption Key (k_nr_up_enc)");
logger.info(sec_cfg.k_nr_up_int.data(), 32, "NR UP Encryption Key (k_nr_up_enc)");
logger.info(sec_cfg.k_nr_up_int.data(), 32, "NR UP Integrity Key (k_nr_up_int)");
}
void nr_security_context::regenerate_keys_handover(uint32_t new_pci, uint32_t new_dl_arfcn)

@ -1117,20 +1117,8 @@ void rrc_nr::ue::handle_rrc_setup_complete(const asn1::rrc_nr::rrc_setup_complet
/// TS 38.331, SecurityModeCommand
void rrc_nr::ue::send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu)
{
// FIXME: Currently we are using the PDCP-LTE, so we need to convert from nr_as_security_cfg to as_security_config.
// When we start using PDCP-NR we can avoid this step.
srsran::nr_as_security_config_t tmp_cnfg = sec_ctx.get_as_sec_cfg();
srsran::as_security_config_t pdcp_cnfg = {};
pdcp_cnfg.k_rrc_int = tmp_cnfg.k_nr_rrc_int;
pdcp_cnfg.k_rrc_enc = tmp_cnfg.k_nr_rrc_enc;
pdcp_cnfg.k_up_int = tmp_cnfg.k_nr_up_int;
pdcp_cnfg.k_up_enc = tmp_cnfg.k_nr_up_enc;
pdcp_cnfg.integ_algo = (srsran::INTEGRITY_ALGORITHM_ID_ENUM)tmp_cnfg.integ_algo;
pdcp_cnfg.cipher_algo = (srsran::CIPHERING_ALGORITHM_ID_ENUM)tmp_cnfg.cipher_algo;
// Setup SRB1 security/integrity. Encryption is set on completion
parent->pdcp->config_security(rnti, srb_to_lcid(srsran::nr_srb::srb1), pdcp_cnfg);
parent->pdcp->enable_integrity(rnti, srb_to_lcid(srsran::nr_srb::srb1));
// apply selected security config and enable integrity on SRB1 before generating security mode command
update_as_security(srb_to_lcid(srsran::nr_srb::srb1), true, false);
if (nas_pdu != nullptr) {
nas_pdu_queue.push_back(std::move(nas_pdu));
@ -1148,11 +1136,58 @@ void rrc_nr::ue::send_security_mode_command(srsran::unique_byte_buffer_t nas_pdu
}
}
/**
* @brief Internal helper to update the security configuration of a PDCP bearer
*
* If no valid AS security config is present (yet) the method doesn't modify the
* PDCP config and returns SRSRAN_ERROR. In some cases, however,
* for example during RRC Setup, this is in fact the expected behaviour as
* AS security isn't established yet.
*
* @param lcid Logical channel ID of the bearer
* @param enable_integrity Whether to enable integrity protection for the bearer
* @param enable_ciphering Whether to enable ciphering for the bearer
* @return int SRSRAN_SUCCESS if a valid AS security config was found and the security was configured
*/
int rrc_nr::ue::update_as_security(uint32_t lcid, bool enable_integrity = true, bool enable_ciphering = true)
{
if (not sec_ctx.is_as_sec_cfg_valid()) {
parent->logger.error("Invalid AS security configuration. Skipping configuration for lcid=%d", lcid);
return SRSRAN_ERROR;
}
// FIXME: Currently we are using the PDCP-LTE, so we need to convert from nr_as_security_cfg to as_security_config.
// When we start using PDCP-NR we can avoid this step.
srsran::nr_as_security_config_t tmp_cnfg = sec_ctx.get_as_sec_cfg();
srsran::as_security_config_t pdcp_cnfg = {};
pdcp_cnfg.k_rrc_int = tmp_cnfg.k_nr_rrc_int;
pdcp_cnfg.k_rrc_enc = tmp_cnfg.k_nr_rrc_enc;
pdcp_cnfg.k_up_int = tmp_cnfg.k_nr_up_int;
pdcp_cnfg.k_up_enc = tmp_cnfg.k_nr_up_enc;
pdcp_cnfg.integ_algo = (srsran::INTEGRITY_ALGORITHM_ID_ENUM)tmp_cnfg.integ_algo;
pdcp_cnfg.cipher_algo = (srsran::CIPHERING_ALGORITHM_ID_ENUM)tmp_cnfg.cipher_algo;
// configure algorithm and keys
parent->pdcp->config_security(rnti, lcid, pdcp_cnfg);
if (enable_integrity) {
parent->pdcp->enable_integrity(rnti, lcid);
}
if (enable_ciphering) {
parent->pdcp->enable_encryption(rnti, lcid);
}
return SRSRAN_SUCCESS;
}
/// TS 38.331, SecurityModeComplete
void rrc_nr::ue::handle_security_mode_complete(const asn1::rrc_nr::security_mode_complete_s& msg)
{
parent->logger.info("SecurityModeComplete transaction ID: %d", msg.rrc_transaction_id);
parent->pdcp->enable_encryption(rnti, srb_to_lcid(srsran::nr_srb::srb1));
// finally, also enable ciphering on SRB1
update_as_security(srb_to_lcid(srsran::nr_srb::srb1), false, true);
send_rrc_reconfiguration();
// Note: Skip UE capabilities
@ -1366,20 +1401,8 @@ int rrc_nr::ue::update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radi
}
parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg);
// enable security config
if (sec_ctx.is_as_sec_cfg_valid()) {
srsran::nr_as_security_config_t tmp_cnfg = sec_ctx.get_as_sec_cfg();
srsran::as_security_config_t pdcp_cnfg = {};
pdcp_cnfg.k_rrc_int = tmp_cnfg.k_nr_rrc_int;
pdcp_cnfg.k_rrc_enc = tmp_cnfg.k_nr_rrc_enc;
pdcp_cnfg.k_up_int = tmp_cnfg.k_nr_up_int;
pdcp_cnfg.k_up_enc = tmp_cnfg.k_nr_up_enc;
pdcp_cnfg.integ_algo = (srsran::INTEGRITY_ALGORITHM_ID_ENUM)tmp_cnfg.integ_algo;
pdcp_cnfg.cipher_algo = (srsran::CIPHERING_ALGORITHM_ID_ENUM)tmp_cnfg.cipher_algo;
// Setup SRB1 security/integrity. Encryption is set on completion
parent->pdcp->config_security(rnti, srb_to_lcid(srsran::nr_srb::srb1), pdcp_cnfg);
parent->pdcp->enable_integrity(rnti, srb_to_lcid(srsran::nr_srb::srb1));
update_as_security(rlc_bearer->lc_ch_id);
}
}
@ -1399,6 +1422,10 @@ int rrc_nr::ue::update_pdcp_bearers(const asn1::rrc_nr::radio_bearer_cfg_s& radi
return SRSRAN_ERROR;
}
parent->pdcp->add_bearer(rnti, rlc_bearer->lc_ch_id, pdcp_cnfg);
if (sec_ctx.is_as_sec_cfg_valid()) {
update_as_security(rlc_bearer->lc_ch_id);
}
}
return SRSRAN_SUCCESS;
@ -1449,6 +1476,14 @@ int rrc_nr::ue::update_mac(const cell_group_cfg_s& cell_group_config, bool is_co
// TODO: remaining fields
}
}
if (cell_group_config.sp_cell_cfg_present and cell_group_config.sp_cell_cfg.sp_cell_cfg_ded_present and
cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg_present and
cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp_present and
cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg_present) {
auto& pucch_cfg = cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.ul_cfg.init_ul_bwp.pucch_cfg.setup();
srsran::fill_phy_pucch_cfg(pucch_cfg, &uecfg.phy_cfg.pucch);
}
} else {
auto& pdcch = cell_group_config.sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup();
for (auto& ss : pdcch.search_spaces_to_add_mod_list) {
@ -1466,6 +1501,7 @@ int rrc_nr::ue::update_mac(const cell_group_cfg_s& cell_group_config, bool is_co
uecfg.sp_cell_cfg.reset(new sp_cell_cfg_s{cell_group_cfg.sp_cell_cfg});
uecfg.mac_cell_group_cfg.reset(new mac_cell_group_cfg_s{cell_group_cfg.mac_cell_group_cfg});
uecfg.phy_cell_group_cfg.reset(new phys_cell_group_cfg_s{cell_group_cfg.phys_cell_group_cfg});
srsran::make_csi_cfg_from_serv_cell(cell_group_config.sp_cell_cfg.sp_cell_cfg_ded, &uecfg.phy_cfg.csi);
parent->mac->ue_cfg(rnti, uecfg);
return SRSRAN_SUCCESS;

@ -179,6 +179,7 @@ void test_rrc_sa_connection()
SRSRAN_SUCCESS);
TESTASSERT_SUCCESS(rrc_obj.add_user(0x4601, 0));
TESTASSERT_SUCCESS(rrc_obj.ue_set_security_cfg_key(0x4601, {}));
test_rrc_nr_connection_establishment(task_sched, rrc_obj, rlc_obj, mac_obj, ngap_obj, 0x4601);
test_rrc_nr_info_transfer(task_sched, rrc_obj, pdcp_obj, ngap_obj, 0x4601);

@ -62,7 +62,7 @@ public:
void meas_stop() final {}
/* Cell search and selection procedures */
bool cell_search() final { return false; }
bool cell_search(int earfcn) final { return false; }
bool cell_select(phy_cell_t cell) final { return false; }
bool cell_is_camping() final { return false; }

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

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

@ -77,7 +77,7 @@ public:
// RRC interface for controling the SYNC state
bool cell_search_init();
rrc_interface_phy_lte::cell_search_ret_t cell_search_start(phy_cell_t* cell);
rrc_interface_phy_lte::cell_search_ret_t cell_search_start(phy_cell_t* cell, int earfcn);
bool cell_select_init(phy_cell_t cell);
bool cell_select_start(phy_cell_t cell);
bool cell_is_camping();

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

@ -220,6 +220,9 @@ private:
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;
asn1::rrc::reest_cause_e m_reest_cause = asn1::rrc::reest_cause_e::nulltype;
uint16_t m_reest_rnti = 0;
@ -337,7 +340,8 @@ private:
bool con_reconfig_ho(const asn1::rrc::rrc_conn_recfg_s& reconfig);
void ho_failed();
void start_go_idle();
void rrc_connection_release(const std::string& cause);
void handle_rrc_connection_release(const asn1::rrc::rrc_conn_release_s& release);
void start_rrc_redirect(uint32_t new_dl_earfcn);
void radio_link_failure_push_cmd();
void radio_link_failure_process();
void leave_connected();

@ -24,6 +24,7 @@
#include "../rrc/rrc_cell.h"
#include "rrc_nr_config.h"
#include "rrc_nr_metrics.h"
#include "srsran/adt/circular_map.h"
#include "srsran/asn1/rrc_nr.h"
#include "srsran/asn1/rrc_nr_utils.h"
@ -44,8 +45,6 @@ class usim_interface_rrc_nr;
class pdcp_interface_rrc;
class rlc_interface_rrc;
struct rrc_nr_metrics_t {};
class rrc_nr final : public rrc_interface_phy_nr,
public rrc_interface_pdcp,
public rrc_interface_rlc,
@ -146,6 +145,7 @@ private:
// senders
void send_setup_request(srsran::nr_establishment_cause_t cause);
void send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg);
void send_rrc_reconfig_complete();
void send_ul_info_transfer(srsran::unique_byte_buffer_t nas_msg);
void send_ul_ccch_msg(const asn1::rrc_nr::ul_ccch_msg_s& msg);
void send_ul_dcch_msg(uint32_t lcid, const asn1::rrc_nr::ul_dcch_msg_s& msg);
@ -193,14 +193,6 @@ private:
uint32_t sim_measurement_carrier_freq_r15;
srsran::timer_handler::unique_timer sim_measurement_timer;
/// RRC states (3GPP 38.331 v15.5.1 Sec 4.2.1)
enum rrc_nr_state_t {
RRC_NR_STATE_IDLE = 0,
RRC_NR_STATE_CONNECTED,
RRC_NR_STATE_CONNECTED_INACTIVE,
RRC_NR_STATE_N_ITEMS,
};
const static char* rrc_nr_state_text[RRC_NR_STATE_N_ITEMS];
rrc_nr_state_t state = RRC_NR_STATE_IDLE;
uint8_t transaction_id = 0;

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

@ -67,6 +67,8 @@ public:
// Stack+RRC interface
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);
@ -89,7 +91,7 @@ private:
gw_interface_nas* gw = nullptr;
bool running = false;
bool has_sec_ctxt = false;
bool initial_sec_command = false;
srsran::nas_5g::mobile_identity_5gs_t::guti_5g_s guti_5g;
@ -102,6 +104,8 @@ private:
bool ia5g_caps[8] = {};
bool ea5g_caps[8] = {};
void set_k_gnb_count(uint32_t count);
// TS 23.003 Sec. 6.2.2 IMEISV's last two octets are Software Version Number (SVN)
// which identifies the software version number of the mobile equipment
const uint8_t ue_svn_oct1 = 0x5;

@ -69,6 +69,7 @@ protected:
struct nas_5g_sec_ctxt {
uint8_t ksi;
uint8_t k_amf[32];
uint32_t k_gnb_count;
};
nas_sec_base_ctxt ctxt_base = {};

@ -106,6 +106,7 @@ public:
void restore_keys_from_failed_ho(srsran::as_security_config_t* as_ctx) final;
// NR RRC interface
void generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg) final;
bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) final;
bool update_nr_context(srsran::as_security_config_t* sec_cfg) final;
@ -149,6 +150,8 @@ protected:
uint8_t k_enb_initial[KEY_LEN] = {};
uint8_t auts[AKA_AUTS_LEN] = {};
srsran::as_key_t k_gnb_initial = {};
// Current K_eNB context (K_eNB, NH and NCC)
srsran::k_enb_context_t k_enb_ctx = {};
srsran::k_gnb_context_t k_gnb_ctx = {};

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

@ -190,7 +190,7 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per
return;
}
if (metrics.stack.rrc.state != RRC_STATE_CONNECTED) {
if (metrics.stack.rrc.state != RRC_STATE_CONNECTED && metrics.stack.rrc_nr.state != RRC_NR_STATE_CONNECTED) {
fmt::print("--- disconnected ---\n");
return;
}
@ -202,25 +202,30 @@ void metrics_stdout::set_metrics(const ue_metrics_t& metrics, const uint32_t per
display_neighbours |= metrics.stack.rrc.neighbour_cells.size() > 0;
}
bool is_nr = metrics.phy_nr.nof_active_cc > 0;
bool has_lte = metrics.phy.nof_active_cc > 0;
bool has_nr = metrics.phy_nr.nof_active_cc > 0;
// print table header every 10 reports
if (++n_reports > 10) {
print_table(display_neighbours, is_nr);
print_table(display_neighbours, has_nr);
}
// also print table header if neighbours are added/removed in between
if (display_neighbours != table_has_neighbours) {
print_table(display_neighbours, is_nr);
print_table(display_neighbours, has_nr);
}
if (has_lte) {
for (uint32_t r = 0; r < metrics.phy.nof_active_cc; r++) {
set_metrics_helper(metrics.phy, metrics.stack.mac, metrics.stack.rrc, display_neighbours, r, false, !is_nr);
set_metrics_helper(metrics.phy, metrics.stack.mac, metrics.stack.rrc, display_neighbours, r, false, !has_nr);
}
}
if (has_nr) {
for (uint32_t r = 0; r < metrics.phy_nr.nof_active_cc; r++) {
// Assumption LTE is followed by the NR carriers.
set_metrics_helper(metrics.phy_nr, metrics.stack.mac_nr, metrics.stack.rrc, display_neighbours, r, true, !is_nr);
set_metrics_helper(metrics.phy_nr, metrics.stack.mac_nr, metrics.stack.rrc, display_neighbours, r, true, !has_nr);
}
}
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.
// When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH
// processing.
bool phy::cell_search()
// processing. If a valid EARFCN (>0) is given, this is used for cell search.
bool phy::cell_search(int earfcn)
{
sfsync.scell_sync_stop();
if (sfsync.cell_search_init()) {
cmd_worker_cell.add_cmd([this]() {
cmd_worker_cell.add_cmd([this, earfcn]() {
// Wait SYNC transitions to IDLE
sfsync.wait_idle();
@ -364,7 +364,7 @@ bool phy::cell_search()
reset();
phy_cell_t found_cell = {};
rrc_interface_phy_lte::cell_search_ret_t ret = sfsync.cell_search_start(&found_cell);
rrc_interface_phy_lte::cell_search_ret_t ret = sfsync.cell_search_start(&found_cell, earfcn);
stack->cell_search_complete(ret, found_cell);
});
} else {

@ -188,10 +188,10 @@ void sync::reset()
*
*/
/* A call to cell_search() finds the strongest cell in the set of supported EARFCNs. When the first cell is found,
* returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not found in the
* current frequency it moves to the next one and the next call to cell_search() will look in the next EARFCN in the
* set. If no cells are found in any frequency it returns 0. If error returns -1.
/* A call to cell_search() finds the strongest cell at a given EARFCN or in the set of supported EARFCNs. When the first
* cell is found, returns 1 and stores cell information and RSRP values in the pointers (if provided). If a cell is not
* found in the current frequency it moves to the next one and the next call to cell_search() will look in the next
* EARFCN in the set. If no cells are found in any frequency it returns 0. If error returns -1.
*
* The first part of the procedure (call to _init()) moves the PHY To IDLE, ensuring that no UL/DL/PRACH will happen
*
@ -206,7 +206,6 @@ bool sync::cell_search_init()
}
// Move state to IDLE
Info("Cell Search: Start EARFCN index=%u/%zd", cellsearch_earfcn_index, worker_com->args->dl_earfcn_list.size());
phy_state.go_idle();
// Stop all intra-frequency measurement before changing frequency
@ -217,10 +216,16 @@ bool sync::cell_search_init()
return true;
}
rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* found_cell)
rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* found_cell, int earfcn)
{
std::unique_lock<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 = {};
ret.found = rrc_interface_phy_lte::cell_search_ret_t::ERROR;
ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS;
@ -238,16 +243,20 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou
Info("SYNC: Setting Cell Search sampling rate");
}
if (earfcn < 0) {
try {
if (current_earfcn != (int)worker_com->args->dl_earfcn_list.at(cellsearch_earfcn_index)) {
current_earfcn = (int)worker_com->args->dl_earfcn_list[cellsearch_earfcn_index];
Info("Cell Search: changing frequency to EARFCN=%d", current_earfcn);
set_frequency();
}
} catch (const std::out_of_range& oor) {
Error("Index %d is not a valid EARFCN element.", cellsearch_earfcn_index);
return ret;
}
} else {
current_earfcn = earfcn;
}
Info("Cell Search: changing frequency to EARFCN=%d", current_earfcn);
set_frequency();
// Move to CELL SEARCH and wait to finish
Info("Cell Search: Setting Cell search state");
@ -275,7 +284,7 @@ rrc_interface_phy_lte::cell_search_ret_t sync::cell_search_start(phy_cell_t* fou
}
cellsearch_earfcn_index++;
if (cellsearch_earfcn_index >= worker_com->args->dl_earfcn_list.size()) {
if (cellsearch_earfcn_index >= worker_com->args->dl_earfcn_list.size() or earfcn < 0) {
Info("Cell Search: No more frequencies in the current EARFCN set");
cellsearch_earfcn_index = 0;
ret.last_freq = rrc_interface_phy_lte::cell_search_ret_t::NO_MORE_FREQS;

@ -224,6 +224,11 @@ void sync_sa::run_state_idle()
void sync_sa::run_state_cell_search()
{
// Initialise buffer
if (cell_search_nof_trials == 0) {
srsran_vec_cf_zero(rx_buffer, slot_sz);
}
// Receive samples
srsran::rf_buffer_t rf_buffer = {};
rf_buffer.set_nof_samples(slot_sz);

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

@ -175,13 +175,13 @@ void phy_controller::selecting_cell::wait_in_sync::enter(selecting_cell* f)
*************************************/
//! Searches for a cell in the current frequency and retrieves SIB1 if not retrieved yet
bool phy_controller::start_cell_search(srsran::event_observer<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>()) {
fsmInfo("Cell search already launched.");
return true;
}
trigger(cell_search_cmd{});
trigger(cell_search_cmd{earfcn});
if (not is_in_state<searching_cell>()) {
fsmWarning("Failed to launch cell search");
return false;
@ -195,10 +195,10 @@ void phy_controller::cell_search_completed(cell_search_ret_t cs_ret, phy_cell_t
trigger(cell_srch_res{cs_ret, found_cell});
}
void phy_controller::searching_cell::enter(phy_controller* f)
void phy_controller::searching_cell::enter(phy_controller* f, const cell_search_cmd& ev)
{
otherfsmInfo(f, "Initiating Cell search");
f->phy->cell_search();
f->phy->cell_search(ev.earfcn);
}
void phy_controller::handle_cell_search_res(searching_cell& s, const cell_srch_res& result)

@ -1162,8 +1162,9 @@ void rrc::handle_rrc_con_reconfig(uint32_t lcid, const rrc_conn_recfg_s& reconfi
}
/* Actions upon reception of RRCConnectionRelease 5.3.8.3 */
void rrc::rrc_connection_release(const std::string& cause)
void rrc::handle_rrc_connection_release(const asn1::rrc::rrc_conn_release_s& release)
{
std::string cause = release.crit_exts.c1().rrc_conn_release_r8().release_cause.to_string();
// Save idleModeMobilityControlInfo, etc.
srsran::console("Received RRC Connection Release (releaseCause: %s)\n", cause.c_str());
@ -1173,6 +1174,36 @@ void rrc::rrc_connection_release(const std::string& cause)
// delay actions by 60ms as per 5.3.8.3
task_sched.defer_callback(60, [this]() { start_go_idle(); });
uint32_t earfcn = 0;
if (release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info_present) {
switch (release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type()) {
case asn1::rrc::redirected_carrier_info_c::types_opts::options::eutra:
earfcn = release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.eutra();
break;
default:
srsran::console("Ignoring RedirectedCarrierInfo with unsupported type (%s)\n",
release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string());
break;
}
if (earfcn != 0) {
srsran::console("RedirectedCarrierInfo present (type %s, earfcn: %d) - Redirecting\n",
release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string(),
earfcn);
logger.info("RedirectedCarrierInfo present (type %s, earfcn: %d) - Redirecting",
release.crit_exts.c1().rrc_conn_release_r8().redirected_carrier_info.type().to_string(),
earfcn);
// delay actions by 60ms as per 5.3.8.3
task_sched.defer_callback(60, [this, earfcn]() { start_rrc_redirect(earfcn); });
}
}
}
void rrc::start_rrc_redirect(uint32_t new_dl_earfcn)
{
cell_search_earfcn = (int)new_dl_earfcn;
plmn_search();
}
/// TS 36.331, 5.3.12 - UE actions upon leaving RRC_CONNECTED
@ -1827,7 +1858,7 @@ void rrc::parse_dl_dcch(uint32_t lcid, unique_byte_buffer_t pdu)
handle_ue_capability_enquiry(c1->ue_cap_enquiry());
break;
case dl_dcch_msg_type_c::c1_c_::types::rrc_conn_release:
rrc_connection_release(c1->rrc_conn_release().crit_exts.c1().rrc_conn_release_r8().release_cause.to_string());
handle_rrc_connection_release(c1->rrc_conn_release());
break;
case dl_dcch_msg_type_c::c1_c_::types::ue_info_request_r9:
transaction_id = c1->ue_info_request_r9().rrc_transaction_id;

@ -80,6 +80,12 @@ void meas_cell_eutra::set_sib13(const asn1::rrc::sib_type13_r9_s& sib13_)
has_valid_sib13 = true;
}
void meas_cell_nr::set_sib1(const asn1::rrc_nr::sib1_s& sib1_)
{
sib1 = sib1_;
has_valid_sib1 = true;
}
bool meas_cell::is_sib_scheduled(uint32_t sib_index) const
{
return sib_info_map.find(sib_index) != sib_info_map.end();

@ -50,7 +50,7 @@ proc_outcome_t rrc::cell_search_proc::init()
{
Info("Starting...");
state = state_t::phy_cell_search;
if (not rrc_ptr->phy_ctrl->start_cell_search(rrc_ptr->cell_searcher)) {
if (not rrc_ptr->phy_ctrl->start_cell_search(rrc_ptr->cell_searcher, rrc_ptr->cell_search_earfcn)) {
Warning("Failed to initiate Cell Search.");
return proc_outcome_t::error;
}
@ -1265,11 +1265,14 @@ proc_outcome_t rrc::go_idle_proc::step()
void rrc::go_idle_proc::then(const srsran::proc_state_t& result)
{
// only start cell reselection if no RRC redirect is present (redirect will trigger a cell search)
if (rrc_ptr->cell_search_earfcn < 0) {
if (rrc_ptr->nas->is_registered() and not rrc_ptr->cell_reselector.launch()) {
rrc_ptr->logger.error("Failed to initiate a Cell Reselection procedure...");
return;
}
rrc_ptr->callback_list.add_proc(rrc_ptr->cell_reselector);
}
}
/**************************************
@ -1393,9 +1396,9 @@ proc_outcome_t rrc::connection_reest_proc::init(asn1::rrc::reest_cause_e cause)
reest_cellid = rrc_ptr->meas_cells.find_cell(reest_source_freq, reest_source_pci)->get_cell_id();
Info("Starting... cause: \"%s\", UE context: {C-RNTI=0x%x, PCI=%d, CELL ID=%d}",
reest_cause == asn1::rrc::reest_cause_opts::recfg_fail
? "Reconfiguration failure"
: cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure" : "Other failure",
reest_cause == asn1::rrc::reest_cause_opts::recfg_fail ? "Reconfiguration failure"
: cause == asn1::rrc::reest_cause_opts::ho_fail ? "Handover failure"
: "Other failure",
reest_rnti,
reest_source_pci,
reest_cellid);

@ -49,7 +49,7 @@ public:
void set_config_mbsfn_sib2(srsran::mbsfn_sf_cfg_t* cfg_list, uint32_t nof_cfgs) override {}
void set_config_mbsfn_sib13(const srsran::sib13_t& sib13) override {}
void set_config_mbsfn_mcch(const srsran::mcch_msg_t& mcch) override {}
bool cell_search() override { return true; }
bool cell_search(int earfcn) override { return true; }
bool cell_is_camping() override { return true; }
void deactivate_scells() override {}
bool cell_select(phy_cell_t cell) override

@ -90,7 +90,7 @@ int test_phy_ctrl_fsm()
TESTASSERT(phy_ctrl.is_in_sync());
// TEST: Correct initiation of Cell Search state
TESTASSERT(phy_ctrl.start_cell_search(csearch_tester));
TESTASSERT(phy_ctrl.start_cell_search(csearch_tester, -1));
TESTASSERT(not phy_ctrl.is_in_sync());
// TEST: Cell Search only listens to a cell search result event

@ -34,7 +34,7 @@ using namespace asn1;
using namespace srsran;
namespace srsue {
const char* rrc_nr::rrc_nr_state_text[] = {"IDLE", "CONNECTED", "CONNECTED-INACTIVE"};
const static char* rrc_nr_state_text[] = {"IDLE", "CONNECTED", "CONNECTED-INACTIVE"};
rrc_nr::rrc_nr(srsran::task_sched_handle task_sched_) :
logger(srslog::fetch_basic_logger("RRC-NR")),
@ -71,9 +71,15 @@ int rrc_nr::init(phy_interface_rrc_nr* phy_,
stack = stack_;
args = args_;
plmn_is_selected = true; // short-cut SA test
// allocate RRC timers
t300 = task_sched.get_unique_timer();
t301 = task_sched.get_unique_timer();
t302 = task_sched.get_unique_timer();
t304 = task_sched.get_unique_timer();
t310 = task_sched.get_unique_timer();
t311 = task_sched.get_unique_timer();
plmn_is_selected = true; // short-cut SA test
running = true;
sim_measurement_timer = task_sched.get_unique_timer();
@ -104,7 +110,11 @@ void rrc_nr::init_core_less()
pdcp->add_bearer(args.coreless.drb_lcid, pdcp_cnfg);
return;
}
void rrc_nr::get_metrics(rrc_nr_metrics_t& m) {}
void rrc_nr::get_metrics(rrc_nr_metrics_t& m)
{
m.state = state;
}
const char* rrc_nr::get_rb_name(uint32_t lcid)
{
@ -255,7 +265,7 @@ void rrc_nr::decode_dl_ccch(unique_byte_buffer_t pdu)
case dl_ccch_msg_type_c::c1_c_::types::rrc_reject: {
// 5.3.15
const auto& reject = c1->rrc_reject();
srsran::console("Received RRC Reject");
srsran::console("Received RRC Reject\n");
t300.stop();
@ -275,7 +285,6 @@ void rrc_nr::decode_dl_ccch(unique_byte_buffer_t pdu)
task_sched.defer_task([this, rrc_setup_copy]() { handle_rrc_setup(rrc_setup_copy); });
break;
}
default:
logger.error("The provided DL-CCCH message type is not recognized");
break;
@ -349,6 +358,11 @@ void rrc_nr::handle_sib1(const sib1_s& sib1)
{
logger.info("SIB1 received, CellID=%d", meas_cells.serving_cell().get_cell_id() & 0xfff);
meas_cells.serving_cell().set_sib1(sib1);
// TODO: config basic config and remove early exit
return;
// clang-format off
// unhandled fields:
// - cellSelectionInfo
@ -611,7 +625,7 @@ void rrc_nr::send_setup_request(srsran::nr_establishment_cause_t cause)
for (uint i = 0; i < 5; i++) { // fill random ID bytewise, 40 bits = 5 bytes
random_id |= ((uint64_t)rand() & 0xFF) << i * 8;
}
rrc_setup_req->ue_id.random_value().from_number(random_id);
rrc_setup_req->ue_id.random_value().from_number(random_id, rrc_setup_req->ue_id.random_value().length());
rrc_setup_req->establishment_cause = (establishment_cause_opts::options)cause;
send_ul_ccch_msg(ul_ccch_msg);
@ -692,6 +706,16 @@ void rrc_nr::send_con_setup_complete(srsran::unique_byte_buffer_t nas_msg)
send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg);
}
void rrc_nr::send_rrc_reconfig_complete()
{
logger.debug("Preparing RRC Connection Reconfig Complete");
asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg;
auto& rrc_reconfig_complete = ul_dcch_msg.msg.set_c1().set_rrc_recfg_complete().crit_exts.set_rrc_recfg_complete();
send_ul_dcch_msg(srb_to_lcid(nr_srb::srb1), ul_dcch_msg);
}
// EUTRA-RRC interface
int rrc_nr::get_eutra_nr_capabilities(srsran::byte_buffer_t* eutra_nr_caps_pdu)
{
@ -1903,12 +1927,26 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg)
return false;
}
if (!(drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::eps_bearer_id)) {
logger.error("CN association type not supported %s ", drb_cfg.cn_assoc.type().to_string());
return false;
}
if (drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::eps_bearer_id) {
// register EPS bearer over NR PDCP
uint32_t eps_bearer_id = drb_cfg.cn_assoc.eps_bearer_id();
drb_eps_bearer_id[drb_cfg.drb_id] = eps_bearer_id;
stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::nr, lcid);
} else if (drb_cfg.cn_assoc.type() == drb_to_add_mod_s::cn_assoc_c_::types_opts::sdap_cfg) {
const auto& sdap_cfg = drb_cfg.cn_assoc.sdap_cfg();
if (sdap_cfg.sdap_hdr_dl == asn1::rrc_nr::sdap_cfg_s::sdap_hdr_dl_opts::present ||
sdap_cfg.sdap_hdr_ul == asn1::rrc_nr::sdap_cfg_s::sdap_hdr_ul_opts::present) {
logger.error("SDAP currently not supported.");
return false;
}
// TODO: configure SDAP accordingly
uint32_t pdu_session_id = drb_cfg.cn_assoc.sdap_cfg().pdu_session;
// Register PDU session as "EPS bearer" in bearer manager
stack->add_eps_bearer(pdu_session_id, srsran::srsran_rat_t::nr, lcid);
} else {
logger.error("CN association type not supported %s", drb_cfg.cn_assoc.type().to_string());
return false;
}
if (drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl_present && drb_cfg.pdcp_cfg.drb.pdcp_sn_size_ul_present &&
(drb_cfg.pdcp_cfg.drb.pdcp_sn_size_ul.to_number() != drb_cfg.pdcp_cfg.drb.pdcp_sn_size_dl.to_number())) {
@ -1919,9 +1957,6 @@ bool rrc_nr::apply_drb_add_mod(const drb_to_add_mod_s& drb_cfg)
srsran::pdcp_config_t pdcp_cfg = make_drb_pdcp_config_t(drb_cfg.drb_id, true, drb_cfg.pdcp_cfg);
pdcp->add_bearer(lcid, pdcp_cfg);
// register EPS bearer over NR PDCP
stack->add_eps_bearer(eps_bearer_id, srsran::srsran_rat_t::nr, lcid);
return true;
}
@ -2094,12 +2129,11 @@ void rrc_nr::handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_
// Security helper used by Security Mode Command and Mobility handling routines
void rrc_nr::generate_as_keys()
{
uint8_t k_asme[32] = {};
// FIXME: need to add
// nas->get_k_asme(k_asme, 32);
logger.debug(k_asme, 32, "UE K_asme");
// logger.debug("Generating K_enb with UL NAS COUNT: %d", nas->get_k_enb_count());
// usim->generate_as_keys(k_asme, nas->get_k_enb_count(), &sec_cfg);
as_key_t k_amf = {};
nas->get_k_amf(k_amf);
logger.debug(k_amf.data(), 32, "UE K_amf");
logger.debug("Generating K_gnb with UL NAS COUNT: %d", nas->get_ul_nas_count());
usim->generate_nr_as_keys(k_amf, nas->get_ul_nas_count(), &sec_cfg);
logger.info(sec_cfg.k_rrc_enc.data(), 32, "RRC encryption key - k_rrc_enc");
logger.info(sec_cfg.k_rrc_int.data(), 32, "RRC integrity key - k_rrc_int");
logger.info(sec_cfg.k_up_enc.data(), 32, "UP encryption key - k_up_enc");

@ -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) {
srsran::unique_byte_buffer_t nas_sdu;
for (uint32_t i = 0; i < rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list.size(); ++i) {
nas_sdu = srsran::make_byte_buffer();
if (nas_sdu != nullptr) {
memcpy(nas_sdu->msg,
srsran::unique_byte_buffer_t nas_pdu = srsran::make_byte_buffer();
if (nas_pdu != nullptr) {
memcpy(nas_pdu->msg,
rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].data(),
rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size());
nas_sdu->N_bytes = rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size();
rrc_handle.write_sdu(std::move(nas_sdu));
nas_pdu->N_bytes = rrc_nr_reconf.crit_exts.rrc_recfg().non_crit_ext.ded_nas_msg_list[i].size();
rrc_handle.nas->write_pdu(std::move(nas_pdu));
} else {
rrc_handle.logger.error("Couldn't allocate SDU in %s.", __FUNCTION__);
return proc_outcome_t::error;
@ -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;
}
@ -317,7 +319,7 @@ srsran::proc_outcome_t rrc_nr::connection_setup_proc::init(const asn1::rrc_nr::r
{
Info("Starting...");
if (dedicated_info_nas_) {
if (dedicated_info_nas_ == nullptr) {
logger.error("Connection Setup Failed, no dedicatedInfoNAS available");
return proc_outcome_t::error;
}
@ -436,6 +438,17 @@ rrc_nr::cell_selection_proc::handle_cell_search_result(const rrc_interface_phy_n
// until cell selection is done, update PHY config to take the last found PCI
rrc_handle.phy_cfg.carrier.pci = result.pci;
// Until SI acquisition is implemented, provide hard-coded SIB for now
uint8_t msg[] = {0x74, 0x81, 0x01, 0x70, 0x10, 0x40, 0x04, 0x02, 0x00, 0x00, 0x0e, 0x00, 0x00, 0x33, 0x60, 0x38,
0x05, 0x01, 0x00, 0x40, 0x1a, 0x00, 0x00, 0x06, 0x6c, 0x6d, 0x92, 0x21, 0xf3, 0x70, 0x40, 0x20,
0x00, 0x00, 0x80, 0x80, 0x00, 0x41, 0x06, 0x80, 0xa0, 0x90, 0x9c, 0x20, 0x08, 0x55, 0x19, 0x40,
0x00, 0x00, 0x33, 0xa1, 0xc6, 0xd9, 0x22, 0x40, 0x00, 0x00, 0x20, 0xb8, 0x94, 0x63, 0xc0, 0x09,
0x28, 0x44, 0x1b, 0x7e, 0xad, 0x8e, 0x1d, 0x00, 0x9e, 0x2d, 0xa3, 0x0a};
srsran::unique_byte_buffer_t pdu = srsran::make_byte_buffer();
memcpy(pdu->msg, msg, sizeof(msg));
pdu->N_bytes = sizeof(msg);
rrc_handle.write_pdu_bcch_dlsch(std::move(pdu));
if (not rrc_handle.phy->start_cell_select(cs_args)) {
Error("Could not set start cell search.");
return proc_outcome_t::error;

@ -119,10 +119,13 @@ class dummy_eutra : public rrc_eutra_interface_rrc_nr
class dummy_nas : public nas_5g_interface_rrc_nr
{
int write_pdu(srsran::unique_byte_buffer_t pdu) { return SRSRAN_SUCCESS; };
int get_k_amf(srsran::as_key_t& k_amf) { return SRSRAN_SUCCESS; };
uint32_t get_ul_nas_count() { return SRSRAN_SUCCESS; };
};
class dummy_sim : public usim_interface_rrc_nr
{
void generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg){};
bool generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg) { return true; }
bool update_nr_context(srsran::as_security_config_t* sec_cfg) { return true; }
};

@ -237,8 +237,6 @@ int ue_stack_lte::init(const stack_args_t& args_)
args.rrc_nr);
rrc.init(phy, &mac, &rlc, &pdcp, &nas, usim.get(), gw, &rrc_nr, args.rrc);
args.nas_5g.ia5g = "0,1,2,3";
args.nas_5g.ea5g = "0,1,2,3";
nas_5g.init(usim.get(), &rrc_nr, gw, args.nas_5g);
running = true;
@ -339,7 +337,13 @@ bool ue_stack_lte::disable_data()
bool ue_stack_lte::start_service_request()
{
if (running) {
ue_task_queue.try_push([this]() { nas.start_service_request(srsran::establishment_cause_t::mo_data); });
ue_task_queue.try_push([this]() {
if (args.attach_on_nr) {
nas_5g.start_service_request();
} else {
nas.start_service_request(srsran::establishment_cause_t::mo_data);
}
});
}
return true;
}
@ -355,6 +359,7 @@ bool ue_stack_lte::get_metrics(stack_metrics_t* metrics)
rlc.get_metrics(metrics.rlc, metrics.mac[0].nof_tti);
nas.get_metrics(&metrics.nas);
rrc.get_metrics(metrics.rrc);
rrc_nr.get_metrics(metrics.rrc_nr);
pending_stack_metrics.push(metrics);
});
// wait for result

@ -302,6 +302,11 @@ int nas_5g::send_registration_request()
}
}
if (has_sec_ctxt) {
set_k_gnb_count(ctxt_base.tx_count);
ctxt_base.tx_count++;
}
state.set_registered_initiated();
return SRSRAN_SUCCESS;
@ -462,6 +467,7 @@ int nas_5g::send_security_mode_complete(const srsran::nas_5g::security_mode_comm
pcap->write_nas(pdu.get()->msg, pdu.get()->N_bytes);
}
has_sec_ctxt = true;
logger.info("Sending Security Mode Complete");
rrc_nr->write_sdu(std::move(pdu));
ctxt_base.tx_count++;
@ -807,6 +813,7 @@ int nas_5g::handle_registration_accept(registration_accept_t& registration_accep
int nas_5g::handle_registration_reject(registration_reject_t& registration_reject)
{
logger.info("Handling Registration Reject");
has_sec_ctxt = false;
ctxt_base.rx_count++;
state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search);
@ -854,6 +861,7 @@ int nas_5g::handle_authentication_request(authentication_request_t& authenticati
return SRSRAN_ERROR;
}
initial_sec_command = true;
uint8_t res_star[16];
logger.info(authentication_request.authentication_parameter_rand.rand.data(),
@ -897,6 +905,7 @@ int nas_5g::handle_authentication_request(authentication_request_t& authenticati
int nas_5g::handle_authentication_reject(srsran::nas_5g::authentication_reject_t& authentication_reject)
{
logger.info("Handling Authentication Reject");
has_sec_ctxt = false;
ctxt_base.rx_count++;
state.set_deregistered(mm5g_state_t::deregistered_substate_t::plmn_search);
return SRSRAN_SUCCESS;
@ -920,6 +929,7 @@ int nas_5g::handle_service_accept(srsran::nas_5g::service_accept_t& service_acce
int nas_5g::handle_service_reject(srsran::nas_5g::service_reject_t& service_reject)
{
logger.info("Handling Service Reject");
has_sec_ctxt = false;
ctxt_base.rx_count++;
return SRSRAN_SUCCESS;
}
@ -941,10 +951,8 @@ int nas_5g::handle_security_mode_command(security_mode_command_t& security_m
return SRSRAN_ERROR;
}
initial_sec_command = false; // TODO
if (initial_sec_command) {
ctxt_base.rx_count = 0;
set_k_gnb_count(0);
ctxt_base.tx_count = 0;
initial_sec_command = false;
}
@ -1115,6 +1123,27 @@ void nas_5g::get_metrics(nas_5g_metrics_t& metrics)
metrics.state = state.get_state();
}
int nas_5g::get_k_amf(as_key_t& k_amf)
{
if (not has_sec_ctxt) {
logger.error("K_amf requested before a valid NAS security context was established");
return SRSRAN_ERROR;
}
std::copy(std::begin(ctxt_5g.k_amf), std::end(ctxt_5g.k_amf), k_amf.begin());
return SRSRAN_SUCCESS;
}
uint32_t nas_5g::get_ul_nas_count()
{
return ctxt_5g.k_gnb_count;
}
void nas_5g::set_k_gnb_count(uint32_t count)
{
ctxt_5g.k_gnb_count = count;
}
/*******************************************************************************
* Helpers
******************************************************************************/

@ -231,6 +231,7 @@ void usim_base::generate_nas_keys(uint8_t* k_asme,
/*
* RRC Interface
*/
void usim_base::generate_as_keys(uint8_t* k_asme_, uint32_t count_ul, srsran::as_security_config_t* sec_cfg)
{
if (!initiated) {
@ -376,6 +377,42 @@ bool usim_base::generate_nas_keys_5g(uint8_t* k_amf,
* NR RRC Interface
*/
void usim_base::generate_nr_as_keys(srsran::as_key_t& k_amf, uint32_t count_ul, srsran::as_security_config_t* sec_cfg)
{
if (!initiated) {
logger.error("USIM not initiated!");
return;
}
logger.info("Generating NR AS Keys. NAS UL COUNT %d", count_ul);
logger.debug(k_amf.data(), 32, "K_amf");
// Generate K_gnb
srsran::security_generate_k_gnb(k_amf, count_ul, k_gnb_ctx.k_gnb);
logger.info(k_gnb_ctx.k_gnb.data(), 32, "K_gnb");
// Save initial k_gnb
k_gnb_initial = k_gnb_ctx.k_gnb;
security_generate_k_nr_rrc(k_gnb_ctx.k_gnb.data(),
sec_cfg->cipher_algo,
sec_cfg->integ_algo,
sec_cfg->k_rrc_enc.data(),
sec_cfg->k_rrc_int.data());
security_generate_k_nr_up(k_gnb_ctx.k_gnb.data(),
sec_cfg->cipher_algo,
sec_cfg->integ_algo,
sec_cfg->k_up_enc.data(),
sec_cfg->k_up_int.data());
logger.info(k_gnb_ctx.k_gnb.data(), 32, "Initial K_gnb");
logger.info(sec_cfg->k_rrc_int.data(), sec_cfg->k_rrc_int.size(), "NR K_RRC_int");
logger.info(sec_cfg->k_rrc_enc.data(), sec_cfg->k_rrc_enc.size(), "NR K_RRC_enc");
logger.info(sec_cfg->k_up_int.data(), sec_cfg->k_up_int.size(), "NR K_UP_int");
logger.info(sec_cfg->k_up_enc.data(), sec_cfg->k_up_enc.size(), "NR K_UP_enc");
}
bool usim_base::generate_nr_context(uint16_t sk_counter, srsran::as_security_config_t* sec_cfg)
{
if (!initiated) {

@ -20,7 +20,7 @@
*/
#include "srsran/common/metrics_hub.h"
#include "srsran/srsran.h"
#include "srsran/common/test_common.h"
#include "srsue/hdr/metrics_csv.h"
#include "srsue/hdr/metrics_stdout.h"
#include "srsue/hdr/ue_metrics_interface.h"
@ -34,10 +34,12 @@ using namespace srsue;
namespace srsue {
char* csv_file_name = NULL;
static char* csv_file_name = NULL;
static float period = 1.0;
static int duration_s = 5;
// fake classes
class ue_dummy : public ue_metrics_interface
class eutra_ue_dummy : public ue_metrics_interface
{
public:
bool get_metrics(ue_metrics_t* m)
@ -72,6 +74,51 @@ public:
m->phy.nof_active_cc = 1;
m->phy.ch[0].rsrp = -10.0f;
m->phy.ch[0].pathloss = 32;
m->stack.rrc.state = (rand() % 2 == 0) ? RRC_STATE_CONNECTED : RRC_STATE_IDLE;
return true;
}
};
class nsa_ue_dummy : public ue_metrics_interface
{
public:
bool get_metrics(ue_metrics_t* m)
{
*m = {};
// fill dummy values
m->rf.rf_o = 10;
m->phy.nof_active_cc = 1;
m->phy.ch[0].rsrp = -10.0f;
m->phy.ch[0].pathloss = 74;
m->stack.mac[0].rx_pkts = 100;
m->stack.mac[0].rx_errors = 0;
m->stack.mac[0].rx_brate = 200;
m->stack.mac[0].nof_tti = 1;
m->phy.info[1].pci = UINT32_MAX;
m->stack.mac[1].rx_pkts = 100;
m->stack.mac[1].rx_errors = 100;
m->stack.mac[1].rx_brate = 150;
m->stack.mac[1].nof_tti = 1;
// random neighbour cells
if (rand() % 2 == 0) {
phy_meas_t neighbor = {};
neighbor.pci = 8;
neighbor.rsrp = -33;
m->stack.rrc.neighbour_cells.push_back(neighbor);
m->stack.rrc.neighbour_cells.push_back(neighbor); // need to add twice since we use CA
}
m->phy.nof_active_cc = 1;
m->phy.ch[0].rsrp = -10.0f;
m->phy.ch[0].pathloss = 32;
// NR
m->phy_nr.nof_active_cc = 1;
m->stack.mac_nr[0].rx_pkts = 100;
m->stack.mac_nr[0].rx_errors = 2;
m->stack.mac_nr[0].rx_brate = 223;
@ -83,6 +130,31 @@ public:
}
};
class sa_ue_dummy : public ue_metrics_interface
{
public:
bool get_metrics(ue_metrics_t* m)
{
*m = {};
// fill dummy values
m->rf.rf_o = 10;
m->phy.nof_active_cc = 0;
// NR
m->phy_nr.nof_active_cc = 1;
m->phy_nr.info[0].pci = 501;
m->stack.mac_nr[0].rx_pkts = 100;
m->stack.mac_nr[0].rx_errors = 2;
m->stack.mac_nr[0].rx_brate = 223;
m->stack.mac_nr[0].nof_tti = 1;
m->stack.rrc_nr.state = (rand() % 2 == 0) ? RRC_NR_STATE_CONNECTED : RRC_NR_STATE_IDLE;
return true;
}
};
} // namespace srsue
void usage(char* prog)
@ -110,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
metrics_stdout metrics_screen;
metrics_screen.set_ue_handle(&ue);
metrics_screen.set_ue_handle(ue);
// the CSV file writer
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
srsran::metrics_hub<ue_metrics_t> metricshub;
metricshub.init(&ue, period);
metricshub.init(ue, period);
metricshub.add_listener(&metrics_screen);
metricshub.add_listener(&metrics_file);
// enable printing
metrics_screen.toggle_print(true);
std::cout << "Running for 5 seconds .." << std::endl;
usleep(5e6);
std::cout << "Running " << name << " UE test for " << duration_s << " seconds .." << std::endl;
usleep(duration_s * 1e6);
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{};
// Cell search and selection procedures
bool cell_search() override;
bool cell_search(int earfcn) override;
bool cell_select(phy_cell_t cell) override;
bool cell_is_camping() override;

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

@ -71,7 +71,7 @@ int ue::init(const all_args_t& args_)
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) {
srsran::console("Error creating a GW instance.\n");
return SRSRAN_ERROR;
@ -285,6 +285,11 @@ int ue::parse_args(const all_args_t& args_)
return SRSRAN_ERROR;
}
// Update NAS-5G args
args.stack.nas_5g.ia5g = args.stack.nas.eia;
args.stack.nas_5g.ea5g = args.stack.nas.eea;
args.stack.nas_5g.pdu_session_cfgs.push_back({args.stack.nas.apn_name});
return SRSRAN_SUCCESS;
}

Loading…
Cancel
Save