Merge branch 'next' into agpl_next

master
Codebot 3 years ago committed by Your Name
commit 42cc7d5d1a

@ -22,6 +22,7 @@
#include "srsran/common/crash_handler.h"
#include "srsran/common/gen_mch_tables.h"
#include "srsran/srsran.h"
#include <getopt.h>
#include <pthread.h>
#include <semaphore.h>
#include <signal.h>
@ -52,6 +53,12 @@ static char* output_file_name = NULL;
#define PAGE_UP 53
#define PAGE_DOWN 54
#define CFR_THRES_UP_KEY 't'
#define CFR_THRES_DN_KEY 'g'
#define CFR_THRES_STEP 0.05f
#define CFR_PAPR_STEP 0.1f
static srsran_cell_t cell = {
25, // nof_prb
1, // nof_ports
@ -79,6 +86,31 @@ static bool enable_256qam = false;
static float output_file_snr = +INFINITY;
static bool use_standard_lte_rate = false;
// CFR type test args
static char cfr_manual_str[] = "manual";
static char cfr_auto_cma_str[] = "auto_cma";
static char cfr_auto_ema_str[] = "auto_ema";
// CFR runtime control flags
static bool cfr_thr_inc = false;
static bool cfr_thr_dec = false;
typedef struct {
int enable;
char* mode;
float manual_thres;
float strength;
float auto_target_papr;
float ema_alpha;
} cfr_args_t;
static cfr_args_t cfr_args = {.enable = 0,
.mode = cfr_manual_str,
.manual_thres = 1.0f,
.strength = 1.0f,
.auto_target_papr = 8.0f,
.ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB};
static bool null_file_sink = false;
static srsran_filesink_t fsink;
static srsran_ofdm_t ifft[SRSRAN_MAX_PORTS];
@ -94,6 +126,7 @@ static srsran_softbuffer_tx_t* softbuffers[SRSRAN_MAX_CODEWORDS];
static srsran_regs_t regs;
static srsran_dci_dl_t dci_dl;
static int rvidx[SRSRAN_MAX_CODEWORDS] = {0, 0};
static srsran_cfr_cfg_t cfr_config = {};
static cf_t * sf_buffer[SRSRAN_MAX_PORTS] = {NULL}, *output_buffer[SRSRAN_MAX_PORTS] = {NULL};
static uint32_t sf_n_re, sf_n_samples;
@ -143,14 +176,28 @@ static void usage(char* prog)
printf("\t-s output file SNR [Default %f]\n", output_file_snr);
printf("\t-q Enable/Disable 256QAM modulation (default %s)\n", enable_256qam ? "enabled" : "disabled");
printf("\t-Q Use standard LTE sample rates (default %s)\n", use_standard_lte_rate ? "enabled" : "disabled");
printf("CFR Options:\n");
printf("\t--enable_cfr Enable the CFR (default %s)\n", cfr_args.enable ? "enabled" : "disabled");
printf("\t--cfr_mode CFR mode: manual, auto_cma, auto_ema. (default %s)\n", cfr_args.mode);
printf("\t--cfr_manual_thres CFR manual threshold (default %.2f)\n", cfr_args.manual_thres);
printf("\t--cfr_strength CFR strength (default %.2f)\n", cfr_args.strength);
printf("\t--cfr_auto_papr CFR PAPR target for auto modes (default %.2f)\n", cfr_args.auto_target_papr);
printf("\t--cfr_ema_alpha CFR alpha parameter for EMA mode (default %.2f)\n", cfr_args.ema_alpha);
printf("\n");
printf("\t*: See 3GPP 36.212 Table 5.3.3.1.5-4 for more information\n");
}
struct option cfr_opts[] = {{"enable_cfr", no_argument, &cfr_args.enable, 1},
{"cfr_mode", required_argument, NULL, 'C'},
{"cfr_manual_thres", required_argument, NULL, 'T'},
{"cfr_strength", required_argument, NULL, 'S'},
{"cfr_auto_papr", required_argument, NULL, 'P'},
{"cfr_ema_alpha", required_argument, NULL, 'e'},
{0, 0, 0, 0}};
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "IadglfmoncpqvutxbwMsBQ")) != -1) {
while ((opt = getopt_long(argc, argv, "IadglfmoncpqvutxbwMsBQ", cfr_opts, NULL)) != -1) {
switch (opt) {
case 'I':
rf_dev = argv[optind];
@ -215,6 +262,24 @@ static void parse_args(int argc, char** argv)
case 'E':
cell.cp = SRSRAN_CP_EXT;
break;
case 'C':
cfr_args.mode = optarg;
break;
case 'T':
cfr_args.manual_thres = strtof(optarg, NULL);
break;
case 'S':
cfr_args.strength = strtof(optarg, NULL);
break;
case 'P':
cfr_args.auto_target_papr = strtof(optarg, NULL);
break;
case 'e':
cfr_args.ema_alpha = strtof(optarg, NULL);
break;
case 0:
/* getopt_long() set a variable, keep going */
break;
default:
usage(argv[0]);
exit(-1);
@ -228,6 +293,32 @@ static void parse_args(int argc, char** argv)
#endif
}
static int parse_cfr_args()
{
cfr_config.cfr_enable = cfr_args.enable;
cfr_config.manual_thr = cfr_args.manual_thres;
cfr_config.max_papr_db = cfr_args.auto_target_papr;
cfr_config.alpha = cfr_args.strength;
cfr_config.ema_alpha = cfr_args.ema_alpha;
if (!strcmp(cfr_args.mode, cfr_manual_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_MANUAL;
} else if (!strcmp(cfr_args.mode, cfr_auto_cma_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_AUTO_CMA;
} else if (!strcmp(cfr_args.mode, cfr_auto_ema_str)) {
cfr_config.cfr_mode = SRSRAN_CFR_THR_AUTO_EMA;
} else {
ERROR("CFR mode is not recognised");
return SRSRAN_ERROR;
}
if (!srsran_cfr_params_valid(&cfr_config)) {
ERROR("Invalid CFR parameters");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
static void base_init()
{
int i;
@ -325,6 +416,10 @@ static void base_init()
}
srsran_ofdm_set_normalize(&ifft[i], true);
if (srsran_ofdm_set_cfr(&ifft[i], &cfr_config)) {
ERROR("Error setting CFR object");
exit(-1);
}
}
if (srsran_ofdm_tx_init_mbsfn(&ifft_mbsfn, SRSRAN_CP_EXT, sf_buffer[0], output_buffer[0], cell.nof_prb)) {
@ -333,6 +428,10 @@ static void base_init()
}
srsran_ofdm_set_non_mbsfn_region(&ifft_mbsfn, 2);
srsran_ofdm_set_normalize(&ifft_mbsfn, true);
if (srsran_ofdm_set_cfr(&ifft_mbsfn, &cfr_config)) {
ERROR("Error setting CFR object");
exit(-1);
}
if (srsran_pbch_init(&pbch)) {
ERROR("Error creating PBCH object");
@ -483,6 +582,8 @@ static int update_radl()
{
ZERO_OBJECT(dci_dl);
int ret = SRSRAN_ERROR;
/* Configure cell and PDSCH in function of the transmission mode */
switch (transmission_mode) {
case SRSRAN_TM1:
@ -505,7 +606,7 @@ static int update_radl()
break;
default:
ERROR("Transmission mode not implemented.");
exit(-1);
goto exit;
}
dci_dl.rnti = UE_CRNTI;
@ -526,7 +627,80 @@ static int update_radl()
SRSRAN_DCI_TB_DISABLE(dci_dl.tb[1]);
}
// Increase the CFR threshold or target PAPR
if (cfr_thr_inc) {
cfr_thr_inc = false; // Reset the flag
if (cfr_config.cfr_enable && cfr_config.cfr_mode == SRSRAN_CFR_THR_MANUAL) {
cfr_config.manual_thr += CFR_THRES_STEP;
for (int i = 0; i < cell.nof_ports; i++) {
if (srsran_cfr_set_threshold(&ifft[i].tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
}
if (srsran_cfr_set_threshold(&ifft_mbsfn.tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
printf("CFR Thres. set to %.3f\n", cfr_config.manual_thr);
} else if (cfr_config.cfr_enable && cfr_config.cfr_mode != SRSRAN_CFR_THR_MANUAL) {
cfr_config.max_papr_db += CFR_PAPR_STEP;
for (int i = 0; i < cell.nof_ports; i++) {
if (srsran_cfr_set_papr(&ifft[i].tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
}
if (srsran_cfr_set_papr(&ifft_mbsfn.tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
printf("CFR target PAPR set to %.3f\n", cfr_config.max_papr_db);
}
}
// Decrease the CFR threshold or target PAPR
if (cfr_thr_dec) {
cfr_thr_dec = false; // Reset the flag
if (cfr_config.cfr_enable && cfr_config.cfr_mode == SRSRAN_CFR_THR_MANUAL) {
if (cfr_config.manual_thr - CFR_THRES_STEP >= 0) {
cfr_config.manual_thr -= CFR_THRES_STEP;
for (int i = 0; i < cell.nof_ports; i++) {
if (srsran_cfr_set_threshold(&ifft[i].tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
}
if (srsran_cfr_set_threshold(&ifft_mbsfn.tx_cfr, cfr_config.manual_thr) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
printf("CFR Thres. set to %.3f\n", cfr_config.manual_thr);
}
} else if (cfr_config.cfr_enable && cfr_config.cfr_mode != SRSRAN_CFR_THR_MANUAL) {
if (cfr_config.max_papr_db - CFR_PAPR_STEP >= 0) {
cfr_config.max_papr_db -= CFR_PAPR_STEP;
for (int i = 0; i < cell.nof_ports; i++) {
if (srsran_cfr_set_papr(&ifft[i].tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
}
if (srsran_cfr_set_papr(&ifft_mbsfn.tx_cfr, cfr_config.max_papr_db) < SRSRAN_SUCCESS) {
ERROR("Setting the CFR");
goto exit;
}
printf("CFR target PAPR set to %.3f\n", cfr_config.max_papr_db);
}
}
}
srsran_dci_dl_fprint(stdout, &dci_dl, cell.nof_prb);
printf("\nCFR controls:\n");
printf(" Param | INC | DEC |\n");
printf("------------+-----+-----+\n");
printf(" Thres/PAPR | %c | %c |\n", CFR_THRES_UP_KEY, CFR_THRES_DN_KEY);
printf("\n");
if (transmission_mode != SRSRAN_TM1) {
printf("\nTransmission mode key table:\n");
printf(" Mode | 1TB | 2TB |\n");
@ -535,13 +709,15 @@ static int update_radl()
printf(" CDD | | z |\n");
printf("Multiplex | q,w,e,r | a,s |\n");
printf("\n");
printf("Type new MCS index (0-28) or mode key and press Enter: ");
printf("Type new MCS index (0-28) or cfr/mode key and press Enter: ");
} else {
printf("Type new MCS index (0-28) and press Enter: ");
printf("Type new MCS index (0-28) or cfr key and press Enter: ");
}
fflush(stdout);
ret = SRSRAN_SUCCESS;
return 0;
exit:
return ret;
}
/* Read new MCS from stdin */
@ -635,6 +811,12 @@ static int update_control()
case 'x':
transmission_mode = SRSRAN_TM2;
break;
case CFR_THRES_UP_KEY:
cfr_thr_inc = true;
break;
case CFR_THRES_DN_KEY:
cfr_thr_dec = true;
break;
default:
last_mcs_idx = mcs_idx;
mcs_idx = strtol(input, NULL, 10);
@ -652,9 +834,9 @@ static int update_control()
} else if (n < 0) {
// error
perror("select");
return -1;
return SRSRAN_ERROR;
} else {
return 0;
return SRSRAN_SUCCESS;
}
}
@ -728,6 +910,10 @@ int main(int argc, char** argv)
#endif
parse_args(argc, argv);
if (parse_cfr_args() < SRSRAN_SUCCESS) {
ERROR("Error parsing CFR args");
exit(-1);
}
srsran_use_standard_symbol_size(use_standard_lte_rate);
@ -882,7 +1068,7 @@ int main(int argc, char** argv)
srsran_pcfich_encode(&pcfich, &dl_sf, sf_symbols);
/* Update DL resource allocation from control port */
if (update_control()) {
if (update_control() < SRSRAN_SUCCESS) {
ERROR("Error updating parameters from control port");
}

@ -439,8 +439,6 @@ public:
}; // s_nssai_t
// IE: NSSAI
// Reference: 9.11.3.37
class nssai_t
@ -1588,11 +1586,6 @@ public:
class time_zone_t
{
public:
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t second;
uint8_t time_zone;
SRSASN_CODE pack(asn1::bit_ref& bref);
@ -1605,6 +1598,12 @@ public:
class time_zone_and_time_t
{
public:
uint8_t year;
uint8_t month;
uint8_t day;
uint8_t hour;
uint8_t minute;
uint8_t second;
uint8_t time_zone;
SRSASN_CODE pack(asn1::bit_ref& bref);
@ -2285,8 +2284,8 @@ public:
bool si6_lla = false;
PDU_session_type_value_type pdu_session_type_value = PDU_session_type_value_type_::options::ipv4;
std::array<uint8_t, 4> ipv4;
std::array<uint8_t, 16> ipv6;
std::array<uint8_t, 16> smf_i_pv6_link_local_address;
std::array<uint8_t, 8> ipv6;
std::array<uint8_t, 8> smf_i_pv6_link_local_address;
SRSASN_CODE pack(asn1::bit_ref& bref);
SRSASN_CODE unpack(asn1::cbit_ref& bref);
@ -2407,7 +2406,7 @@ public:
protocol_error_unspecified = 0b01101111,
} value;
const char* to_string();
const char* to_string() const;
};
typedef nas_enumerated<cause_value_type_, 8> cause_value_type;

@ -90,7 +90,10 @@ void to_asn1(asn1::rrc_nr::plmn_id_s* asn1_type, const plmn_id_t& cfg);
/***************************
* PHY Config
**************************/
bool make_phy_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* prach_cfg);
bool make_phy_rach_cfg(const asn1::rrc_nr::rach_cfg_common_s& asn1_type,
srsran_duplex_mode_t duplex_mode,
srsran_prach_cfg_t* prach_cfg);
bool fill_rach_cfg_common(const srsran_prach_cfg_t& prach_cfg, asn1::rrc_nr::rach_cfg_common_s& asn1_type);
bool make_phy_tdd_cfg(const asn1::rrc_nr::tdd_ul_dl_cfg_common_s& tdd_ul_dl_cfg_common,
srsran_duplex_config_nr_t* srsran_duplex_config_nr);
@ -133,6 +136,17 @@ bool make_phy_zp_csi_rs_resource(const asn1::rrc_nr::zp_csi_rs_res_s& zp_csi_rs_
bool make_phy_nzp_csi_rs_resource(const asn1::rrc_nr::nzp_csi_rs_res_s& nzp_csi_rs_res,
srsran_csi_rs_nzp_resource_t* csi_rs_nzp_resource);
bool make_phy_carrier_cfg(const asn1::rrc_nr::freq_info_dl_s& freq_info_dl, srsran_carrier_nr_t* carrier_nr);
bool fill_phy_ssb_cfg(const srsran_carrier_nr_t& carrier,
const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg,
phy_cfg_nr_t::ssb_cfg_t* out_ssb);
void fill_ssb_pos_in_burst(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& ssb_pos,
phy_cfg_nr_t::ssb_cfg_t* out_ssb);
bool fill_ssb_pattern_scs(const srsran_carrier_nr_t& carrier,
srsran_ssb_pattern_t* pattern,
srsran_subcarrier_spacing_t* ssb_scs);
bool fill_phy_ssb_cfg(const srsran_carrier_nr_t& carrier,
const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg,
srsran_ssb_cfg_t* out_ssb);
bool make_phy_ssb_cfg(const srsran_carrier_nr_t& carrier,
const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg,
phy_cfg_nr_t::ssb_cfg_t* ssb);
@ -141,11 +155,12 @@ bool make_pdsch_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_
bool make_csi_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_s& serv_cell, srsran_csi_hl_cfg_t* csi_hl);
bool make_duplex_cfg_from_serv_cell(const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell,
srsran_duplex_config_nr_t* duplex_cfg);
void fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch);
bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch);
bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch);
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_pucch_hl_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);

@ -89,7 +89,7 @@ public:
* @param scs SSB Subcarrier spacing
* @return The SSB pattern case if band and subcarrier spacing match, SRSRAN_SSB_PATTERN_INVALID otherwise
*/
static srsran_ssb_patern_t get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs);
static srsran_ssb_pattern_t get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs);
/**
* @brief Select the lower SSB subcarrier spacing valid for this band
@ -411,7 +411,7 @@ private:
struct nr_band_ss_raster {
uint16_t band;
srsran_subcarrier_spacing_t scs;
srsran_ssb_patern_t pattern;
srsran_ssb_pattern_t pattern;
uint32_t gscn_first;
uint32_t gscn_step;
uint32_t gscn_last;

@ -65,6 +65,9 @@ struct rf_args_t {
std::array<rf_args_band_t, SRSRAN_MAX_CARRIERS> ch_rx_bands;
std::array<rf_args_band_t, SRSRAN_MAX_CARRIERS> ch_tx_bands;
FILE** rx_files; // Array of pre-opened FILE* for rx instead of a real device
FILE** tx_files; // Array of pre-opened FILE* for tx instead of a real device
};
class srsran_gw_config_t

@ -113,6 +113,7 @@ protected:
std::atomic<bool> running = {false};
static_blocking_queue<pcap_pdu_t, 1024> queue;
uint16_t ue_id = 0;
int emergency_handler_id = -1;
private:
void pack_and_queue(uint8_t* payload,

@ -32,6 +32,7 @@ class nas_pcap
{
public:
nas_pcap();
~nas_pcap();
void enable();
uint32_t open(std::string filename_, uint32_t ue_id = 0, srsran_rat_t rat_type = srsran_rat_t::lte);
void close();
@ -42,6 +43,7 @@ private:
std::string filename;
FILE* pcap_file = nullptr;
uint32_t ue_id = 0;
int emergency_handler_id = -1;
void pack_and_write(uint8_t* pdu, uint32_t pdu_len_bytes);
};

@ -31,7 +31,7 @@ class ngap_pcap
{
public:
ngap_pcap();
~ngap_pcap() = default;
~ngap_pcap();
ngap_pcap(const ngap_pcap& other) = delete;
ngap_pcap& operator=(const ngap_pcap& other) = delete;
ngap_pcap(ngap_pcap&& other) = delete;
@ -46,6 +46,7 @@ private:
bool enable_write = false;
std::string filename;
FILE* pcap_file = nullptr;
int emergency_handler_id = -1;
};
} // namespace srsran

@ -42,7 +42,7 @@ struct phy_cfg_nr_t {
uint32_t periodicity_ms = 0;
std::array<bool, SRSRAN_SSB_NOF_CANDIDATES> position_in_burst = {};
srsran_subcarrier_spacing_t scs = srsran_subcarrier_spacing_30kHz;
srsran_ssb_patern_t pattern = SRSRAN_SSB_PATTERN_A;
srsran_ssb_pattern_t pattern = SRSRAN_SSB_PATTERN_A;
};
srsran_duplex_config_nr_t duplex = {};

@ -31,6 +31,7 @@ class s1ap_pcap
{
public:
s1ap_pcap();
~s1ap_pcap();
s1ap_pcap(const s1ap_pcap& other) = delete;
s1ap_pcap& operator=(const s1ap_pcap& other) = delete;
s1ap_pcap(s1ap_pcap&& other) = delete;
@ -45,6 +46,7 @@ private:
bool enable_write = false;
std::string filename;
FILE* pcap_file = nullptr;
int emergency_handler_id = -1;
};
} // namespace srsran

@ -62,6 +62,7 @@
#define SRSRAN_ERROR_OUT_OF_BOUNDS -5
#define SRSRAN_ERROR_CANT_START -6
#define SRSRAN_ERROR_ALREADY_STARTED -7
#define SRSRAN_ERROR_RX_EOF -8
// cf_t definition
typedef _Complex float cf_t;

@ -28,6 +28,11 @@ namespace srsenb {
class enb_command_interface
{
public:
/**
* Trigger downlink singnal measurements (currently PAPR)
*/
virtual void cmd_cell_measure() = 0;
/**
* Sets the relative gain of a cell from it's index (following rr.conf) order.
* @param cell_id Provides a cell identifier

@ -310,7 +310,7 @@ public:
double center_freq_hz;
double ssb_freq_hz;
srsran_subcarrier_spacing_t ssb_scs;
srsran_ssb_patern_t ssb_pattern;
srsran_ssb_pattern_t ssb_pattern;
srsran_duplex_mode_t duplex_mode;
};

@ -0,0 +1,118 @@
/**
*
* \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_CFR_H
#define SRSRAN_CFR_H
#include "srsran/config.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/dft/dft.h"
#define CFR_EMA_INIT_AVG_PWR 0.1
/**
* @brief CFR manual threshold or PAPR limiting with Moving Average or EMA power averaging
*/
typedef enum SRSRAN_API {
SRSRAN_CFR_THR_MANUAL = 1,
SRSRAN_CFR_THR_AUTO_CMA = 2,
SRSRAN_CFR_THR_AUTO_EMA = 3
} srsran_cfr_mode_t;
/**
* @brief CFR module configuration arguments
*/
typedef struct SRSRAN_API {
bool cfr_enable;
srsran_cfr_mode_t cfr_mode;
// always used (mandatory)
uint32_t symbol_bw; ///< OFDM symbol bandwidth, in FFT bins
uint32_t symbol_sz; ///< OFDM symbol size (in samples). This is the FFT size
float alpha; ///< Alpha parameter of the clipping algorithm
bool dc_sc; ///< Take into account the DC subcarrier for the filter BW
// SRSRAN_CFR_THR_MANUAL mode parameters
float manual_thr; ///< Fixed threshold used in SRSRAN_CFR_THR_MANUAL mode
// SRSRAN_CFR_THR_AUTO_CMA and SRSRAN_CFR_THR_AUTO_EMA mode parameters
bool measure_out_papr; ///< Enable / disable output PAPR measurement
float max_papr_db; ///< Input PAPR threshold used in SRSRAN_CFR_THR_AUTO_CMA and SRSRAN_CFR_THR_AUTO_EMA modes
float ema_alpha; ///< EMA alpha parameter for avg power calculation, used in SRSRAN_CFR_THR_AUTO_EMA mode
} srsran_cfr_cfg_t;
typedef struct SRSRAN_API {
srsran_cfr_cfg_t cfg;
float max_papr_lin;
srsran_dft_plan_t fft_plan;
srsran_dft_plan_t ifft_plan;
float* lpf_spectrum; ///< FFT filter spectrum
uint32_t lpf_bw; ///< Bandwidth of the LPF
float* abs_buffer_in; ///< Store the input absolute value
float* abs_buffer_out; ///< Store the output absolute value
cf_t* peak_buffer;
float pwr_avg_in; ///< store the avg. input power with MA or EMA averaging
float pwr_avg_out; ///< store the avg. output power with MA or EMA averaging
// Power average buffers, used in SRSRAN_CFR_THR_AUTO_CMA mode
uint64_t cma_n;
} srsran_cfr_t;
SRSRAN_API int srsran_cfr_init(srsran_cfr_t* q, srsran_cfr_cfg_t* cfg);
/**
* @brief Applies the CFR algorithm to the time domain OFDM symbols
*
* @attention This function must be called once per symbol, and it will process q->symbol_sz samples
*
* @param[in] q The CFR object and configuration
* @param[in] in Input buffer containing the time domain OFDM symbol without CP
* @param[out] out Output buffer with the processed OFDM symbol
* @return SRSRAN_SUCCESS if the CFR object is initialised, otherwise SRSRAN_ERROR
*/
SRSRAN_API void srsran_cfr_process(srsran_cfr_t* q, cf_t* in, cf_t* out);
SRSRAN_API void srsran_cfr_free(srsran_cfr_t* q);
/**
* @brief Checks the validity of the CFR algorithm parameters.
*
* @attention Does not check symbol size and bandwidth
*
* @param[in] cfr_conf the CFR configuration
* @return true if the configuration is valid, false otherwise
*/
SRSRAN_API bool srsran_cfr_params_valid(srsran_cfr_cfg_t* cfr_conf);
/**
* @brief Sets the manual threshold of the CFR (used in manual mode).
*
* @attention this is not thread-safe
*
* @param[in] q the CFR object
* @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR or SRSRAN_ERROR_INVALID_INPUTS otherwise
*/
SRSRAN_API int srsran_cfr_set_threshold(srsran_cfr_t* q, float thres);
/**
* @brief Sets the papr target of the CFR (used in auto modes).
*
* @attention this is not thread-safe
*
* @param[in] q the CFR object
* @return SRSRAN_SUCCESS if successful, SRSRAN_ERROR or SRSRAN_ERROR_INVALID_INPUTS otherwise
*/
SRSRAN_API int srsran_cfr_set_papr(srsran_cfr_t* q, float papr);
#endif // SRSRAN_CFR_H

@ -358,7 +358,7 @@ typedef enum SRSRAN_API {
SRSRAN_SSB_PATTERN_D, // FR2, 120 kHz SCS
SRSRAN_SSB_PATTERN_E, // FR2, 240 kHz SCS
SRSRAN_SSB_PATTERN_INVALID,
} srsran_ssb_patern_t;
} srsran_ssb_pattern_t;
typedef enum SRSRAN_API {
SRSRAN_DUPLEX_MODE_FDD = 0, // Paired
@ -741,14 +741,14 @@ SRSRAN_API int srsran_coreset_to_str(srsran_coreset_t* coreset, char* str, uint3
* @param pattern
* @return a string describing the SSB pattern
*/
SRSRAN_API const char* srsran_ssb_pattern_to_str(srsran_ssb_patern_t pattern);
SRSRAN_API const char* srsran_ssb_pattern_to_str(srsran_ssb_pattern_t pattern);
/**
* @brief Convert string to SSB pattern
* @param str String to convert
* @return The pattern, SRSRAN_SSB_PATTERN_INVALID if string is invalid
*/
SRSRAN_API srsran_ssb_patern_t srsran_ssb_pattern_fom_str(const char* str);
SRSRAN_API srsran_ssb_pattern_t srsran_ssb_pattern_fom_str(const char* str);
/**
* @brief Compares if two NR carrier structures are equal

@ -34,6 +34,7 @@
#include <strings.h>
#include "srsran/config.h"
#include "srsran/phy/cfr/cfr.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/dft/dft.h"
@ -48,7 +49,7 @@
typedef struct SRSRAN_API {
// Compulsory parameters
uint32_t nof_prb; ///< Number of Resource Block
cf_t* in_buffer; ///< Input bnuffer pointer
cf_t* in_buffer; ///< Input buffer pointer
cf_t* out_buffer; ///< Output buffer pointer
srsran_cp_t cp; ///< Cyclic prefix type
@ -60,6 +61,7 @@ typedef struct SRSRAN_API {
uint32_t symbol_sz; ///< Symbol size, forces a given symbol size for the number of PRB
bool keep_dc; ///< If true, it does not remove the DC
double phase_compensation_hz; ///< Carrier frequency in Hz for phase compensation, set to 0 to disable
srsran_cfr_cfg_t cfr_tx_cfg; ///< Tx CFR configuration
} srsran_ofdm_cfg_t;
/**
@ -85,6 +87,7 @@ typedef struct SRSRAN_API {
cf_t* shift_buffer;
cf_t* window_offset_buffer;
cf_t phase_compensation[SRSRAN_MAX_NSYMB * SRSRAN_NOF_SLOTS_PER_SF];
srsran_cfr_t tx_cfr; ///< Tx CFR object
} srsran_ofdm_t;
/**
@ -145,4 +148,6 @@ SRSRAN_API int srsran_ofdm_set_phase_compensation(srsran_ofdm_t* q, double cente
SRSRAN_API void srsran_ofdm_set_non_mbsfn_region(srsran_ofdm_t* q, uint8_t non_mbsfn_region);
SRSRAN_API int srsran_ofdm_set_cfr(srsran_ofdm_t* q, srsran_cfr_cfg_t* cfr);
#endif // SRSRAN_OFDM_H

@ -64,6 +64,8 @@ typedef struct SRSRAN_API {
srsran_dl_sf_cfg_t dl_sf;
srsran_cfr_cfg_t cfr_config;
cf_t* sf_symbols[SRSRAN_MAX_PORTS];
cf_t* out_buffer[SRSRAN_MAX_PORTS];
srsran_ofdm_t ifft[SRSRAN_MAX_PORTS];
@ -102,6 +104,8 @@ SRSRAN_API void srsran_enb_dl_free(srsran_enb_dl_t* q);
SRSRAN_API int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell);
SRSRAN_API int srsran_enb_dl_set_cfr(srsran_enb_dl_t* q, const srsran_cfr_cfg_t* cfr);
SRSRAN_API bool srsran_enb_dl_location_is_common_ncce(srsran_enb_dl_t* q, const srsran_dci_location_t* loc);
SRSRAN_API void srsran_enb_dl_put_base(srsran_enb_dl_t* q, srsran_dl_sf_cfg_t* dl_sf);

@ -58,7 +58,7 @@ typedef struct {
*
* Reference: 3GPP TS 36.211 version 13.2.0 Release 13 Sec. 10.2.4
*/
typedef struct SRS_API {
typedef struct SRSRAN_API {
srsran_nbiot_cell_t cell;
uint32_t frame_idx;

@ -25,6 +25,7 @@
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <sys/time.h>
#include "srsran/config.h"
@ -77,6 +78,18 @@ SRSRAN_API int srsran_rf_open_multi(srsran_rf_t* h, char* args, uint32_t nof_cha
SRSRAN_API int srsran_rf_open_devname(srsran_rf_t* h, const char* devname, char* args, uint32_t nof_channels);
/**
* @brief Opens a file-based RF abstraction
* @param[out] rf Device handle
* @param[in] rx_files List of pre-opened FILE* for each RX channel; NULL to disable
* @param[in] tx_files List of pre-opened FILE* for each TX channel; NULL to disable
* @param[in] nof_channels Number of channels per direction
* @param[in] base_srate Sample rate of RX and TX files
* @return SRSRAN_SUCCESS on success, otherwise error code
*/
SRSRAN_API int
srsran_rf_open_file(srsran_rf_t* rf, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate);
SRSRAN_API const char* srsran_rf_name(srsran_rf_t* h);
SRSRAN_API int srsran_rf_start_gain_thread(srsran_rf_t* rf, bool tx_gain_same_rx);

@ -70,7 +70,7 @@ typedef struct SRSRAN_API {
double center_freq_hz; ///< Base-band center frequency in Hz
double ssb_freq_hz; ///< SSB center frequency
srsran_subcarrier_spacing_t scs; ///< SSB configured Subcarrier spacing
srsran_ssb_patern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search
srsran_ssb_pattern_t pattern; ///< SSB pattern as defined in TS 38.313 section 4.1 Cell search
srsran_duplex_mode_t duplex_mode; ///< Set to true if the spectrum is paired (FDD)
uint32_t periodicity_ms; ///< SSB periodicity in ms
float beta_pss; ///< PSS power allocation
@ -98,7 +98,7 @@ typedef struct SRSRAN_API {
uint32_t corr_sz; ///< Correlation size
uint32_t corr_window; ///< Correlation window length
uint32_t ssb_sz; ///< SSB size in samples at the configured sampling rate
int32_t f_offset; ///< Current SSB integer frequency offset (multiple of SCS)
int32_t f_offset; ///< SSB integer frequency offset (multiple of SCS) between DC and the SSB center
uint32_t cp_sz; ///< CP length for the given symbol size
/// Other parameters
@ -152,22 +152,6 @@ SRSRAN_API void srsran_ssb_free(srsran_ssb_t* q);
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg);
/**
* @brief Decodes PBCH in the given time domain signal
* @note It currently expects an input buffer of half radio frame
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param n_hf Number of hald radio frame, 0 or 1
* @param ssb_idx SSB candidate index
* @param in Input baseband buffer
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* in,
srsran_pbch_msg_nr_t* msg);
/**
* @brief Searches for an SSB transmission and decodes the PBCH message
@ -187,6 +171,21 @@ SRSRAN_API int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_s
*/
SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx);
/**
*
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param msg NR PBCH message to transmit
* @param re_grid Resource grid pointer
* @param grid_sz_rb Resource grid bandwidth in subcarriers
* @return SRSRAN_SUCCES if inputs and data are correct, otherwise SRSRAN_ERROR code
*/
SRSRAN_API int srsran_ssb_put_grid(srsran_ssb_t* q,
uint32_t N_id,
const srsran_pbch_msg_nr_t* msg,
cf_t* re_grid,
uint32_t grid_bw_sc);
/**
* @brief Adds SSB to a given signal in time domain
* @param q SSB object
@ -197,6 +196,41 @@ SRSRAN_API bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx);
SRSRAN_API int
srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out);
/**
* @brief Decodes PBCH in the given time domain signal
* @note It currently expects an input buffer of half radio frame
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param n_hf Number of hald radio frame, 0 or 1
* @param ssb_idx SSB candidate index
* @param in Input baseband buffer
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* grid,
srsran_pbch_msg_nr_t* msg);
/**
* @brief Decodes PBCH in the given resource grid
* @param q SSB object
* @param N_id Physical Cell Identifier
* @param n_hf Number of hald radio frame, 0 or 1
* @param ssb_idx SSB candidate index
* @param grid Input resource grid buffer
* @param grid_sz_rb Resource grid bandwidth in subcarriers
* @return SRSRAN_SUCCESS if the parameters are valid, SRSRAN_ERROR code otherwise
*/
SRSRAN_API int srsran_ssb_decode_grid(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* in,
uint32_t grid_sz_rb,
srsran_pbch_msg_nr_t* msg);
/**
* @brief Perform cell search and measurement
* @note This function assumes the SSB transmission is aligned with the input base-band signal

@ -233,6 +233,7 @@ SRSRAN_API void srsran_vec_conj_cc(const cf_t* x, cf_t* y, const uint32_t len);
SRSRAN_API float srsran_vec_avg_power_cf(const cf_t* x, const uint32_t len);
SRSRAN_API float srsran_vec_avg_power_sf(const int16_t* x, const uint32_t len);
SRSRAN_API float srsran_vec_avg_power_bf(const int8_t* x, const uint32_t len);
SRSRAN_API float srsran_vec_avg_power_ff(const float* x, const uint32_t len);
/* Correlation between complex vectors x and y */
SRSRAN_API float srsran_vec_corr_ccc(const cf_t* x, cf_t* y, const uint32_t len);
@ -361,6 +362,36 @@ SRSRAN_API void srsran_vec_apply_cfo(const cf_t* x, float cfo, cf_t* z, int len)
SRSRAN_API float srsran_vec_estimate_frequency(const cf_t* x, int len);
/*!
* @brief Generates an amplitude envelope that, multiplied point-wise with a vector, results in clipping
* by a specified amplitude threshold.
* @param[in] x_abs Absolute value vector of the signal to be clipped
* @param[in] thres Clipping threshold
* @param[out] clip_env The generated clipping envelope
* @param[in] len Length of the vector.
*/
SRSRAN_API void
srsran_vec_gen_clip_env(const float* x_abs, const float thres, const float alpha, float* env, const int len);
/*!
* @brief Calculates the PAPR of a complex vector
* @param[in] in Input vector
* @param[in] len Vector length.
*/
SRSRAN_API float srsran_vec_papr_c(const cf_t* in, const int len);
/*!
* @brief Calculates the ACPR of a signal using its baseband spectrum
* @attention The spectrum passed by x_f needs to be in FFT form
* @param[in] x_f Spectrum of the signal
* @param[in] win_pos_len Channel frequency window for the positive side of the spectrum
* @param[in] win_neg_len Channel frequency window for the negative side of the spectrum
* @param[in] len Length of the x_f vector
* @returns The ACPR in linear form
*/
SRSRAN_API float
srsran_vec_acpr_c(const cf_t* x_f, const uint32_t win_pos_len, const uint32_t win_neg_len, const uint32_t len);
#ifdef __cplusplus
}
#endif

@ -167,6 +167,19 @@ private:
*/
bool open_dev(const uint32_t& device_idx, const std::string& device_name, const std::string& devive_args);
/**
* Helper method for opening a file-based RF device abstraction
*
* @param device_idx Device index
* @param rx_files Array of pre-opened FILE* for rx
* @param tx_files Array of pre-opened FILE* for tx
* @param nof_channels Number of elements in each array @p rx_files and @p tx_files
* @param base_srate Sampling rate in Hz
* @return it returns true if the device was opened successful, otherwise it returns false
*/
bool
open_dev(const uint32_t& device_idx, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate);
/**
* Helper method for transmitting over a single RF device. This function maps automatically the logical transmit
* buffers to the physical RF buffers for the given device.

@ -61,6 +61,7 @@ extern "C" {
#include "srsran/phy/channel/ch_awgn.h"
#include "srsran/phy/cfr/cfr.h"
#include "srsran/phy/dft/dft.h"
#include "srsran/phy/dft/dft_precoding.h"
#include "srsran/phy/dft/ofdm.h"

@ -26,7 +26,11 @@ using emergency_cleanup_callback = void (*)(void*);
// Add a cleanup function to be called when a kill signal is about to be delivered to the process. The handler may
// optionally pass a pointer to identify what instance of the handler is being called.
void add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data);
// Returns the id of the handler as a positive value, otherwise returns -1 on error.
int add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data);
// Removes the emergency handler with the specified id.
void remove_emergency_cleanup_handler(int id);
// Executes all registered emergency cleanup handlers.
void execute_emergency_cleanup_handlers();

@ -2730,11 +2730,6 @@ SRSASN_CODE network_name_t::unpack(asn1::cbit_ref& bref)
// Reference: 9.11.3.52
SRSASN_CODE time_zone_t::pack(asn1::bit_ref& bref)
{
HANDLE_CODE(bref.pack(year, 8));
HANDLE_CODE(bref.pack(month, 8));
HANDLE_CODE(bref.pack(day, 8));
HANDLE_CODE(bref.pack(hour, 8));
HANDLE_CODE(bref.pack(second, 8));
HANDLE_CODE(bref.pack(time_zone, 8));
return SRSASN_SUCCESS;
}
@ -2743,11 +2738,6 @@ SRSASN_CODE time_zone_t::pack(asn1::bit_ref& bref)
// Reference: 9.11.3.52
SRSASN_CODE time_zone_t::unpack(asn1::cbit_ref& bref)
{
HANDLE_CODE(bref.unpack(year, 8));
HANDLE_CODE(bref.unpack(month, 8));
HANDLE_CODE(bref.unpack(day, 8));
HANDLE_CODE(bref.unpack(hour, 8));
HANDLE_CODE(bref.unpack(second, 8));
HANDLE_CODE(bref.unpack(time_zone, 8));
return SRSASN_SUCCESS;
}
@ -2756,6 +2746,12 @@ SRSASN_CODE time_zone_t::unpack(asn1::cbit_ref& bref)
// Reference: 9.11.3.53
SRSASN_CODE time_zone_and_time_t::pack(asn1::bit_ref& bref)
{
HANDLE_CODE(bref.pack(year, 8));
HANDLE_CODE(bref.pack(month, 8));
HANDLE_CODE(bref.pack(day, 8));
HANDLE_CODE(bref.pack(hour, 8));
HANDLE_CODE(bref.pack(minute, 8));
HANDLE_CODE(bref.pack(second, 8));
HANDLE_CODE(bref.pack(time_zone, 8));
return SRSASN_SUCCESS;
}
@ -2764,6 +2760,12 @@ SRSASN_CODE time_zone_and_time_t::pack(asn1::bit_ref& bref)
// Reference: 9.11.3.53
SRSASN_CODE time_zone_and_time_t::unpack(asn1::cbit_ref& bref)
{
HANDLE_CODE(bref.unpack(year, 8));
HANDLE_CODE(bref.unpack(month, 8));
HANDLE_CODE(bref.unpack(day, 8));
HANDLE_CODE(bref.unpack(hour, 8));
HANDLE_CODE(bref.unpack(minute, 8));
HANDLE_CODE(bref.unpack(second, 8));
HANDLE_CODE(bref.unpack(time_zone, 8));
return SRSASN_SUCCESS;
}
@ -3944,9 +3946,9 @@ SRSASN_CODE pdu_address_t::pack(asn1::bit_ref& bref)
if (pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4) {
HANDLE_CODE(bref.pack_bytes(ipv4.data(), 4));
} else if (pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv6) {
HANDLE_CODE(bref.pack_bytes(ipv6.data(), 16));
HANDLE_CODE(bref.pack_bytes(ipv6.data(), 8));
} else if (pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4v6) {
HANDLE_CODE(bref.pack_bytes(ipv6.data(), 16));
HANDLE_CODE(bref.pack_bytes(ipv6.data(), 8));
HANDLE_CODE(bref.pack_bytes(ipv4.data(), 4));
}
@ -3985,20 +3987,20 @@ SRSASN_CODE pdu_address_t::unpack(asn1::cbit_ref& bref)
HANDLE_CODE(bref.unpack_bytes(ipv4.data(), 4));
} else if (length == 9 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv6 &&
si6_lla == false) {
HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 16));
HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 8));
} else if (length == 13 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4v6 &&
si6_lla == false) {
HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 16));
HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 8));
HANDLE_CODE(bref.unpack_bytes(ipv4.data(), 4));
} else if (length == 25 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv6 &&
si6_lla == true) {
HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 16));
HANDLE_CODE(bref.unpack_bytes(smf_i_pv6_link_local_address.data(), 16));
HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 8));
HANDLE_CODE(bref.unpack_bytes(smf_i_pv6_link_local_address.data(), 8));
} else if (length == 29 && pdu_session_type_value == pdu_address_t::PDU_session_type_value_type_::options::ipv4v6 &&
si6_lla == true) {
HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 16));
HANDLE_CODE(bref.unpack_bytes(ipv6.data(), 8));
HANDLE_CODE(bref.unpack_bytes(ipv4.data(), 4));
HANDLE_CODE(bref.unpack_bytes(smf_i_pv6_link_local_address.data(), 16));
HANDLE_CODE(bref.unpack_bytes(smf_i_pv6_link_local_address.data(), 8));
} else {
asn1::log_error("Not expected combination of length and type field");
return SRSASN_ERROR_DECODE_FAIL;
@ -4098,6 +4100,100 @@ SRSASN_CODE cause_5gsm_t::unpack(asn1::cbit_ref& bref)
return SRSASN_SUCCESS;
}
const char* cause_5gsm_t::cause_value_type_::to_string() const
{
switch (value) {
case cause_value_type_::operator_determined_barring:
return "Operator determined barring";
case cause_value_type_::insufficient_resources:
return "Insufficient resources";
case cause_value_type_::missing_or_unknown_dnn:
return "Missing or unknown DNN";
case cause_value_type_::unknown_pdu_session_type:
return "Unknown PDU session type";
case cause_value_type_::user_authentication_or_authorization_failed:
return "User authentication or authorization failed";
case cause_value_type_::request_rejected_unspecified:
return "Request rejected, unspecified";
case cause_value_type_::service_option_not_supported:
return "Service option not supported";
case cause_value_type_::requested_service_option_not_subscribed:
return "Requested service option not subscribed";
case cause_value_type_::pti_already_in_use:
return "PTI already in use";
case cause_value_type_::regular_deactivation:
return "Regular deactivation";
case cause_value_type_::network_failure:
return "Network failure";
case cause_value_type_::reactivation_requested:
return "Reactivation requested";
case cause_value_type_::semantic_error_in_the_tft_operation:
return "Semantic error in the TFT operation";
case cause_value_type_::syntactical_error_in_the_tft_operation:
return "Syntactical error in the TFT operation";
case cause_value_type_::invalid_pdu_session_identity:
return "Invalid PDU session identity";
case cause_value_type_::semantic_errors_in_packet_filter:
return "Semantic errors in packet filter";
case cause_value_type_::syntactical_error_in_packet_filter:
return "Syntactical error in packet filter";
case cause_value_type_::out_of_ladn_service_area:
return "Out of LADN service area";
case cause_value_type_::pti_mismatch:
return "PTI mismatch";
case cause_value_type_::pdu_session_type_i_pv4_only_allowed:
return "PDU session type IPv4 only allowed";
case cause_value_type_::pdu_session_type_i_pv6_only_allowed:
return "PDU session type IPv6 only allowed";
case cause_value_type_::pdu_session_does_not_exist:
return "PDU session does not exist";
case cause_value_type_::pdu_session_type_i_pv4v6_only_allowed:
return "PDU session type IPv4v6 only allowed";
case cause_value_type_::pdu_session_type_unstructured_only_allowed:
return "PDU session type Unstructured only allowed";
case cause_value_type_::unsupported_5_qi_value:
return "Unsupported 5QI value";
case cause_value_type_::pdu_session_type_ethernet_only_allowed:
return "PDU session type Ethernet only allowed";
case cause_value_type_::insufficient_resources_for_specific_slice_and_dnn:
return "Insufficient resources for specific slice and DNN";
case cause_value_type_::not_supported_ssc_mode:
return "Not supported SSC mode";
case cause_value_type_::insufficient_resources_for_specific_slice:
return "Insufficient resources for specific slice";
case cause_value_type_::missing_or_unknown_dnn_in_a_slice:
return "Missing or unknown DNN in a slice";
case cause_value_type_::invalid_pti_value:
return "Invalid PTI value";
case cause_value_type_::maximum_data_rate_per_ue_for_user_plane_integrity_protection_is_too_low:
return "Maximum data rate per UE for user-plane integrity protection is too low";
case cause_value_type_::semantic_error_in_the_qo_s_operation:
return "Semantic error in the QoS operation";
case cause_value_type_::syntactical_error_in_the_qo_s_operation:
return "Syntactical error in the QoS operation";
case cause_value_type_::invalid_mapped_eps_bearer_identity:
return "Invalid mapped EPS bearer identity";
case cause_value_type_::semantically_incorrect_message:
return "Semantically incorrect message";
case cause_value_type_::invalid_mandatory_information:
return "Invalid mandatory information";
case cause_value_type_::message_type_non_existent_or_not_implemented:
return "Message type non-existent or not implemented";
case cause_value_type_::message_type_not_compatible_with_the_protocol_state:
return "Message type not compatible with the protocol state";
case cause_value_type_::information_element_non_existent_or_not_implemented:
return "Information element non-existent or not implemented";
case cause_value_type_::conditional_ie_error:
return "Conditional IE error";
case cause_value_type_::message_not_compatible_with_the_protocol_state:
return "Message not compatible with the protocol state";
case cause_value_type_::protocol_error_unspecified:
return "Protocol error, unspecified";
default:
return "Invalid Choice";
}
}
// IE: GPRS timer
// Reference: 9.11.2.3
SRSASN_CODE gprs_timer_t::pack(asn1::bit_ref& bref)

@ -111,10 +111,11 @@ void make_mac_rach_cfg(const rach_cfg_common_s& asn1_type, rach_cfg_nr_t* rach_c
int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_t* cfg_out)
{
rlc_config_t rlc_cfg = rlc_config_t::default_rlc_um_nr_config();
rlc_config_t rlc_cfg = {};
rlc_cfg.rat = srsran_rat_t::nr;
switch (asn1_type.type().value) {
case rlc_cfg_c::types_opts::am:
rlc_cfg = rlc_config_t::default_rlc_am_nr_config();
if (asn1_type.am().dl_am_rlc.sn_field_len_present && asn1_type.am().ul_am_rlc.sn_field_len_present &&
asn1_type.am().dl_am_rlc.sn_field_len != asn1_type.am().ul_am_rlc.sn_field_len) {
asn1::log_warning("NR RLC sequence number length is not the same in uplink and downlink");
@ -135,6 +136,7 @@ int make_rlc_config_t(const rlc_cfg_c& asn1_type, uint8_t bearer_id, rlc_config_
}
break;
case rlc_cfg_c::types_opts::um_bi_dir:
rlc_cfg = rlc_config_t::default_rlc_um_nr_config();
rlc_cfg.rlc_mode = rlc_mode_t::um;
rlc_cfg.um_nr.t_reassembly_ms = asn1_type.um_bi_dir().dl_um_rlc.t_reassembly.to_number();
rlc_cfg.um_nr.bearer_id = bearer_id;
@ -279,14 +281,22 @@ srsran::pdcp_config_t make_drb_pdcp_config_t(const uint8_t bearer_id, bool is_ue
return cfg;
}
bool make_phy_rach_cfg(const rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* prach_cfg)
bool make_phy_rach_cfg(const rach_cfg_common_s& asn1_type,
srsran_duplex_mode_t duplex_mode,
srsran_prach_cfg_t* prach_cfg)
{
prach_cfg->is_nr = true;
prach_cfg->config_idx = asn1_type.rach_cfg_generic.prach_cfg_idx;
prach_cfg->zero_corr_zone = (uint32_t)asn1_type.rach_cfg_generic.zero_correlation_zone_cfg;
prach_cfg->num_ra_preambles = 64; // Hard-coded
prach_cfg->num_ra_preambles = 64;
if (asn1_type.total_nof_ra_preambs_present) {
prach_cfg->num_ra_preambles = asn1_type.total_nof_ra_preambs;
}
prach_cfg->hs_flag = false; // Hard-coded
prach_cfg->tdd_config = {}; // Hard-coded
prach_cfg->tdd_config = {};
if (duplex_mode == SRSRAN_DUPLEX_MODE_TDD) {
prach_cfg->tdd_config.configured = true;
}
// As the current PRACH is based on LTE, the freq-offset shall be subtracted 1 for aligning with NR bandwidth
// For example. A 52 PRB cell with an freq_offset of 1 will match a LTE 50 PRB cell with freq_offset of 0
@ -296,11 +306,12 @@ bool make_phy_rach_cfg(const rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* p
return false;
}
switch (prach_cfg->root_seq_idx = asn1_type.prach_root_seq_idx.type()) {
switch (asn1_type.prach_root_seq_idx.type().value) {
case rach_cfg_common_s::prach_root_seq_idx_c_::types_opts::l839:
prach_cfg->root_seq_idx = (uint32_t)asn1_type.prach_root_seq_idx.l839();
break;
case rach_cfg_common_s::prach_root_seq_idx_c_::types_opts::l139:
prach_cfg->root_seq_idx = (uint32_t)asn1_type.prach_root_seq_idx.l139();
default:
asn1::log_error("Not-implemented option for prach_root_seq_idx type %s",
asn1_type.prach_root_seq_idx.type().to_string());
@ -310,6 +321,40 @@ bool make_phy_rach_cfg(const rach_cfg_common_s& asn1_type, srsran_prach_cfg_t* p
return true;
};
bool fill_rach_cfg_common(const srsran_prach_cfg_t& prach_cfg, asn1::rrc_nr::rach_cfg_common_s& asn1_type)
{
asn1_type = {};
// rach-ConfigGeneric
asn1_type.rach_cfg_generic.prach_cfg_idx = prach_cfg.config_idx;
asn1_type.rach_cfg_generic.msg1_fdm.value = rach_cfg_generic_s::msg1_fdm_opts::one;
asn1_type.rach_cfg_generic.msg1_freq_start = prach_cfg.freq_offset;
asn1_type.rach_cfg_generic.zero_correlation_zone_cfg = prach_cfg.zero_corr_zone;
asn1_type.rach_cfg_generic.preamb_rx_target_pwr = -110;
asn1_type.rach_cfg_generic.preamb_trans_max.value = rach_cfg_generic_s::preamb_trans_max_opts::n7;
asn1_type.rach_cfg_generic.pwr_ramp_step.value = rach_cfg_generic_s::pwr_ramp_step_opts::db4;
asn1_type.rach_cfg_generic.ra_resp_win.value = rach_cfg_generic_s::ra_resp_win_opts::sl10;
// totalNumberOfRA-Preambles
if (prach_cfg.num_ra_preambles != 64) {
asn1_type.total_nof_ra_preambs_present = true;
asn1_type.total_nof_ra_preambs = prach_cfg.num_ra_preambles;
}
// ssb-perRACH-OccasionAndCB-PreamblesPerSSB
asn1_type.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present = true;
if (not asn1::number_to_enum(asn1_type.ssb_per_rach_occasion_and_cb_preambs_per_ssb.set_one(),
prach_cfg.num_ra_preambles)) {
asn1::log_error("Invalid number of RA preambles=%d", prach_cfg.num_ra_preambles);
return false;
}
asn1_type.ra_contention_resolution_timer.value = rach_cfg_common_s::ra_contention_resolution_timer_opts::sf64;
asn1_type.prach_root_seq_idx.set_l839() = prach_cfg.root_seq_idx;
asn1_type.restricted_set_cfg.value = rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set;
return true;
}
bool make_phy_tdd_cfg(const tdd_ul_dl_cfg_common_s& tdd_ul_dl_cfg_common,
srsran_duplex_config_nr_t* in_srsran_duplex_config_nr)
{
@ -1501,6 +1546,91 @@ static inline void make_ssb_positions_in_burst(const bitstring_t&
}
}
void fill_ssb_pos_in_burst(const asn1::rrc_nr::serving_cell_cfg_common_sib_s& cfg, phy_cfg_nr_t::ssb_cfg_t* out_ssb)
{
auto& ssb_pos = cfg.ssb_positions_in_burst;
out_ssb->position_in_burst = {};
uint32_t N = ssb_pos.in_one_group.length();
for (uint32_t i = 0; i < N; ++i) {
out_ssb->position_in_burst[i] = ssb_pos.in_one_group.get(i);
}
if (ssb_pos.group_presence_present) {
for (uint32_t i = 1; i < ssb_pos.group_presence.length(); ++i) {
if (ssb_pos.group_presence.get(i)) {
std::copy(out_ssb->position_in_burst.begin(),
out_ssb->position_in_burst.begin() + N,
out_ssb->position_in_burst.begin() + i * N);
}
}
}
}
bool fill_ssb_pattern_scs(const srsran_carrier_nr_t& carrier,
srsran_ssb_pattern_t* pattern,
srsran_subcarrier_spacing_t* ssb_scs)
{
srsran::srsran_band_helper bands;
uint16_t band = bands.get_band_from_dl_freq_Hz(carrier.ssb_center_freq_hz);
if (band == UINT16_MAX) {
asn1::log_error("Invalid band for SSB frequency %.3f MHz", carrier.ssb_center_freq_hz);
return false;
}
// TODO: Generalize conversion for other SCS
*pattern = bands.get_ssb_pattern(band, srsran_subcarrier_spacing_15kHz);
if (*pattern == SRSRAN_SSB_PATTERN_A) {
*ssb_scs = carrier.scs;
} else {
// try to optain SSB pattern for same band with 30kHz SCS
*pattern = bands.get_ssb_pattern(band, srsran_subcarrier_spacing_30kHz);
if (*pattern == SRSRAN_SSB_PATTERN_B || *pattern == SRSRAN_SSB_PATTERN_C) {
// SSB SCS is 30 kHz
*ssb_scs = srsran_subcarrier_spacing_30kHz;
} else {
asn1::log_error("Can't derive SSB pattern from band %d", band);
return false;
}
}
return true;
}
bool fill_phy_ssb_cfg(const srsran_carrier_nr_t& carrier,
const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg,
srsran_ssb_cfg_t* out_ssb)
{
*out_ssb = {};
out_ssb->center_freq_hz = carrier.dl_center_frequency_hz;
out_ssb->ssb_freq_hz = carrier.ssb_center_freq_hz;
if (not fill_ssb_pattern_scs(carrier, &out_ssb->pattern, &out_ssb->scs)) {
return false;
}
out_ssb->duplex_mode = SRSRAN_DUPLEX_MODE_FDD;
if (serv_cell_cfg.tdd_ul_dl_cfg_common_present) {
out_ssb->duplex_mode = SRSRAN_DUPLEX_MODE_TDD;
}
out_ssb->periodicity_ms = serv_cell_cfg.ssb_periodicity_serving_cell.to_number();
return true;
}
// SA case
bool fill_phy_ssb_cfg(const srsran_carrier_nr_t& carrier,
const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg,
phy_cfg_nr_t::ssb_cfg_t* out_ssb)
{
// Derive SSB pattern and SCS
if (not fill_ssb_pattern_scs(carrier, &out_ssb->pattern, &out_ssb->scs)) {
return false;
}
fill_ssb_pos_in_burst(serv_cell_cfg, out_ssb);
out_ssb->periodicity_ms = (uint32_t)serv_cell_cfg.ssb_periodicity_serving_cell.to_number();
return true;
}
bool make_phy_ssb_cfg(const srsran_carrier_nr_t& carrier,
const asn1::rrc_nr::serving_cell_cfg_common_s& serv_cell_cfg,
phy_cfg_nr_t::ssb_cfg_t* out_ssb)
@ -1697,7 +1827,7 @@ bool fill_phy_pdcch_cfg(const asn1::rrc_nr::pdcch_cfg_s& pdcch_cfg, srsran_pdcch
return true;
}
void fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch)
bool fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg, srsran_pdcch_cfg_nr_t* pdcch)
{
if (pdcch_cfg.common_ctrl_res_set_present) {
pdcch->coreset_present[pdcch_cfg.common_ctrl_res_set.ctrl_res_set_id] = true;
@ -1705,12 +1835,19 @@ void fill_phy_pdcch_cfg_common(const asn1::rrc_nr::pdcch_cfg_common_s& pdcch_cfg
}
for (const search_space_s& ss : pdcch_cfg.common_search_space_list) {
pdcch->search_space_present[ss.search_space_id] = true;
make_phy_search_space_cfg(ss, &pdcch->search_space[ss.search_space_id]);
if (not make_phy_search_space_cfg(ss, &pdcch->search_space[ss.search_space_id])) {
asn1::log_error("Failed to convert SearchSpace Configuration");
return false;
}
if (pdcch_cfg.ra_search_space_present and pdcch_cfg.ra_search_space == ss.search_space_id) {
pdcch->ra_search_space_present = true;
pdcch->ra_search_space = pdcch->search_space[ss.search_space_id];
pdcch->ra_search_space.type = srsran_search_space_type_common_1;
pdcch->ra_search_space.nof_formats = 1;
pdcch->ra_search_space.formats[0] = srsran_dci_format_nr_1_0;
}
}
return true;
}
void fill_phy_pucch_cfg_common(const asn1::rrc_nr::pucch_cfg_common_s& pucch_cfg, srsran_pucch_nr_common_cfg_t* pucch)
@ -1747,9 +1884,7 @@ bool fill_phy_pucch_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch
}
// 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++) {
for (size_t n = 0; n < pucch_cfg.res_set_to_add_mod_list.size(); 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) {
@ -1797,6 +1932,32 @@ bool fill_phy_pucch_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch
}
}
// configure scheduling request resources
return fill_phy_pucch_hl_cfg(pucch_cfg, pucch);
}
bool fill_phy_pucch_hl_cfg(const asn1::rrc_nr::pucch_cfg_s& pucch_cfg, srsran_pucch_nr_hl_cfg_t* pucch)
{
for (size_t n = 0; n < pucch_cfg.sched_request_res_to_add_mod_list.size(); n++) {
// fill each sr_resource's cnf
auto& asn_sr_res = pucch_cfg.sched_request_res_to_add_mod_list[n];
srsran_pucch_nr_sr_resource_t* sr_res = &pucch->sr_resources[asn_sr_res.sched_request_res_id];
make_phy_sr_resource(asn_sr_res, sr_res);
// get the pucch_resource from pucch_cfg.res_to_add_mod_list and copy it into the sr_resouce.resource
const auto& asn1_pucch_resource = pucch_cfg.res_to_add_mod_list[asn_sr_res.res];
auto& pucch_resource = sr_res->resource;
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_pucch_resource, format2_rate, &pucch_resource)) {
return false;
}
}
return true;
}

@ -175,7 +175,7 @@ uint32_t srsran_band_helper::get_abs_freq_ssb_arfcn(uint16_t
return find_lower_bound_abs_freq_ssb(band, scs, freq_point_a_hz + coreset0_offset_hz);
}
srsran_ssb_patern_t srsran_band_helper::get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs)
srsran_ssb_pattern_t srsran_band_helper::get_ssb_pattern(uint16_t band, srsran_subcarrier_spacing_t scs)
{
// Look for the given band and SCS
for (const nr_band_ss_raster& ss_raster : nr_band_ss_raster_table) {

@ -35,10 +35,15 @@ static void emergency_cleanup_handler(void* data)
mac_pcap_base::mac_pcap_base() : logger(srslog::fetch_basic_logger("MAC")), thread("PCAP_WRITER_MAC")
{
add_emergency_cleanup_handler(emergency_cleanup_handler, this);
emergency_handler_id = add_emergency_cleanup_handler(emergency_cleanup_handler, this);
}
mac_pcap_base::~mac_pcap_base() {}
mac_pcap_base::~mac_pcap_base()
{
if (emergency_handler_id > 0) {
remove_emergency_cleanup_handler(emergency_handler_id);
}
}
void mac_pcap_base::enable(bool enable_)
{

@ -35,7 +35,14 @@ static void emergency_cleanup_handler(void* data)
nas_pcap::nas_pcap()
{
add_emergency_cleanup_handler(emergency_cleanup_handler, this);
emergency_handler_id = add_emergency_cleanup_handler(emergency_cleanup_handler, this);
}
nas_pcap::~nas_pcap()
{
if (emergency_handler_id > 0) {
remove_emergency_cleanup_handler(emergency_handler_id);
}
}
void nas_pcap::enable()

@ -35,7 +35,14 @@ static void emergency_cleanup_handler(void* data)
ngap_pcap::ngap_pcap()
{
add_emergency_cleanup_handler(emergency_cleanup_handler, this);
emergency_handler_id = add_emergency_cleanup_handler(emergency_cleanup_handler, this);
}
ngap_pcap::~ngap_pcap()
{
if (emergency_handler_id > 0) {
remove_emergency_cleanup_handler(emergency_handler_id);
}
}
void ngap_pcap::enable()

@ -35,7 +35,14 @@ static void emergency_cleanup_handler(void* data)
s1ap_pcap::s1ap_pcap()
{
add_emergency_cleanup_handler(emergency_cleanup_handler, this);
emergency_handler_id = add_emergency_cleanup_handler(emergency_cleanup_handler, this);
}
s1ap_pcap::~s1ap_pcap()
{
if (emergency_handler_id > 0) {
remove_emergency_cleanup_handler(emergency_handler_id);
}
}
void s1ap_pcap::enable()

@ -107,7 +107,8 @@ int32_t mac_sch_subpdu_nr::read_subheader(const uint8_t* ptr)
void mac_sch_subpdu_nr::set_sdu(const uint32_t lcid_, const uint8_t* payload_, const uint32_t len_)
{
lcid = lcid_;
// Use CCCH_SIZE_48 when SDU len fits
lcid = (lcid_ == CCCH_SIZE_64 && len_ == sizeof_ce(CCCH_SIZE_48, true)) ? CCCH_SIZE_48 : lcid_;
sdu.set_storage_to(const_cast<uint8_t*>(payload_));
header_length = is_ul_ccch() ? 1 : 2;
sdu_length = len_;

@ -371,7 +371,7 @@ void pdcp::get_metrics(pdcp_metrics_t& m, const uint32_t nof_tti)
double rx_rate_mbps = (nof_tti > 0) ? ((metrics.num_rx_pdu_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0;
double tx_rate_mbps = (nof_tti > 0) ? ((metrics.num_tx_pdu_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0;
logger.info("lcid=%d, rx_rate_mbps=%4.2f (real=%4.2f), tx_rate_mbps=%4.2f (real=%4.2f)",
logger.debug("lcid=%d, rx_rate_mbps=%4.2f (real=%4.2f), tx_rate_mbps=%4.2f (real=%4.2f)",
it->first,
rx_rate_mbps,
rx_rate_mbps_real_time,

@ -36,6 +36,7 @@ add_subdirectory(scrambling)
add_subdirectory(ue)
add_subdirectory(enb)
add_subdirectory(gnb)
add_subdirectory(cfr)
set(srsran_srcs $<TARGET_OBJECTS:srsran_agc>
$<TARGET_OBJECTS:srsran_ch_estimation>
$<TARGET_OBJECTS:srsran_phy_common>
@ -53,6 +54,7 @@ set(srsran_srcs $<TARGET_OBJECTS:srsran_agc>
$<TARGET_OBJECTS:srsran_ue>
$<TARGET_OBJECTS:srsran_enb>
$<TARGET_OBJECTS:srsran_gnb>
$<TARGET_OBJECTS:srsran_cfr>
)
add_library(srsran_phy STATIC ${srsran_srcs} )

@ -0,0 +1,13 @@
#
# Copyright 2013-2021 Software Radio Systems Limited
#
# By using this file, you agree to the terms and conditions set
# forth in the LICENSE file which can be found at the top level of
# the distribution.
#
set(SRCS cfr.c)
add_library(srsran_cfr OBJECT ${SRCS})
add_subdirectory(test)

@ -0,0 +1,367 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsran/phy/cfr/cfr.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
// Uncomment this to use a literal implementation of the CFR algorithm
// #define CFR_PEAK_EXTRACTION
// Uncomment this to filter by zeroing the FFT bins instead of applying a frequency window
#define CFR_LPF_WITH_ZEROS
static inline float cfr_symb_peak(float* in_abs, int len);
void srsran_cfr_process(srsran_cfr_t* q, cf_t* in, cf_t* out)
{
if (q == NULL || in == NULL || out == NULL) {
return;
}
if (!q->cfg.cfr_enable) {
// If no processing, copy the input samples into the output buffer
if (in != out) {
srsran_vec_cf_copy(out, in, q->cfg.symbol_sz);
}
return;
}
const float alpha = q->cfg.alpha;
const uint32_t symbol_sz = q->cfg.symbol_sz;
float beta = 0.0f;
// Calculate absolute input values
srsran_vec_abs_cf(in, q->abs_buffer_in, symbol_sz);
// In auto modes, the beta threshold is calculated based on the measured PAPR
if (q->cfg.cfr_mode == SRSRAN_CFR_THR_MANUAL) {
beta = q->cfg.manual_thr;
} else {
const float symb_peak = cfr_symb_peak(q->abs_buffer_in, q->cfg.symbol_sz);
const float pwr_symb_peak = symb_peak * symb_peak;
const float pwr_symb_avg = srsran_vec_avg_power_ff(q->abs_buffer_in, q->cfg.symbol_sz);
float symb_papr = 0.0f;
if (isnormal(pwr_symb_avg) && isnormal(pwr_symb_peak)) {
if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_CMA) {
// Once cma_n reaches its max value, stop incrementing to prevent overflow.
// This turns the averaging into a de-facto EMA with an extremely slow time constant
q->pwr_avg_in = SRSRAN_VEC_CMA(pwr_symb_avg, q->pwr_avg_in, q->cma_n++);
q->cma_n = q->cma_n & UINT64_MAX ? q->cma_n : UINT64_MAX;
} else if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_EMA) {
q->pwr_avg_in = SRSRAN_VEC_EMA(pwr_symb_avg, q->pwr_avg_in, q->cfg.ema_alpha);
}
symb_papr = pwr_symb_peak / q->pwr_avg_in;
}
float papr_reduction = symb_papr / q->max_papr_lin;
beta = (papr_reduction > 1) ? symb_peak / sqrtf(papr_reduction) : 0;
}
// Clipping algorithm
if (isnormal(beta)) {
#ifdef CFR_PEAK_EXTRACTION
srsran_vec_cf_zero(q->peak_buffer, symbol_sz);
cf_t clip_thr = 0;
for (int i = 0; i < symbol_sz; i++) {
if (q->abs_buffer_in[i] > beta) {
clip_thr = beta * (in[i] / q->abs_buffer_in[i]);
q->peak_buffer[i] = in[i] - clip_thr;
}
}
// Apply FFT filter to the peak signal
srsran_dft_run_c(&q->fft_plan, q->peak_buffer, q->peak_buffer);
#ifdef CFR_LPF_WITH_ZEROS
srsran_vec_cf_zero(q->peak_buffer + q->lpf_bw / 2 + q->cfg.dc_sc, symbol_sz - q->cfg.symbol_bw - q->cfg.dc_sc);
#else /* CFR_LPF_WITH_ZEROS */
srsran_vec_prod_cfc(q->peak_buffer, q->lpf_spectrum, q->peak_buffer, symbol_sz);
#endif /* CFR_LPF_WITH_ZEROS */
srsran_dft_run_c(&q->ifft_plan, q->peak_buffer, q->peak_buffer);
// Scale the peak signal according to alpha
srsran_vec_sc_prod_cfc(q->peak_buffer, alpha, q->peak_buffer, symbol_sz);
// Apply the filtered clipping
srsran_vec_sub_ccc(in, q->peak_buffer, out, symbol_sz);
#else /* CFR_PEAK_EXTRACTION */
// Generate a clipping envelope and clip the signal
srsran_vec_gen_clip_env(q->abs_buffer_in, beta, alpha, q->abs_buffer_in, symbol_sz);
srsran_vec_prod_cfc(in, q->abs_buffer_in, out, symbol_sz);
// FFT filter
srsran_dft_run_c(&q->fft_plan, out, out);
#ifdef CFR_LPF_WITH_ZEROS
srsran_vec_cf_zero(out + q->lpf_bw / 2 + q->cfg.dc_sc, symbol_sz - q->cfg.symbol_bw - q->cfg.dc_sc);
#else /* CFR_LPF_WITH_ZEROS */
srsran_vec_prod_cfc(out, q->lpf_spectrum, out, symbol_sz);
#endif /* CFR_LPF_WITH_ZEROS */
srsran_dft_run_c(&q->ifft_plan, out, out);
#endif /* CFR_PEAK_EXTRACTION */
} else {
// If no processing, copy the input samples into the output buffer
if (in != out) {
srsran_vec_cf_copy(out, in, symbol_sz);
}
}
if (q->cfg.cfr_mode != SRSRAN_CFR_THR_MANUAL && q->cfg.measure_out_papr) {
srsran_vec_abs_cf(in, q->abs_buffer_out, symbol_sz);
const float symb_peak = cfr_symb_peak(q->abs_buffer_out, q->cfg.symbol_sz);
const float pwr_symb_peak = symb_peak * symb_peak;
const float pwr_symb_avg = srsran_vec_avg_power_ff(q->abs_buffer_out, q->cfg.symbol_sz);
float symb_papr = 0.0f;
if (isnormal(pwr_symb_avg) && isnormal(pwr_symb_peak)) {
if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_CMA) {
// Do not increment cma_n here, as it is being done when calculating input PAPR
q->pwr_avg_out = SRSRAN_VEC_CMA(pwr_symb_avg, q->pwr_avg_out, q->cma_n);
}
else if (q->cfg.cfr_mode == SRSRAN_CFR_THR_AUTO_EMA) {
q->pwr_avg_out = SRSRAN_VEC_EMA(pwr_symb_avg, q->pwr_avg_out, q->cfg.ema_alpha);
}
symb_papr = pwr_symb_peak / q->pwr_avg_out;
}
const float papr_out_db = srsran_convert_power_to_dB(symb_papr);
printf("Output PAPR: %f dB\n", papr_out_db);
}
}
int srsran_cfr_init(srsran_cfr_t* q, srsran_cfr_cfg_t* cfg)
{
int ret = SRSRAN_ERROR;
if (q == NULL || cfg == NULL) {
ERROR("Error, invalid inputs");
ret = SRSRAN_ERROR_INVALID_INPUTS;
goto clean_exit;
}
if (!cfg->symbol_sz || !cfg->symbol_bw || cfg->alpha < 0 || cfg->alpha > 1) {
ERROR("Error, invalid configuration");
goto clean_exit;
}
if (cfg->cfr_mode == SRSRAN_CFR_THR_MANUAL && cfg->manual_thr <= 0) {
ERROR("Error, invalid configuration for manual threshold");
goto clean_exit;
}
if (cfg->cfr_mode == SRSRAN_CFR_THR_AUTO_CMA && (cfg->max_papr_db <= 0)) {
ERROR("Error, invalid configuration for CMA averaging");
goto clean_exit;
}
if (cfg->cfr_mode == SRSRAN_CFR_THR_AUTO_EMA &&
(cfg->max_papr_db <= 0 || (cfg->ema_alpha < 0 || cfg->ema_alpha > 1))) {
ERROR("Error, invalid configuration for EMA averaging");
goto clean_exit;
}
// Copy all the configuration parameters
q->cfg = *cfg;
q->max_papr_lin = srsran_convert_dB_to_power(q->cfg.max_papr_db);
q->pwr_avg_in = CFR_EMA_INIT_AVG_PWR;
q->cma_n = 0;
if (q->cfg.measure_out_papr) {
q->pwr_avg_out = CFR_EMA_INIT_AVG_PWR;
}
if (q->abs_buffer_in) {
free(q->abs_buffer_in);
}
q->abs_buffer_in = srsran_vec_f_malloc(q->cfg.symbol_sz);
if (!q->abs_buffer_in) {
ERROR("Error allocating abs_buffer_in");
goto clean_exit;
}
if (q->abs_buffer_out) {
free(q->abs_buffer_out);
}
q->abs_buffer_out = srsran_vec_f_malloc(q->cfg.symbol_sz);
if (!q->abs_buffer_out) {
ERROR("Error allocating abs_buffer_out");
goto clean_exit;
}
if (q->peak_buffer) {
free(q->peak_buffer);
}
q->peak_buffer = srsran_vec_cf_malloc(q->cfg.symbol_sz);
if (!q->peak_buffer) {
ERROR("Error allocating peak_buffer");
goto clean_exit;
}
// Allocate the filter
if (q->lpf_spectrum) {
free(q->lpf_spectrum);
}
q->lpf_spectrum = srsran_vec_f_malloc(q->cfg.symbol_sz);
if (!q->lpf_spectrum) {
ERROR("Error allocating lpf_spectrum");
goto clean_exit;
}
// The LPF bandwidth is exactly the OFDM symbol bandwidth, in number of FFT bins
q->lpf_bw = q->cfg.symbol_bw;
// Initialise the filter
srsran_vec_f_zero(q->lpf_spectrum, q->cfg.symbol_sz);
// DC subcarrier is in position 0, so the OFDM symbol can go from index 1 to q->lpf_bw / 2 + 1
for (uint32_t i = 0; i < q->lpf_bw / 2 + q->cfg.dc_sc; i++) {
q->lpf_spectrum[i] = 1;
}
for (uint32_t i = q->cfg.symbol_sz - q->lpf_bw / 2; i < q->cfg.symbol_sz; i++) {
q->lpf_spectrum[i] = 1;
}
// FFT plans, for 1 OFDM symbol
if (q->fft_plan.size) {
// Replan if it was initialised previously with bigger FFT size
if (q->fft_plan.size >= q->cfg.symbol_sz) {
if (srsran_dft_replan(&q->fft_plan, q->cfg.symbol_sz)) {
ERROR("Replaning DFT plan");
goto clean_exit;
}
} else {
srsran_dft_plan_free(&q->fft_plan);
if (srsran_dft_plan_c(&q->fft_plan, q->cfg.symbol_sz, SRSRAN_DFT_FORWARD)) {
ERROR("Creating DFT plan");
goto clean_exit;
}
}
} else {
// Create plan from zero otherwise
if (srsran_dft_plan_c(&q->fft_plan, q->cfg.symbol_sz, SRSRAN_DFT_FORWARD)) {
ERROR("Creating DFT plan");
goto clean_exit;
}
}
if (q->ifft_plan.size) {
if (q->ifft_plan.size >= q->cfg.symbol_sz) {
// Replan if it was initialised previously with bigger FFT size
if (srsran_dft_replan(&q->ifft_plan, q->cfg.symbol_sz)) {
ERROR("Replaning DFT plan");
goto clean_exit;
}
} else {
srsran_dft_plan_free(&q->ifft_plan);
if (srsran_dft_plan_c(&q->ifft_plan, q->cfg.symbol_sz, SRSRAN_DFT_BACKWARD)) {
ERROR("Creating DFT plan");
goto clean_exit;
}
}
} else {
// Create plan from zero otherwise
if (srsran_dft_plan_c(&q->ifft_plan, q->cfg.symbol_sz, SRSRAN_DFT_BACKWARD)) {
ERROR("Creating DFT plan");
goto clean_exit;
}
}
srsran_dft_plan_set_norm(&q->fft_plan, true);
srsran_dft_plan_set_norm(&q->ifft_plan, true);
srsran_vec_cf_zero(q->peak_buffer, q->cfg.symbol_sz);
srsran_vec_f_zero(q->abs_buffer_in, q->cfg.symbol_sz);
srsran_vec_f_zero(q->abs_buffer_out, q->cfg.symbol_sz);
ret = SRSRAN_SUCCESS;
clean_exit:
if (ret < SRSRAN_SUCCESS) {
srsran_cfr_free(q);
}
return ret;
}
void srsran_cfr_free(srsran_cfr_t* q)
{
if (q) {
srsran_dft_plan_free(&q->fft_plan);
srsran_dft_plan_free(&q->ifft_plan);
if (q->abs_buffer_in) {
free(q->abs_buffer_in);
}
if (q->abs_buffer_out) {
free(q->abs_buffer_out);
}
if (q->peak_buffer) {
free(q->peak_buffer);
}
if (q->lpf_spectrum) {
free(q->lpf_spectrum);
}
SRSRAN_MEM_ZERO(q, srsran_cfr_t, 1);
}
}
// Find the peak absolute value of an OFDM symbol
static inline float cfr_symb_peak(float* in_abs, int len)
{
const uint32_t max_index = srsran_vec_max_fi(in_abs, len);
return in_abs[max_index];
}
bool srsran_cfr_params_valid(srsran_cfr_cfg_t* cfr_conf)
{
if (cfr_conf == NULL) {
return false;
}
if (cfr_conf->alpha < 0 || cfr_conf->alpha > 1) {
return false;
}
if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_MANUAL && cfr_conf->manual_thr <= 0) {
return false;
}
if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_AUTO_CMA && (cfr_conf->max_papr_db <= 0)) {
return false;
}
if (cfr_conf->cfr_mode == SRSRAN_CFR_THR_AUTO_EMA &&
(cfr_conf->max_papr_db <= 0 || (cfr_conf->ema_alpha < 0 || cfr_conf->ema_alpha > 1))) {
return false;
}
return true;
}
int srsran_cfr_set_threshold(srsran_cfr_t* q, float thres)
{
if (q == NULL) {
ERROR("Invalid CFR object");
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (thres <= 0.0f) {
ERROR("Invalid CFR threshold");
return SRSRAN_ERROR;
}
q->cfg.manual_thr = thres;
return SRSRAN_SUCCESS;
}
int srsran_cfr_set_papr(srsran_cfr_t* q, float papr)
{
if (q == NULL) {
ERROR("Invalid CFR object");
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (papr <= 0.0f) {
ERROR("Invalid CFR configuration");
return SRSRAN_ERROR;
}
q->cfg.max_papr_db = papr;
q->max_papr_lin = srsran_convert_dB_to_power(q->cfg.max_papr_db);
return SRSRAN_SUCCESS;
}

@ -0,0 +1,17 @@
#
# 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.
#
########################################################################
# CFR Test
########################################################################
add_executable(cfr_test cfr_test.c)
target_link_libraries(cfr_test srsran_phy)
add_test(cfr_test_default cfr_test)

@ -0,0 +1,308 @@
/**
*
* \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 <math.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include "srsran/phy/utils/random.h"
#include "srsran/srsran.h"
#define MAX_ACPR_DB -100
// CFR type test args
static char cfr_manual_str[] = "manual";
static char cfr_auto_cma_str[] = "auto_cma";
static char cfr_auto_ema_str[] = "auto_ema";
// Default CFR type
static char* cfr_type_arg = cfr_manual_str;
static int nof_prb = -1;
static srsran_cp_t cp = SRSRAN_CP_NORM;
static int nof_repetitions = 1;
static int nof_frames = 10;
static srsran_cfr_mode_t cfr_mode = SRSRAN_CFR_THR_MANUAL;
static float alpha = 1.0f;
static bool dc_empty = true;
static float thr_manual = 1.5f;
static float max_papr_db = 8.0f;
static float ema_alpha = (float)1 / (float)SRSRAN_CP_NORM_NSYMB;
static uint32_t force_symbol_sz = 0;
static double elapsed_us(struct timeval* ts_start, struct timeval* ts_end)
{
if (ts_end->tv_usec > ts_start->tv_usec) {
return ((double)ts_end->tv_sec - (double)ts_start->tv_sec) * 1000000 + (double)ts_end->tv_usec -
(double)ts_start->tv_usec;
} else {
return ((double)ts_end->tv_sec - (double)ts_start->tv_sec - 1) * 1000000 + ((double)ts_end->tv_usec + 1000000) -
(double)ts_start->tv_usec;
}
}
static void usage(char* prog)
{
printf("Usage: %s\n", prog);
printf("\t-N Force symbol size, 0 for auto [Default %d]\n", force_symbol_sz);
printf("\t-n Force number of Resource blocks [Default All]\n");
printf("\t-e extended cyclic prefix [Default Normal]\n");
printf("\t-f Number of frames [Default %d]\n", nof_frames);
printf("\t-r Number of repetitions [Default %d]\n", nof_repetitions);
printf("\t-m CFR mode: %s, %s, %s [Default %s]\n", cfr_manual_str, cfr_auto_cma_str, cfr_auto_ema_str, cfr_type_arg);
printf("\t-d Use DC subcarrier: [Default DC empty]\n");
printf("\t-a CFR alpha: [Default %.2f]\n", alpha);
printf("\t-t CFR manual threshold: [Default %.2f]\n", thr_manual);
printf("\t-p CFR Max PAPR in dB (auto modes): [Default %.2f]\n", max_papr_db);
printf("\t-E Power avg EMA alpha (EMA mode): [Default %.2f]\n", ema_alpha);
}
static int parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "NnerfmatdpE")) != -1) {
switch (opt) {
case 'n':
nof_prb = (int)strtol(argv[optind], NULL, 10);
break;
case 'N':
force_symbol_sz = (uint32_t)strtol(argv[optind], NULL, 10);
break;
case 'e':
cp = SRSRAN_CP_EXT;
break;
case 'r':
nof_repetitions = (int)strtol(argv[optind], NULL, 10);
break;
case 'f':
nof_frames = (int)strtol(argv[optind], NULL, 10);
break;
case 'm':
cfr_type_arg = argv[optind];
break;
case 'a':
alpha = strtof(argv[optind], NULL);
break;
case 't':
thr_manual = strtof(argv[optind], NULL);
break;
case 'd':
dc_empty = false;
break;
case 'p':
max_papr_db = strtof(argv[optind], NULL);
break;
case 'E':
ema_alpha = strtof(argv[optind], NULL);
break;
default:
usage(argv[0]);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSRAN_ERROR;
srsran_random_t random_gen = srsran_random_init(0);
struct timeval start, end;
srsran_cfr_t cfr = {};
cf_t* input = NULL;
cf_t* output = NULL;
cf_t* error = NULL;
float* acpr_buff = NULL;
float mse_dB = 0.0f;
float nmse_dB = 0.0f;
float evm = 0.0f;
int max_prb = 0.0f;
float acpr_in_dB = 0.0f;
float acpr_out_dB = 0.0f;
srsran_dft_plan_t ofdm_ifft = {};
srsran_dft_plan_t ofdm_fft = {};
if (parse_args(argc, argv) < SRSRAN_SUCCESS) {
ERROR("Error in parse_args");
goto clean_exit;
}
if (!strcmp(cfr_type_arg, cfr_manual_str)) {
cfr_mode = SRSRAN_CFR_THR_MANUAL;
} else if (!strcmp(cfr_type_arg, cfr_auto_cma_str)) {
cfr_mode = SRSRAN_CFR_THR_AUTO_CMA;
} else if (!strcmp(cfr_type_arg, cfr_auto_ema_str)) {
cfr_mode = SRSRAN_CFR_THR_AUTO_EMA;
} else {
ERROR("CFR mode is not recognised");
goto clean_exit;
}
if (nof_prb == -1) {
nof_prb = 6;
max_prb = SRSRAN_MAX_PRB;
} else {
max_prb = nof_prb;
}
while (nof_prb <= max_prb) {
const uint32_t symbol_sz = (force_symbol_sz) ? force_symbol_sz : (uint32_t)srsran_symbol_sz(nof_prb);
const uint32_t symbol_bw = nof_prb * SRSRAN_NRE;
const uint32_t nof_symb_slot = SRSRAN_CP_NSYMB(cp);
const uint32_t nof_symb_frame = nof_symb_slot * SRSRAN_NOF_SLOTS_PER_SF * SRSRAN_NOF_SF_X_FRAME;
const uint32_t frame_sz = symbol_sz * nof_symb_frame;
const uint32_t total_nof_re = frame_sz * nof_frames;
const uint32_t total_nof_symb = nof_symb_frame * nof_frames;
printf("Running test for %d PRB, %d Frames: \t", nof_prb, nof_frames);
fflush(stdout);
input = srsran_vec_cf_malloc(total_nof_re);
output = srsran_vec_cf_malloc(total_nof_re);
error = srsran_vec_cf_malloc(total_nof_re);
acpr_buff = srsran_vec_f_malloc(total_nof_symb);
if (!input || !output || !error || !acpr_buff) {
perror("malloc");
goto clean_exit;
}
srsran_vec_cf_zero(input, total_nof_re);
srsran_vec_cf_zero(output, total_nof_re);
srsran_vec_cf_zero(error, total_nof_re);
srsran_vec_f_zero(acpr_buff, total_nof_symb);
// Set the parameters for the CFR.
srsran_cfr_cfg_t cfr_tx_cfg = {};
cfr_tx_cfg.cfr_enable = true;
cfr_tx_cfg.symbol_sz = symbol_sz;
cfr_tx_cfg.symbol_bw = nof_prb * SRSRAN_NRE;
cfr_tx_cfg.cfr_mode = cfr_mode;
cfr_tx_cfg.max_papr_db = max_papr_db;
cfr_tx_cfg.alpha = alpha;
cfr_tx_cfg.manual_thr = thr_manual;
cfr_tx_cfg.ema_alpha = ema_alpha;
cfr_tx_cfg.dc_sc = dc_empty;
if (srsran_cfr_init(&cfr, &cfr_tx_cfg)) {
ERROR("Error initializing CFR");
goto clean_exit;
}
if (srsran_dft_plan_c(&ofdm_ifft, (int)symbol_sz, SRSRAN_DFT_BACKWARD)) {
ERROR("Creating IFFT plan");
goto clean_exit;
}
srsran_dft_plan_set_norm(&ofdm_ifft, true);
if (srsran_dft_plan_c(&ofdm_fft, (int)symbol_sz, SRSRAN_DFT_FORWARD)) {
ERROR("Creating FFT plan");
goto clean_exit;
}
srsran_dft_plan_set_norm(&ofdm_fft, true);
// Generate Random data
cf_t* ofdm_symb = NULL;
for (int i = 0; i < total_nof_symb; i++) {
ofdm_symb = input + i * symbol_sz;
srsran_random_uniform_complex_dist_vector(random_gen, ofdm_symb + dc_empty, symbol_bw / 2, -1.0f, +1.0f);
srsran_random_uniform_complex_dist_vector(
random_gen, ofdm_symb + symbol_sz - symbol_bw / 2, symbol_bw / 2, -1.0f, +1.0f);
acpr_buff[i] = srsran_vec_acpr_c(ofdm_symb, symbol_bw / 2 + dc_empty, symbol_bw / 2, symbol_sz);
srsran_dft_run_c(&ofdm_ifft, ofdm_symb, ofdm_symb);
}
// compute the average intput ACPR
acpr_in_dB = srsran_vec_acc_ff(acpr_buff, total_nof_symb) / (float)total_nof_symb;
acpr_in_dB = srsran_convert_power_to_dB(acpr_in_dB);
// Execute CFR
gettimeofday(&start, NULL);
for (uint32_t i = 0; i < nof_repetitions; i++) {
for (uint32_t j = 0; j < nof_frames; j++) {
for (uint32_t k = 0; k < nof_symb_frame; k++) {
srsran_cfr_process(&cfr,
input + (size_t)((k * symbol_sz) + (j * frame_sz)),
output + (size_t)((k * symbol_sz) + (j * frame_sz)));
}
}
}
gettimeofday(&end, NULL);
printf("%.1fMsps \t", (float)(total_nof_re * nof_repetitions) / elapsed_us(&start, &end));
// Compute metrics
srsran_vec_sub_ccc(input, output, error, total_nof_re);
float power_in = srsran_vec_avg_power_cf(input, total_nof_re);
float power_err = srsran_vec_avg_power_cf(error, total_nof_re);
mse_dB = srsran_convert_power_to_dB(power_err);
nmse_dB = srsran_convert_power_to_dB(power_err / power_in);
evm = 100 * sqrtf(power_err / power_in);
float snr_dB = srsran_convert_power_to_dB(power_in / power_err);
float papr_in = srsran_convert_power_to_dB(srsran_vec_papr_c(input, total_nof_re));
float papr_out = srsran_convert_power_to_dB(srsran_vec_papr_c(output, total_nof_re));
ofdm_symb = NULL;
for (int i = 0; i < total_nof_symb; i++) {
ofdm_symb = output + i * symbol_sz;
srsran_dft_run_c(&ofdm_fft, ofdm_symb, ofdm_symb);
acpr_buff[i] = srsran_vec_acpr_c(ofdm_symb, symbol_bw / 2 + dc_empty, symbol_bw / 2, symbol_sz);
}
// Compute the output average ACPR
acpr_out_dB = srsran_vec_acc_ff(acpr_buff, total_nof_symb) / (float)total_nof_symb;
acpr_out_dB = srsran_convert_power_to_dB(acpr_out_dB);
printf("MSE=%.3fdB NMSE=%.3fdB EVM=%.3f%% SNR=%.3fdB", mse_dB, nmse_dB, evm, snr_dB);
printf(" In-PAPR=%.3fdB Out-PAPR=%.3fdB", papr_in, papr_out);
printf(" In-ACPR=%.3fdB Out-ACPR=%.3fdB\n", acpr_in_dB, acpr_out_dB);
srsran_dft_plan_free(&ofdm_ifft);
srsran_dft_plan_free(&ofdm_fft);
free(input);
free(output);
free(error);
free(acpr_buff);
input = NULL;
output = NULL;
error = NULL;
acpr_buff = NULL;
++nof_prb;
if (acpr_out_dB > MAX_ACPR_DB) {
printf("ACPR too large \n");
goto clean_exit;
}
}
ret = SRSRAN_SUCCESS;
// Free resources
clean_exit:
srsran_random_free(random_gen);
srsran_cfr_free(&cfr);
srsran_dft_plan_free(&ofdm_ifft);
srsran_dft_plan_free(&ofdm_fft);
if (input) {
free(input);
}
if (output) {
free(output);
}
if (error) {
free(error);
}
if (acpr_buff) {
free(acpr_buff);
}
return ret;
}

@ -784,7 +784,7 @@ int srsran_coreset0_ssb_offset(uint32_t idx, srsran_subcarrier_spacing_t ssb_scs
return entry->offset_rb;
}
const char* srsran_ssb_pattern_to_str(srsran_ssb_patern_t pattern)
const char* srsran_ssb_pattern_to_str(srsran_ssb_pattern_t pattern)
{
switch (pattern) {
case SRSRAN_SSB_PATTERN_A:
@ -804,7 +804,7 @@ const char* srsran_ssb_pattern_to_str(srsran_ssb_patern_t pattern)
return "Invalid";
}
srsran_ssb_patern_t srsran_ssb_pattern_fom_str(const char* str)
srsran_ssb_pattern_t srsran_ssb_pattern_fom_str(const char* str)
{
if (str == NULL) {
return SRSRAN_SSB_PATTERN_INVALID;

@ -77,6 +77,19 @@ static int ofdm_init_mbsfn_(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg, srsran_dft
q->slot_sz = (uint32_t)SRSRAN_SLOT_LEN(q->cfg.symbol_sz);
q->sf_sz = (uint32_t)SRSRAN_SF_LEN(q->cfg.symbol_sz);
// Set the CFR parameters related to OFDM symbol and FFT size
q->cfg.cfr_tx_cfg.symbol_sz = symbol_sz;
q->cfg.cfr_tx_cfg.symbol_bw = q->nof_re;
// in the DL, the DC carrier is empty but still counts when designing the filter BW
q->cfg.cfr_tx_cfg.dc_sc = (!q->cfg.keep_dc) && (!isnormal(q->cfg.freq_shift_f));
if (q->cfg.cfr_tx_cfg.cfr_enable) {
if (srsran_cfr_init(&q->tx_cfr, &q->cfg.cfr_tx_cfg) < SRSRAN_SUCCESS) {
ERROR("Error while initialising CFR module");
return SRSRAN_ERROR;
}
}
// Plan MBSFN
if (q->fft_plan.size) {
// Replan if it was initialised previously
@ -251,6 +264,7 @@ void srsran_ofdm_free_(srsran_ofdm_t* q)
if (q->window_offset_buffer) {
free(q->window_offset_buffer);
}
srsran_cfr_free(&q->tx_cfr);
SRSRAN_MEM_ZERO(q, srsran_ofdm_t, 1);
}
@ -298,6 +312,10 @@ int srsran_ofdm_tx_init(srsran_ofdm_t* q, srsran_cp_t cp, cf_t* in_buffer, cf_t*
int srsran_ofdm_tx_init_cfg(srsran_ofdm_t* q, srsran_ofdm_cfg_t* cfg)
{
if (q == NULL || cfg == NULL) {
ERROR("Error, invalid inputs");
return SRSRAN_ERROR_INVALID_INPUTS;
}
return ofdm_init_mbsfn_(q, cfg, SRSRAN_DFT_BACKWARD);
}
@ -619,6 +637,11 @@ static void ofdm_tx_slot(srsran_ofdm_t* q, int slot_in_sf)
srsran_vec_sc_prod_cfc(&output[cp_len], norm, &output[cp_len], symbol_sz);
}
// CFR: Process the time-domain signal without the CP
if (q->cfg.cfr_tx_cfg.cfr_enable) {
srsran_cfr_process(&q->tx_cfr, output + cp_len, output + cp_len);
}
/* add CP */
srsran_vec_cf_copy(output, &output[symbol_sz], cp_len);
output += symbol_sz + cp_len;
@ -665,3 +688,37 @@ void srsran_ofdm_tx_sf(srsran_ofdm_t* q)
srsran_vec_prod_ccc(q->cfg.out_buffer, q->shift_buffer, q->cfg.out_buffer, q->sf_sz);
}
}
int srsran_ofdm_set_cfr(srsran_ofdm_t* q, srsran_cfr_cfg_t* cfr)
{
if (q == NULL || cfr == NULL) {
ERROR("Error, invalid inputs");
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->max_prb) {
ERROR("Error, ofdm object not initialised");
return SRSRAN_ERROR;
}
// Check if there is nothing to configure
if (memcmp(&q->cfg.cfr_tx_cfg, cfr, sizeof(srsran_cfr_cfg_t)) == 0) {
return SRSRAN_SUCCESS;
}
// Copy the CFR config into the OFDM object
q->cfg.cfr_tx_cfg = *cfr;
// Set the CFR parameters related to OFDM symbol and FFT size
q->cfg.cfr_tx_cfg.symbol_sz = q->cfg.symbol_sz;
q->cfg.cfr_tx_cfg.symbol_bw = q->nof_re;
// in the DL, the DC carrier is empty but still counts when designing the filter BW
q->cfg.cfr_tx_cfg.dc_sc = (!q->cfg.keep_dc) && (!isnormal(q->cfg.freq_shift_f));
if (q->cfg.cfr_tx_cfg.cfr_enable) {
if (srsran_cfr_init(&q->tx_cfr, &q->cfg.cfr_tx_cfg) < SRSRAN_SUCCESS) {
ERROR("Error while initialising CFR module");
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}

@ -160,6 +160,7 @@ int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell)
ofdm_cfg.in_buffer = q->sf_symbols[i];
ofdm_cfg.out_buffer = q->out_buffer[i];
ofdm_cfg.sf_type = SRSRAN_SF_NORM;
ofdm_cfg.cfr_tx_cfg = q->cfr_config;
if (srsran_ofdm_tx_init_cfg(&q->ifft[i], &ofdm_cfg)) {
ERROR("Error initiating FFT (%d)", i);
return SRSRAN_ERROR;
@ -238,6 +239,31 @@ int srsran_enb_dl_set_cell(srsran_enb_dl_t* q, srsran_cell_t cell)
return ret;
}
int srsran_enb_dl_set_cfr(srsran_enb_dl_t* q, const srsran_cfr_cfg_t* cfr)
{
if (q == NULL || cfr == NULL) {
ERROR("Error, invalid inputs");
return SRSRAN_ERROR_INVALID_INPUTS;
}
// Copy the cfr config into the eNB
q->cfr_config = *cfr;
// Set the cfr for the ifft's
if (srsran_ofdm_set_cfr(&q->ifft_mbsfn, &q->cfr_config) < SRSRAN_SUCCESS) {
ERROR("Error setting the CFR for ifft_mbsfn");
return SRSRAN_ERROR;
}
for (int i = 0; i < SRSRAN_MAX_PORTS; i++) {
if (srsran_ofdm_set_cfr(&q->ifft[i], &q->cfr_config) < SRSRAN_SUCCESS) {
ERROR("Error setting the CFR for the IFFT (%d)", i);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
#ifdef resolve
void srsran_enb_dl_apply_power_allocation(srsran_enb_dl_t* q)
{
@ -419,22 +445,22 @@ int srsran_enb_dl_put_pmch(srsran_enb_dl_t* q, srsran_pmch_cfg_t* pmch_cfg, uint
void srsran_enb_dl_gen_signal(srsran_enb_dl_t* q)
{
// TODO: PAPR control
float norm_factor = enb_dl_get_norm_factor(q->cell.nof_prb);
// First apply the amplitude normalization, then perform the IFFT and optional CFR reduction
if (q->dl_sf.sf_type == SRSRAN_SF_MBSFN) {
srsran_ofdm_tx_sf(&q->ifft_mbsfn);
srsran_vec_sc_prod_cfc(q->ifft_mbsfn.cfg.out_buffer,
srsran_vec_sc_prod_cfc(q->ifft_mbsfn.cfg.in_buffer,
norm_factor,
q->ifft_mbsfn.cfg.out_buffer,
(uint32_t)SRSRAN_SF_LEN_PRB(q->cell.nof_prb));
q->ifft_mbsfn.cfg.in_buffer,
SRSRAN_NOF_SLOTS_PER_SF * q->cell.nof_prb * SRSRAN_NRE * SRSRAN_CP_NSYMB(q->cell.cp));
srsran_ofdm_tx_sf(&q->ifft_mbsfn);
} else {
for (int i = 0; i < q->cell.nof_ports; i++) {
srsran_ofdm_tx_sf(&q->ifft[i]);
srsran_vec_sc_prod_cfc(q->ifft[i].cfg.out_buffer,
srsran_vec_sc_prod_cfc(q->ifft[i].cfg.in_buffer,
norm_factor,
q->ifft[i].cfg.out_buffer,
(uint32_t)SRSRAN_SF_LEN_PRB(q->cell.nof_prb));
q->ifft[i].cfg.in_buffer,
SRSRAN_NOF_SLOTS_PER_SF * q->cell.nof_prb * SRSRAN_NRE * SRSRAN_CP_NSYMB(q->cell.cp));
srsran_ofdm_tx_sf(&q->ifft[i]);
}
}
}

@ -62,7 +62,7 @@ static expected_errors_t expected_errors[] = {{1000, 1, 40, true, 0.0, 7282},
{1000, 1, 56, true, 3.0, 176},
{1000, 1, 56, true, 4.5, 24},
{100, 1, 1000, true, 0.0, 13208},
{100, 1, 1000, true, 0.0, 16000},
{100, 1, 1000, true, 2.0, 939},
{100, 1, 1000, true, 3.0, 110},
{100, 1, 1000, true, 4.5, 5},

@ -260,7 +260,7 @@ uint32_t srsran_sch_cfg_nr_info(const srsran_sch_cfg_nr_t* sch_cfg, char* str, u
// Append SCH information
len += phch_cfg_sch_to_str(&sch_cfg->sch_cfg, &str[len], str_len - len);
// Append SCH information
// Append reserved RE information
len += phch_cfg_rvd_to_str(&sch_cfg->rvd_re, &str[len], str_len - len);
// UCI configuration

@ -635,6 +635,7 @@ int srsran_ra_nr_fill_tb(const srsran_sch_cfg_nr_t* pdsch_cfg,
uint32_t N_re_rvd = srsran_re_pattern_list_count(&pdsch_cfg->rvd_re, grant->S, grant->S + grant->L, grant->prb_idx);
// Steps 2,3,4
tb->mcs = mcs_idx;
tb->tbs = (int)srsran_ra_nr_tbs(N_re, S, R, Qm, tb->N_L);
tb->R = R;
tb->mod = m;

@ -605,6 +605,7 @@ static int sch_nr_decode(srsran_sch_nr_t* q,
// Counter of code blocks that have matched CRC
uint32_t cb_ok = 0;
res->crc = false;
// For each code block...
uint32_t j = 0;

@ -690,6 +690,9 @@ add_nr_test(pusch_nr_ack2_csi4_test pusch_nr_test -p 50 -m 20 -A 2 -C 4)
add_nr_test(pusch_nr_ack4_csi4_test pusch_nr_test -p 50 -m 20 -A 4 -C 4)
add_nr_test(pusch_nr_ack20_csi4_test pusch_nr_test -p 50 -m 20 -A 20 -C 4)
add_executable(pusch_nr_bler_test EXCLUDE_FROM_ALL pusch_nr_bler_test.c)
target_link_libraries(pusch_nr_bler_test srsran_phy)
# this is just for performance evaluation, not for unit testing
add_executable(pdcch_nr_test pdcch_nr_test.c)
target_link_libraries(pdcch_nr_test srsran_phy)

@ -0,0 +1,426 @@
/**
* \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 pusch_nr_bler_test.c
* \brief BLER and throughput test for PUSCH NR.
*
* This program simulates several PUSCH transmissions in order to estimate its performance in terms of the receiver BLER
* and throughput (expressed as a percentage of the transmitted one). Specifically, the simulation runs until 100
* transmissions fail (or after 2,000,000 transmitted transport blocks). Failures are detected by CRC verification.
*
* The simulation setup can be controlled by means of the following arguments.
* - <tt>-p num</tt>: sets the number of granted PUSCH PRBs to \c num.
* - <tt>-T tab</tt>: sets the modulation and coding scheme table (valid options: \c 64qam, \c 256qam, \c 64qamLowSE).
* - <tt>-m mcs</tt>: sets the modulation and coding scheme index to \c mcs.
* - <tt>-L num</tt>: sets the number of transmission layers to \c num.
* - <tt>-A num</tt>: sets the number of HARQ-ACK bits to \c num.
* - <tt>-C num</tt>: sets the number of CSI bits to \c num.
* - <tt>-N num</tt>: sets the maximum number of simulated transport blocks to \c num.
* - <tt>-s val</tt>: sets the nominal SNR to \c val (in dB).
* - <tt>-f </tt>: activates full BLER simulations (Tx--Rx comparison as opposed to CRC-verification only).
* - <tt>-v </tt>: activates verbose output.
*
* Example:
* \code{.cpp}
* pusch_nr_bler_test -p 52 -m 2 -T 64qam -s -1.8 -f
* \endcode
*
*/
#include "srsran/phy/channel/ch_awgn.h"
#include "srsran/phy/phch/pusch_nr.h"
#include "srsran/phy/phch/ra_nr.h"
#include "srsran/phy/phch/ra_ul_nr.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/random.h"
#include "srsran/phy/utils/vector.h"
#include <getopt.h>
static srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR;
static uint32_t n_prb = 0;
static uint32_t mcs = 30;
static srsran_sch_cfg_nr_t pusch_cfg = {};
static uint16_t rnti = 0x1234;
static uint32_t nof_ack_bits = 0;
static uint32_t nof_csi_bits = 0;
static uint32_t max_blocks = 2e6; // max number of simulated transport blocks
static float snr = 10;
static bool full_check = false;
void usage(char* prog)
{
printf("Usage: %s [pmTLACNsfv] \n", prog);
printf("\t-p Number of grant PRB [Default %d]\n", n_prb);
printf("\t-m MCS PRB [Default %d]\n", mcs);
printf("\t-T Provide MCS table (64qam, 256qam, 64qamLowSE) [Default %s]\n",
srsran_mcs_table_to_str(pusch_cfg.sch_cfg.mcs_table));
printf("\t-L Provide number of layers [Default %d]\n", carrier.max_mimo_layers);
printf("\t-A Provide a number of HARQ-ACK bits [Default %d]\n", nof_ack_bits);
printf("\t-C Provide a number of CSI bits [Default %d]\n", nof_csi_bits);
printf("\t-N Maximum number of simulated transport blocks [Default %d]\n", max_blocks);
printf("\t-s Signal-to-Noise Ratio in dB [Default %.1f]\n", snr);
printf("\t-f Perform full BLER check instead of CRC only [Default %s]\n", full_check ? "true" : "false");
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
int parse_args(int argc, char** argv)
{
int opt = 0;
while ((opt = getopt(argc, argv, "p:m:T:L:A:C:N:s:fv")) != -1) {
switch (opt) {
case 'p':
n_prb = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'm':
mcs = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'T':
pusch_cfg.sch_cfg.mcs_table = srsran_mcs_table_from_str(optarg);
break;
case 'L':
carrier.max_mimo_layers = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'A':
nof_ack_bits = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'C':
nof_csi_bits = (uint32_t)strtol(optarg, NULL, 10);
break;
case 'N':
max_blocks = (uint32_t)strtol(optarg, NULL, 10);
break;
case 's':
snr = strtof(optarg, NULL);
break;
case 'f':
full_check = true;
break;
case 'v':
increase_srsran_verbose_level();
break;
default:
usage(argv[0]);
return SRSRAN_ERROR;
}
}
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSRAN_ERROR;
srsran_pusch_nr_t pusch_tx = {};
srsran_pusch_nr_t pusch_rx = {};
srsran_chest_dl_res_t chest = {};
srsran_random_t rand_gen = srsran_random_init(1234);
srsran_pusch_data_nr_t data_tx = {};
srsran_pusch_res_nr_t data_rx = {};
cf_t* sf_symbols_tx[SRSRAN_MAX_LAYERS_NR] = {};
cf_t* sf_symbols_rx[SRSRAN_MAX_LAYERS_NR] = {};
// Set default PUSCH configuration
pusch_cfg.sch_cfg.mcs_table = srsran_mcs_table_64qam;
if (parse_args(argc, argv) < SRSRAN_SUCCESS) {
goto clean_exit;
}
srsran_pusch_nr_args_t pusch_args = {};
pusch_args.sch.disable_simd = false;
pusch_args.measure_evm = true;
if (srsran_pusch_nr_init_ue(&pusch_tx, &pusch_args) < SRSRAN_SUCCESS) {
ERROR("Error initiating PUSCH for Tx");
goto clean_exit;
}
if (srsran_pusch_nr_init_gnb(&pusch_rx, &pusch_args) < SRSRAN_SUCCESS) {
ERROR("Error initiating SCH NR for Rx");
goto clean_exit;
}
if (srsran_pusch_nr_set_carrier(&pusch_tx, &carrier)) {
ERROR("Error setting SCH NR carrier");
goto clean_exit;
}
if (srsran_pusch_nr_set_carrier(&pusch_rx, &carrier)) {
ERROR("Error setting SCH NR carrier");
goto clean_exit;
}
uint32_t slot_length = SRSRAN_SLOT_LEN_RE_NR(carrier.nof_prb);
for (uint32_t i = 0; i < carrier.max_mimo_layers; i++) {
sf_symbols_tx[i] = srsran_vec_cf_malloc(slot_length);
sf_symbols_rx[i] = srsran_vec_cf_malloc(slot_length);
if (sf_symbols_tx[i] == NULL || sf_symbols_rx[i] == NULL) {
ERROR("Error malloc");
goto clean_exit;
}
}
for (uint32_t i = 0; i < pusch_tx.max_cw; i++) {
data_tx.payload[i] = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
data_rx.tb[i].payload = srsran_vec_u8_malloc(SRSRAN_SLOT_MAX_NOF_BITS_NR);
if (data_tx.payload[i] == NULL || data_rx.tb[i].payload == NULL) {
ERROR("Error malloc");
goto clean_exit;
}
}
srsran_softbuffer_tx_t softbuffer_tx = {};
srsran_softbuffer_rx_t softbuffer_rx = {};
if (srsran_softbuffer_tx_init_guru(&softbuffer_tx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) <
SRSRAN_SUCCESS) {
ERROR("Error init soft-buffer");
goto clean_exit;
}
if (srsran_softbuffer_rx_init_guru(&softbuffer_rx, SRSRAN_SCH_NR_MAX_NOF_CB_LDPC, SRSRAN_LDPC_MAX_LEN_ENCODED_CB) <
SRSRAN_SUCCESS) {
ERROR("Error init soft-buffer");
goto clean_exit;
}
// Use grant default A time resources with m=0
if (srsran_ra_ul_nr_pusch_time_resource_default_A(carrier.scs, 0, &pusch_cfg.grant) < SRSRAN_SUCCESS) {
ERROR("Error loading default grant");
goto clean_exit;
}
// Set PUSCH grant without considering any procedure
pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
pusch_cfg.grant.nof_layers = carrier.max_mimo_layers;
pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
pusch_cfg.grant.rnti = rnti;
// Check input: PRB
if (n_prb > carrier.nof_prb) {
ERROR("Invalid number of PRB");
goto clean_exit;
}
// Check input: MCS
uint32_t mcs_end = pusch_cfg.sch_cfg.mcs_table == srsran_mcs_table_256qam ? 28 : 29;
if (mcs > mcs_end) {
ERROR("Invalid MCS");
goto clean_exit;
}
srsran_sch_hl_cfg_nr_t sch_hl_cfg = {};
sch_hl_cfg.scaling = 1.0F;
sch_hl_cfg.beta_offsets.fix_ack = 12.625F;
sch_hl_cfg.beta_offsets.fix_csi1 = 2.25F;
sch_hl_cfg.beta_offsets.fix_csi2 = 2.25F;
if (srsran_chest_dl_res_init(&chest, carrier.nof_prb) < SRSRAN_SUCCESS) {
ERROR("Initiating chest");
goto clean_exit;
}
for (uint32_t n = 0; n < SRSRAN_MAX_PRB_NR; n++) {
pusch_cfg.grant.prb_idx[n] = (n < n_prb);
}
pusch_cfg.grant.nof_prb = n_prb;
pusch_cfg.grant.dci_format = srsran_dci_format_nr_0_0;
pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 2;
pusch_cfg.dmrs.type = srsran_dmrs_sch_type_1;
pusch_cfg.dmrs.length = srsran_dmrs_sch_len_1;
pusch_cfg.dmrs.additional_pos = srsran_dmrs_sch_add_pos_2;
if (srsran_ra_nr_fill_tb(&pusch_cfg, &pusch_cfg.grant, mcs, &pusch_cfg.grant.tb[0]) < SRSRAN_SUCCESS) {
ERROR("Error filling tb");
goto clean_exit;
}
uint32_t n_blocks = 0;
uint32_t n_errors = 0;
uint32_t crc_false_pos = 0;
uint32_t crc_false_neg = 0;
float evm = 0;
for (; n_blocks < max_blocks && n_errors < 100; n_blocks++) {
// Generate SCH payload
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) {
// Skip TB if no allocated
if (data_tx.payload[tb] == NULL) {
continue;
}
// load payload with bytes
for (uint32_t i = 0; i < pusch_cfg.grant.tb[tb].tbs / 8 + 1; i++) {
data_tx.payload[tb][i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, UINT8_MAX);
}
pusch_cfg.grant.tb[tb].softbuffer.tx = &softbuffer_tx;
}
// Generate HARQ ACK bits
if (nof_ack_bits > 0) {
pusch_cfg.uci.ack.count = nof_ack_bits;
for (uint32_t i = 0; i < nof_ack_bits; i++) {
data_tx.uci.ack[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1);
}
}
// Generate CSI report bits
uint8_t csi_report_tx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {};
uint8_t csi_report_rx[SRSRAN_UCI_NR_MAX_CSI1_BITS] = {};
if (nof_csi_bits > 0) {
pusch_cfg.uci.csi[0].cfg.quantity = SRSRAN_CSI_REPORT_QUANTITY_NONE;
pusch_cfg.uci.csi[0].K_csi_rs = nof_csi_bits;
pusch_cfg.uci.nof_csi = 1;
data_tx.uci.csi[0].none = csi_report_tx;
for (uint32_t i = 0; i < nof_csi_bits; i++) {
csi_report_tx[i] = (uint8_t)srsran_random_uniform_int_dist(rand_gen, 0, 1);
}
data_rx.uci.csi[0].none = csi_report_rx;
}
if (srsran_ra_ul_set_grant_uci_nr(&carrier, &sch_hl_cfg, &pusch_cfg.uci, &pusch_cfg) < SRSRAN_SUCCESS) {
ERROR("Setting UCI");
goto clean_exit;
}
if (srsran_pusch_nr_encode(&pusch_tx, &pusch_cfg, &pusch_cfg.grant, &data_tx, sf_symbols_tx) < SRSRAN_SUCCESS) {
ERROR("Error encoding");
goto clean_exit;
}
float noise_var = srsran_convert_dB_to_power(-snr);
for (uint32_t i = 0; i < carrier.max_mimo_layers; i++) {
srsran_ch_awgn_c(sf_symbols_tx[i], sf_symbols_rx[i], noise_var, slot_length);
// memcpy(sf_symbols_rx[i], sf_symbols_tx[i], slot_length * sizeof(cf_t));
}
if (get_srsran_verbose_level() >= SRSRAN_VERBOSE_INFO) {
uint32_t nof_re_total = carrier.nof_prb * SRSRAN_NRE;
uint32_t nof_re_used = pusch_cfg.grant.nof_prb * SRSRAN_NRE;
for (int i_layer = 0; i_layer < carrier.max_mimo_layers; i_layer++) {
INFO("Layer %d", i_layer);
float tx_power = 0;
float rx_power = 0;
uint8_t n_symbols = 0;
for (int i = 0; i < SRSRAN_NSYMB_PER_SLOT_NR; i++) {
if (!pusch_tx.dmrs_re_pattern.symbol[i]) {
n_symbols++;
tx_power += srsran_vec_avg_power_cf(sf_symbols_tx[0] + i * nof_re_total, nof_re_total);
rx_power += srsran_vec_avg_power_cf(sf_symbols_rx[0] + i * nof_re_total, nof_re_total);
}
}
tx_power *= (float)nof_re_total / nof_re_used; // compensate for unused REs
INFO(" Tx power: %.3f", tx_power / n_symbols);
INFO(" Rx power: %.3f", rx_power / n_symbols);
INFO(" SNR: %.3f dB", srsran_convert_power_to_dB(tx_power / (rx_power - tx_power)));
}
}
for (uint32_t tb = 0; tb < SRSRAN_MAX_TB; tb++) {
pusch_cfg.grant.tb[tb].softbuffer.rx = &softbuffer_rx;
srsran_softbuffer_rx_reset(pusch_cfg.grant.tb[tb].softbuffer.rx);
}
// assume perfect channel estimation (including noise variance)
for (uint32_t i = 0; i < pusch_cfg.grant.tb->nof_re; i++) {
chest.ce[0][0][i] = 1.0F;
}
chest.nof_re = pusch_cfg.grant.tb->nof_re;
chest.noise_estimate = 2 * noise_var;
if (srsran_pusch_nr_decode(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &chest, sf_symbols_rx, &data_rx) <
SRSRAN_SUCCESS) {
ERROR("Error decoding");
goto clean_exit;
}
evm += data_rx.evm[0];
// Validate UL-SCH CRC check
if (!data_rx.tb[0].crc) {
n_errors++;
printf("*");
fflush(stdout);
if (n_errors % 20 == 0) {
printf("\n");
}
}
if (full_check) {
// Validate by comparing payload (recall, payload is represented in bytes)
if ((memcmp(data_rx.tb[0].payload, data_tx.payload[0], pusch_cfg.grant.tb[0].tbs * sizeof(uint8_t) / 8) == 0) &&
!data_rx.tb[0].crc) {
printf("\nWARNING! Codeword OK but CRC KO!\n");
crc_false_pos++;
} else if ((memcmp(data_rx.tb[0].payload, data_tx.payload[0], pusch_cfg.grant.tb[0].tbs * sizeof(uint8_t) / 8) !=
0) &&
data_rx.tb[0].crc) {
printf("\nWarning! Codeword KO but CRC OK!\n");
crc_false_neg++;
}
}
}
char str[512];
srsran_pusch_nr_rx_info(&pusch_rx, &pusch_cfg, &pusch_cfg.grant, &data_rx, str, (uint32_t)sizeof(str));
char str_extra[2048];
srsran_sch_cfg_nr_info(&pusch_cfg, str_extra, (uint32_t)sizeof(str_extra));
printf("\nPUSCH: %s\n%s", str, str_extra);
printf("\nNominal SNR: %.1f dB\n", snr);
printf("Average EVM: %.3f\n", evm / n_blocks);
printf("BLER: %.3e (%d errors out of %d blocks)\n", (double)n_errors / n_blocks, n_errors, n_blocks);
printf("Tx Throughput: %.3e Mbps -- Rx Throughput: %.3e Mbps (%.2f%%)\n",
pusch_cfg.grant.tb[0].tbs / 1e3,
(n_blocks - n_errors) / 1e3 * pusch_cfg.grant.tb[0].tbs / n_blocks,
100.0F * (n_blocks - n_errors) / n_blocks);
if (full_check) {
uint32_t true_errors = n_errors + crc_false_neg - crc_false_pos;
printf("CRC: missed detection/Type I err. %.2f%% (%d out of %d)",
100.0F * crc_false_neg / true_errors,
crc_false_neg,
true_errors);
printf(" -- false alarm %.2f%% (%d out of %d)", 100.0F * crc_false_pos / n_errors, crc_false_pos, n_errors);
printf(" -- Type II err. %.2f%% (%d out of %d)\n",
100.0F * crc_false_pos / (n_blocks - true_errors),
crc_false_pos,
n_blocks - true_errors);
}
ret = SRSRAN_SUCCESS;
clean_exit:
srsran_chest_dl_res_free(&chest);
srsran_random_free(rand_gen);
srsran_pusch_nr_free(&pusch_tx);
srsran_pusch_nr_free(&pusch_rx);
for (uint32_t i = 0; i < SRSRAN_MAX_CODEWORDS; i++) {
if (data_tx.payload[i]) {
free(data_tx.payload[i]);
}
if (data_rx.tb[i].payload) {
free(data_rx.tb[i].payload);
}
}
for (uint32_t i = 0; i < SRSRAN_MAX_LAYERS_NR; i++) {
if (sf_symbols_tx[i]) {
free(sf_symbols_tx[i]);
}
if (sf_symbols_rx[i]) {
free(sf_symbols_rx[i]);
}
}
srsran_softbuffer_tx_free(&softbuffer_tx);
srsran_softbuffer_rx_free(&softbuffer_rx);
return ret;
}

@ -165,7 +165,7 @@ int main(int argc, char** argv)
goto clean_exit;
}
// Set PDSCH grant without considering any procedure
// Set PUSCH grant without considering any procedure
pusch_cfg.grant.nof_dmrs_cdm_groups_without_data = 1; // No need for MIMO
pusch_cfg.grant.nof_layers = carrier.max_mimo_layers;
pusch_cfg.grant.dci_format = srsran_dci_format_nr_1_0;
@ -205,7 +205,7 @@ int main(int argc, char** argv)
pusch_cfg.grant.dci_format = srsran_dci_format_nr_0_0;
if (srsran_ra_nr_fill_tb(&pusch_cfg, &pusch_cfg.grant, mcs, &pusch_cfg.grant.tb[0]) < SRSRAN_SUCCESS) {
ERROR("Error filing tb");
ERROR("Error filling tb");
goto clean_exit;
}
@ -280,9 +280,11 @@ int main(int argc, char** argv)
uint32_t nof_re = srsran_ra_dl_nr_slot_nof_re(&pusch_cfg, &pusch_cfg.grant);
if (nof_re * pusch_cfg.grant.nof_layers > 0) {
float mse = 0.0f;
float mse_tmp = 0.0f;
for (uint32_t i = 0; i < pusch_cfg.grant.nof_layers; i++) {
for (uint32_t j = 0; j < nof_re; j++) {
mse += cabsf(pusch_tx.d[i][j] - pusch_rx.d[i][j]);
mse_tmp = cabsf(pusch_tx.d[i][j] - pusch_rx.d[i][j]);
mse += mse_tmp * mse_tmp;
}
}
mse = mse / (nof_re * pusch_cfg.grant.nof_layers);

@ -71,6 +71,8 @@ if(RF_FOUND)
list(APPEND SOURCES_RF rf_zmq_imp.c rf_zmq_imp_tx.c rf_zmq_imp_rx.c)
endif (ZEROMQ_FOUND)
list(APPEND SOURCES_RF rf_file_imp.c rf_file_imp_tx.c rf_file_imp_rx.c)
add_library(srsran_rf_object OBJECT ${SOURCES_RF})
set_property(TARGET srsran_rf_object PROPERTY POSITION_INDEPENDENT_CODE 1)
@ -110,5 +112,9 @@ if(RF_FOUND)
#add_test(rf_zmq_test rf_zmq_test)
endif (ZEROMQ_FOUND)
add_executable(rf_file_test rf_file_test.c)
target_link_libraries(rf_file_test srsran_rf)
add_test(rf_file_test rf_file_test)
INSTALL(TARGETS srsran_rf DESTINATION ${LIBRARY_DIR})
endif(RF_FOUND)

@ -225,6 +225,40 @@ static rf_dev_t dev_zmq = {"zmq",
.srsran_rf_send_timed_multi = rf_zmq_send_timed_multi};
#endif
/* Define implementation for file-based RF */
#include "rf_file_imp.h"
static rf_dev_t dev_file = {"file",
rf_file_devname,
rf_file_start_rx_stream,
rf_file_stop_rx_stream,
rf_file_flush_buffer,
rf_file_has_rssi,
rf_file_get_rssi,
rf_file_suppress_stdout,
rf_file_register_error_handler,
rf_file_open,
.srsran_rf_open_multi = rf_file_open_multi,
rf_file_close,
rf_file_set_rx_srate,
rf_file_set_rx_gain,
rf_file_set_rx_gain_ch,
rf_file_set_tx_gain,
rf_file_set_tx_gain_ch,
rf_file_get_rx_gain,
rf_file_get_tx_gain,
rf_file_get_info,
rf_file_set_rx_freq,
rf_file_set_tx_srate,
rf_file_set_tx_freq,
rf_file_get_time,
NULL,
rf_file_recv_with_time,
rf_file_recv_with_time_multi,
rf_file_send_timed,
.srsran_rf_send_timed_multi = rf_file_send_timed_multi};
/* Define implementation for Sidekiq */
#ifdef ENABLE_SIDEKIQ
@ -276,6 +310,9 @@ static rf_dev_t dev_dummy = {"dummy", dummy_fnc, dummy_fnc, dummy_fnc, dummy_f
dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc, dummy_fnc};
#endif
/**
* Collection of all currently supported RF devices
*/
static rf_dev_t* available_devices[] = {
#ifdef ENABLE_UHD
@ -296,4 +333,5 @@ static rf_dev_t* available_devices[] = {
#ifdef ENABLE_DUMMY_DEV
&dev_dummy,
#endif
&dev_file,
NULL};

@ -0,0 +1,859 @@
/**
*
* \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_RF_IMP_TRX_H
#define SRSRAN_RF_IMP_TRX_H
#include "rf_file_imp.h"
#include "rf_file_imp_trx.h"
#include "rf_helper.h"
#include <errno.h>
#include <math.h>
#include <srsran/phy/common/phy_common.h>
#include <srsran/phy/common/timestamp.h>
#include <srsran/phy/utils/vector.h>
#include <stdarg.h>
#include <stdlib.h>
#include <unistd.h>
typedef struct {
// Common attributes
char* devname;
srsran_rf_info_t info;
uint32_t nof_channels;
// RF State
uint32_t srate; // radio rate configured by upper layers
uint32_t base_srate;
uint32_t decim_factor; // decimation factor between base_srate used on transport on radio's rate
double rx_gain;
uint32_t tx_freq_mhz[SRSRAN_MAX_CHANNELS];
uint32_t rx_freq_mhz[SRSRAN_MAX_CHANNELS];
bool tx_off;
char id[RF_PARAM_LEN];
// FILEs
rf_file_tx_t transmitter[SRSRAN_MAX_CHANNELS];
rf_file_rx_t receiver[SRSRAN_MAX_CHANNELS];
bool close_files;
// Various sample buffers
cf_t* buffer_decimation[SRSRAN_MAX_CHANNELS];
cf_t* buffer_tx;
// Rx timestamp
uint64_t next_rx_ts;
pthread_mutex_t tx_config_mutex;
pthread_mutex_t rx_config_mutex;
pthread_mutex_t decim_mutex;
pthread_mutex_t rx_gain_mutex;
} rf_file_handler_t;
/*
* Static methods
*/
static void update_rates(rf_file_handler_t* handler, double srate);
void rf_file_info(char* id, const char* format, ...)
{
#if VERBOSE
struct timeval t;
gettimeofday(&t, NULL);
va_list args;
va_start(args, format);
printf("[%s@%02ld.%06ld] ", id ? id : "file", t.tv_sec % 10, t.tv_usec);
vprintf(format, args);
va_end(args);
#else /* VERBOSE */
// Do nothing
#endif /* VERBOSE */
}
void rf_file_error(char* id, const char* format, ...)
{
struct timeval t;
gettimeofday(&t, NULL);
va_list args;
va_start(args, format);
vfprintf(stderr, format, args);
va_end(args);
}
static inline int update_ts(void* h, uint64_t* ts, int nsamples, const char* dir)
{
int ret = SRSRAN_ERROR;
if (h && nsamples > 0) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
(*ts) += nsamples;
srsran_timestamp_t _ts = {};
srsran_timestamp_init_uint64(&_ts, *ts, handler->base_srate);
rf_file_info(
handler->id, " -> next %s time after %d samples: %d + %.3f\n", dir, nsamples, _ts.full_secs, _ts.frac_secs);
ret = SRSRAN_SUCCESS;
}
return ret;
}
int rf_file_handle_error(char* id, const char* text)
{
// Not implemented
return SRSRAN_ERROR;
}
/*
* Public methods
*/
const char* rf_file_devname(void* h)
{
return DEVNAME_FILE;
}
int rf_file_start_rx_stream(void* h, bool now)
{
return SRSRAN_SUCCESS;
}
int rf_file_stop_rx_stream(void* h)
{
return SRSRAN_SUCCESS;
}
void rf_file_flush_buffer(void* h)
{
printf("%s\n", __FUNCTION__);
}
bool rf_file_has_rssi(void* h)
{
return false;
}
float rf_file_get_rssi(void* h)
{
return 0.0;
}
void rf_file_suppress_stdout(void* h)
{
// do nothing
}
void rf_file_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg)
{
// do nothing
}
int rf_file_open(char* args, void** h)
{
return rf_file_open_multi(args, h, 1);
}
int rf_file_open_multi(char* args, void** h, uint32_t nof_channels)
{
int ret = SRSRAN_ERROR;
FILE* rx_files[SRSRAN_MAX_CHANNELS] = {NULL};
FILE* tx_files[SRSRAN_MAX_CHANNELS] = {NULL};
if (h && nof_channels <= SRSRAN_MAX_CHANNELS) {
uint32_t base_srate = FILE_BASERATE_DEFAULT_HZ;
// parse args
if (args && strlen(args)) {
// base_srate
parse_uint32(args, "base_srate", -1, &base_srate);
} else {
fprintf(stderr, "[file] Error: RF device args are required for file-based no-RF module\n");
goto clean_exit;
}
for (int i = 0; i < nof_channels; i++) {
// rx_file
char rx_file[RF_PARAM_LEN] = {};
parse_string(args, "rx_file", i, rx_file);
// tx_file
char tx_file[RF_PARAM_LEN] = {};
parse_string(args, "tx_file", i, tx_file);
// initialize transmitter
if (strlen(tx_file) != 0) {
tx_files[i] = fopen(tx_file, "wb");
if (tx_files[i] == NULL) {
fprintf(stderr, "[file] Error: opening tx_file%d: %s; %s\n", i, tx_file, strerror(errno));
goto clean_exit;
}
}
// initialize receiver
if (strlen(rx_file) != 0) {
rx_files[i] = fopen(rx_file, "rb");
if (rx_files[i] == NULL) {
fprintf(stderr, "[file] Error: opening rx_file%d: %s; %s\n", i, rx_file, strerror(errno));
goto clean_exit;
}
}
}
// defer further initialization to open_file method
ret = rf_file_open_file(h, rx_files, tx_files, nof_channels, base_srate);
if (ret != SRSRAN_SUCCESS) {
goto clean_exit;
}
// add flag to close all files when closing device
rf_file_handler_t* handler = (rf_file_handler_t*)(*h);
handler->close_files = true;
return ret;
}
clean_exit:
for (int i = 0; i < nof_channels; i++) {
if (rx_files[i] != NULL) {
fclose(rx_files[i]);
}
if (tx_files[i] != NULL) {
fclose(tx_files[i]);
}
}
return ret;
}
int rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate)
{
int ret = SRSRAN_ERROR;
if (h) {
*h = NULL;
rf_file_handler_t* handler = (rf_file_handler_t*)malloc(sizeof(rf_file_handler_t));
if (!handler) {
fprintf(stderr, "malloc: %s\n", strerror(errno));
return SRSRAN_ERROR;
}
memset(handler, 0, sizeof(rf_file_handler_t));
*h = handler;
handler->base_srate = base_srate;
handler->info.max_rx_gain = FILE_MAX_GAIN_DB;
handler->info.min_rx_gain = FILE_MIN_GAIN_DB;
handler->info.max_tx_gain = FILE_MAX_GAIN_DB;
handler->info.min_tx_gain = FILE_MIN_GAIN_DB;
handler->nof_channels = nof_channels;
strcpy(handler->id, "file\0");
rf_file_opts_t rx_opts = {};
rf_file_opts_t tx_opts = {};
tx_opts.id = handler->id;
rx_opts.id = handler->id;
if (pthread_mutex_init(&handler->tx_config_mutex, NULL)) {
fprintf(stderr, "Mutex init: %s\n", strerror(errno));
}
if (pthread_mutex_init(&handler->rx_config_mutex, NULL)) {
fprintf(stderr, "Mutex init: %s\n", strerror(errno));
}
if (pthread_mutex_init(&handler->decim_mutex, NULL)) {
fprintf(stderr, "Mutex init: %s\n", strerror(errno));
}
if (pthread_mutex_init(&handler->rx_gain_mutex, NULL)) {
fprintf(stderr, "Mutex init: %s\n", strerror(errno));
}
pthread_mutex_lock(&handler->rx_gain_mutex);
handler->rx_gain = 0.0;
pthread_mutex_unlock(&handler->rx_gain_mutex);
// id
// TODO: set some meaningful ID in handler->id
// rx_format, tx_format
// TODO: add other formats
rx_opts.sample_format = FILERF_TYPE_FC32;
tx_opts.sample_format = FILERF_TYPE_FC32;
update_rates(handler, 1.92e6);
// Create channels
for (int i = 0; i < handler->nof_channels; i++) {
if (rx_files != NULL && rx_files[i] != NULL) {
rx_opts.file = rx_files[i];
if (rf_file_rx_open(&handler->receiver[i], rx_opts) != SRSRAN_SUCCESS) {
fprintf(stderr, "[file] Error: opening receiver\n");
goto clean_exit;
}
} else {
// no rx_files provided
fprintf(stdout, "[file] %s rx channel %d not specified. Disabling receiver.\n", handler->id, i);
}
if (tx_files != NULL && tx_files[i] != NULL) {
tx_opts.file = tx_files[i];
if (rf_file_tx_open(&handler->transmitter[i], tx_opts) != SRSRAN_SUCCESS) {
fprintf(stderr, "[file] Error: opening transmitter\n");
goto clean_exit;
}
} else {
// no tx_files provided
fprintf(stdout, "[file] %s tx channel %d not specified. Disabling transmitter.\n", handler->id, i);
handler->tx_off = true;
}
if (!handler->transmitter[i].running && !handler->receiver[i].running) {
fprintf(stderr, "[file] Error: Neither tx nor rx specificed for channel %d.\n", i);
goto clean_exit;
}
}
// Create decimation and overflow buffer
for (uint32_t i = 0; i < handler->nof_channels; i++) {
handler->buffer_decimation[i] = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!handler->buffer_decimation[i]) {
fprintf(stderr, "Error: allocating decimation buffer\n");
goto clean_exit;
}
}
handler->buffer_tx = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!handler->buffer_tx) {
fprintf(stderr, "Error: allocating tx buffer\n");
goto clean_exit;
}
ret = SRSRAN_SUCCESS;
clean_exit:
if (ret) {
rf_file_close(handler);
}
}
return ret;
}
int rf_file_close(void* h)
{
rf_file_handler_t* handler = (rf_file_handler_t*)h;
rf_file_info(handler->id, "Closing ...\n");
// close receiver+transmitter and release related resources (except for the file handles)
for (int i = 0; i < handler->nof_channels; i++) {
rf_file_tx_close(&handler->transmitter[i]);
rf_file_rx_close(&handler->receiver[i]);
}
// release other resources
for (uint32_t i = 0; i < handler->nof_channels; i++) {
if (handler->buffer_decimation[i]) {
free(handler->buffer_decimation[i]);
}
}
if (handler->buffer_tx) {
free(handler->buffer_tx);
}
pthread_mutex_destroy(&handler->tx_config_mutex);
pthread_mutex_destroy(&handler->rx_config_mutex);
pthread_mutex_destroy(&handler->decim_mutex);
pthread_mutex_destroy(&handler->rx_gain_mutex);
// now close the files if we opened them ourselves
if (handler->close_files) {
for (int i = 0; i < handler->nof_channels; i++) {
if (handler->receiver[i].file != NULL) {
fclose(handler->receiver[i].file);
}
if (handler->transmitter[i].file != NULL) {
fclose(handler->transmitter[i].file);
}
}
}
// Free all
free(handler);
return SRSRAN_SUCCESS;
}
void update_rates(rf_file_handler_t* handler, double srate)
{
pthread_mutex_lock(&handler->decim_mutex);
if (handler) {
// Decimation must be full integer
if (((uint64_t)handler->base_srate % (uint64_t)srate) == 0) {
handler->srate = (uint32_t)srate;
handler->decim_factor = handler->base_srate / handler->srate;
} else {
fprintf(stderr,
"Error: couldn't update sample rate. %.2f is not divisible by %.2f\n",
srate / 1e6,
handler->base_srate / 1e6);
}
printf("Current sample rate is %.2f MHz with a base rate of %.2f MHz (x%d decimation)\n",
handler->srate / 1e6,
handler->base_srate / 1e6,
handler->decim_factor);
}
pthread_mutex_unlock(&handler->decim_mutex);
}
double rf_file_set_rx_srate(void* h, double srate)
{
double ret = 0.0;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
update_rates(handler, srate);
ret = handler->srate;
}
return ret;
}
int rf_file_set_rx_gain(void* h, double gain)
{
double ret = 0.0;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
handler->rx_gain = gain;
ret = gain;
}
return ret;
}
int rf_file_set_rx_gain_ch(void* h, uint32_t ch, double gain)
{
return rf_file_set_rx_gain(h, gain);
}
int rf_file_set_tx_gain(void* h, double gain)
{
return 0.0;
}
int rf_file_set_tx_gain_ch(void* h, uint32_t ch, double gain)
{
return rf_file_set_tx_gain(h, gain);
}
double rf_file_get_rx_gain(void* h)
{
double ret = 0.0;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
ret = handler->rx_gain;
}
return ret;
}
double rf_file_get_tx_gain(void* h)
{
return 0.0;
}
srsran_rf_info_t* rf_file_get_info(void* h)
{
srsran_rf_info_t* info = NULL;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
info = &handler->info;
}
return info;
}
double rf_file_set_rx_freq(void* h, uint32_t ch, double freq)
{
double ret = NAN;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
pthread_mutex_lock(&handler->rx_config_mutex);
if (ch < handler->nof_channels && isnormal(freq) && freq > 0.0) {
handler->rx_freq_mhz[ch] = (uint32_t)(freq / 1e6);
ret = freq;
}
pthread_mutex_unlock(&handler->rx_config_mutex);
}
return ret;
}
double rf_file_set_tx_srate(void* h, double srate)
{
double ret = 0.0;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
update_rates(handler, srate);
ret = srate;
}
return ret;
}
double rf_file_set_tx_freq(void* h, uint32_t ch, double freq)
{
double ret = NAN;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
pthread_mutex_lock(&handler->tx_config_mutex);
if (ch < handler->nof_channels && isnormal(freq) && freq > 0.0) {
handler->tx_freq_mhz[ch] = (uint32_t)(freq / 1e6);
ret = freq;
}
pthread_mutex_unlock(&handler->tx_config_mutex);
}
return ret;
}
void rf_file_get_time(void* h, time_t* secs, double* frac_secs)
{
if (h) {
if (secs) {
*secs = 0;
}
if (frac_secs) {
*frac_secs = 0;
}
}
}
int rf_file_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs)
{
return rf_file_recv_with_time_multi(h, &data, nsamples, blocking, secs, frac_secs);
}
int rf_file_recv_with_time_multi(void* h,
void** data,
uint32_t nsamples,
bool blocking,
time_t* secs,
double* frac_secs)
{
int ret = SRSRAN_ERROR;
if (h) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
// Map ports to data buffers according to the selected frequencies
pthread_mutex_lock(&handler->rx_config_mutex);
bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used
cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched
// For each logical channel...
for (uint32_t logical = 0; logical < handler->nof_channels; logical++) {
bool unmatched = true;
// For each physical channel...
for (uint32_t physical = 0; physical < handler->nof_channels; physical++) {
// Consider a match if the physical channel is NOT mapped and the frequency match
if (!mapped[physical] && rf_file_rx_match_freq(&handler->receiver[physical], handler->rx_freq_mhz[logical])) {
// Not mapped and matched frequency with receiver
buffers[physical] = (cf_t*)data[logical];
mapped[physical] = true;
unmatched = false;
break;
}
}
// If no matching frequency found; set data to zeros
if (unmatched) {
srsran_vec_zero(data[logical], nsamples);
}
}
pthread_mutex_unlock(&handler->rx_config_mutex);
// Protect the access to decim_factor since is a shared variable
pthread_mutex_lock(&handler->decim_mutex);
uint32_t decim_factor = handler->decim_factor;
pthread_mutex_unlock(&handler->decim_mutex);
uint32_t nbytes = NSAMPLES2NBYTES(nsamples * decim_factor);
uint32_t nsamples_baserate = nsamples * decim_factor;
rf_file_info(handler->id, "Rx %d samples (%d B)\n", nsamples, nbytes);
// set timestamp for this reception
if (secs != NULL && frac_secs != NULL) {
srsran_timestamp_t ts = {};
srsran_timestamp_init_uint64(&ts, handler->next_rx_ts, handler->base_srate);
*secs = ts.full_secs;
*frac_secs = ts.frac_secs;
}
// return if receiver is turned off
if (!handler->receiver[0].running) {
update_ts(handler, &handler->next_rx_ts, nsamples_baserate, "rx");
return nsamples;
}
// Check available buffer size
if (nbytes > FILE_MAX_BUFFER_SIZE) {
fprintf(stderr,
"[file] Error: Trying to receive %d B but buffer is only %zu B at channel %d.\n",
nbytes,
FILE_MAX_BUFFER_SIZE,
0);
goto clean_exit;
}
// receive samples
srsran_timestamp_t ts_tx = {}, ts_rx = {};
srsran_timestamp_init_uint64(&ts_tx, handler->transmitter[0].nsamples, handler->base_srate);
srsran_timestamp_init_uint64(&ts_rx, handler->next_rx_ts, handler->base_srate);
rf_file_info(handler->id, " - next rx time: %d + %.3f\n", ts_rx.full_secs, ts_rx.frac_secs);
rf_file_info(handler->id, " - next tx time: %d + %.3f\n", ts_tx.full_secs, ts_tx.frac_secs);
// check for tx gap if we're also transmitting on this radio
for (int i = 0; i < handler->nof_channels; i++) {
if (handler->transmitter[i].running) {
rf_file_tx_align(&handler->transmitter[i], handler->next_rx_ts + nsamples_baserate);
}
}
// copy from rx buffer as many samples as requested into provided buffer
bool completed = false;
int32_t count[SRSRAN_MAX_CHANNELS] = {};
while (!completed) {
uint32_t completed_count = 0;
// Iterate channels
for (uint32_t i = 0; i < handler->nof_channels; i++) {
cf_t* ptr = (decim_factor != 1 || buffers[i] == NULL) ? handler->buffer_decimation[i] : buffers[i];
// Completed condition
if (count[i] < nsamples_baserate && handler->receiver[i].running) {
// Keep receiving
int32_t n = rf_file_rx_baseband(&handler->receiver[i], &ptr[count[i]], nsamples_baserate - count[i]);
if (n > 0) {
// No error
count[i] += n;
} else {
if (n != SRSRAN_ERROR_RX_EOF) {
// Other error, exit
fprintf(stderr, "Error: receiving data.\n");
}
ret = n;
goto clean_exit;
}
} else {
// Completed, count it
completed_count++;
}
}
// Check if all channels are completed
completed = (completed_count == handler->nof_channels);
}
rf_file_info(handler->id, " - read %d samples.\n", NBYTES2NSAMPLES(nbytes));
// decimate if needed
if (decim_factor != 1) {
for (uint32_t c = 0; c < handler->nof_channels; c++) {
// skip if buffer is not available
if (buffers[c]) {
cf_t* dst = buffers[c];
cf_t* ptr = handler->buffer_decimation[c];
for (uint32_t i = 0, n = 0; i < nsamples; i++) {
// Averaging decimation
cf_t avg = 0.0f;
for (int j = 0; j < decim_factor; j++, n++) {
avg += ptr[n];
}
dst[i] = avg; // divide by decim_factor later via scale
}
rf_file_info(handler->id,
" - re-adjust bytes due to %dx decimation %d --> %d samples)\n",
decim_factor,
nsamples_baserate,
nsamples);
}
}
}
// Set gain
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
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);
}
}
// update rx time
update_ts(handler, &handler->next_rx_ts, nsamples_baserate, "rx");
}
ret = nsamples;
clean_exit:
return ret;
}
int rf_file_send_timed(void* h,
void* data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst)
{
void* _data[4] = {data, NULL, NULL, NULL};
return rf_file_send_timed_multi(
h, _data, nsamples, secs, frac_secs, has_time_spec, blocking, is_start_of_burst, is_end_of_burst);
}
int rf_file_send_timed_multi(void* h,
void* data[4],
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst)
{
int ret = SRSRAN_ERROR;
if (h && data && nsamples > 0) {
rf_file_handler_t* handler = (rf_file_handler_t*)h;
// Map ports to data buffers according to the selected frequencies
pthread_mutex_lock(&handler->tx_config_mutex);
bool mapped[SRSRAN_MAX_CHANNELS] = {}; // Mapped mask, set to true when the physical channel is used
cf_t* buffers[SRSRAN_MAX_CHANNELS] = {}; // Buffer pointers, NULL if unmatched or zero transmission
// For each logical channel...
for (uint32_t logical = 0; logical < handler->nof_channels; logical++) {
// For each physical channel...
for (uint32_t physical = 0; physical < handler->nof_channels; physical++) {
// Consider a match if the physical channel is NOT mapped and the frequency match
if (!mapped[physical] &&
rf_file_tx_match_freq(&handler->transmitter[physical], handler->tx_freq_mhz[logical])) {
// Not mapped and matched frequency with receiver
buffers[physical] = (cf_t*)data[logical];
mapped[physical] = true;
break;
}
}
}
pthread_mutex_unlock(&handler->tx_config_mutex);
// Protect the access to decim_factor since is a shared variable
pthread_mutex_lock(&handler->decim_mutex);
uint32_t decim_factor = handler->decim_factor;
pthread_mutex_unlock(&handler->decim_mutex);
uint32_t nbytes = NSAMPLES2NBYTES(nsamples);
uint32_t nsamples_baseband = nsamples * decim_factor;
uint32_t nbytes_baseband = NSAMPLES2NBYTES(nsamples_baseband);
if (nbytes_baseband > FILE_MAX_BUFFER_SIZE) {
fprintf(stderr, "Error: trying to transmit too many samples (%d > %zu).\n", nbytes, FILE_MAX_BUFFER_SIZE);
goto clean_exit;
}
rf_file_info(handler->id, "Tx %d samples (%d B)\n", nsamples, nbytes);
// return if transmitter is switched off
if (handler->tx_off) {
return SRSRAN_SUCCESS;
}
// check if this is a tx in the future
if (has_time_spec) {
rf_file_info(handler->id, " - tx time: %d + %.3f\n", secs, frac_secs);
srsran_timestamp_t ts = {};
srsran_timestamp_init(&ts, secs, frac_secs);
uint64_t tx_ts = srsran_timestamp_uint64(&ts, handler->base_srate);
int num_tx_gap_samples = 0;
for (int i = 0; i < handler->nof_channels; i++) {
if (handler->transmitter[i].running) {
num_tx_gap_samples = rf_file_tx_align(&handler->transmitter[i], tx_ts);
}
}
if (num_tx_gap_samples < 0) {
fprintf(stderr,
"[file] Error: tx time is %.3f ms in the past (%" PRIu64 " < %" PRIu64 ")\n",
-1000.0 * num_tx_gap_samples / handler->base_srate,
tx_ts,
handler->transmitter[0].nsamples);
goto clean_exit;
}
}
// Send base-band samples
for (int i = 0; i < handler->nof_channels; i++) {
if (buffers[i] != NULL) {
// Select buffer pointer depending on interpolation
cf_t* buf = (decim_factor != 1) ? handler->buffer_tx : buffers[i];
// Interpolate if required
if (decim_factor != 1) {
rf_file_info(handler->id,
" - re-adjust bytes due to %dx interpolation %d --> %d samples)\n",
decim_factor,
nsamples,
nsamples_baseband);
int n = 0;
cf_t* src = buffers[i];
for (int k = 0; k < nsamples; k++) {
// perform zero order hold
for (int j = 0; j < decim_factor; j++, n++) {
buf[n] = src[k];
}
}
if (nsamples_baseband != n) {
fprintf(stderr,
"Number of tx samples (%d) does not match with number of interpolated samples (%d)\n",
nsamples_baseband,
n);
goto clean_exit;
}
}
int n = rf_file_tx_baseband(&handler->transmitter[i], buf, nsamples_baseband);
if (n == SRSRAN_ERROR) {
goto clean_exit;
}
} else {
int n = rf_file_tx_zeros(&handler->transmitter[i], nsamples_baseband);
if (n == SRSRAN_ERROR) {
goto clean_exit;
}
}
}
}
ret = SRSRAN_SUCCESS;
clean_exit:
return ret;
}
#endif

@ -0,0 +1,132 @@
/**
*
* \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_RF_FILE_IMP_H
#define SRSRAN_RF_FILE_IMP_H
#include <inttypes.h>
#include "srsran/config.h"
#include "srsran/phy/common/phy_common.h"
#include "srsran/phy/rf/rf.h"
#define DEVNAME_FILE "file"
#define PARAM_LEN (128)
#define PARAM_LEN_SHORT (PARAM_LEN / 2)
SRSRAN_API const char* rf_file_devname(void* h);
SRSRAN_API int rf_file_start_rx_stream(void* h, bool now);
// SRSRAN_API int rf_file_start_rx_stream_nsamples(void* h, uint32_t nsamples);
SRSRAN_API int rf_file_stop_rx_stream(void* h);
SRSRAN_API void rf_file_flush_buffer(void* h);
SRSRAN_API bool rf_file_has_rssi(void* h);
SRSRAN_API float rf_file_get_rssi(void* h);
SRSRAN_API void rf_file_suppress_stdout(void* h);
SRSRAN_API void rf_file_register_error_handler(void* h, srsran_rf_error_handler_t error_handler, void* arg);
/**
* @brief This function is not supported for file-based RF abstraction
*
* Use @c rf_file_open_file() to open this device
*
* @param args not used
* @param h not used
* @return SRSRAN_ERROR_INVALID_COMMAND
*/
SRSRAN_API int rf_file_open(char* args, void** h);
/**
* @brief This function is not supported for file-based RF abstraction
*
* Use @c rf_file_open_file() to open this device
*
* @param args not used
* @param h not used
* @param nof_channels not used
* @return SRSRAN_ERROR_INVALID_COMMAND
*/
SRSRAN_API int rf_file_open_multi(char* args, void** h, uint32_t nof_channels);
SRSRAN_API int rf_file_close(void* h);
SRSRAN_API double rf_file_set_rx_srate(void* h, double srate);
SRSRAN_API int rf_file_set_rx_gain(void* h, double gain);
SRSRAN_API int rf_file_set_rx_gain_ch(void* h, uint32_t ch, double gain);
SRSRAN_API int rf_file_set_tx_gain(void* h, double gain);
SRSRAN_API int rf_file_set_tx_gain_ch(void* h, uint32_t ch, double gain);
SRSRAN_API double rf_file_get_rx_gain(void* h);
SRSRAN_API double rf_file_get_tx_gain(void* h);
SRSRAN_API srsran_rf_info_t* rf_file_get_info(void* h);
SRSRAN_API double rf_file_set_rx_freq(void* h, uint32_t ch, double freq);
SRSRAN_API double rf_file_set_tx_srate(void* h, double srate);
SRSRAN_API double rf_file_set_tx_freq(void* h, uint32_t ch, double freq);
SRSRAN_API void rf_file_get_time(void* h, time_t* secs, double* frac_secs);
// srsran_rf_sync_pps
SRSRAN_API int
rf_file_recv_with_time(void* h, void* data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs);
SRSRAN_API int
rf_file_recv_with_time_multi(void* h, void** data, uint32_t nsamples, bool blocking, time_t* secs, double* frac_secs);
SRSRAN_API int rf_file_send_timed(void* h,
void* data,
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
SRSRAN_API int rf_file_send_timed_multi(void* h,
void* data[4],
int nsamples,
time_t secs,
double frac_secs,
bool has_time_spec,
bool blocking,
bool is_start_of_burst,
bool is_end_of_burst);
/**
* @brief Dedicated function to open a file-based RF abstraction
* @param[out] h Resulting object handle
* @param[in] rx_files List of pre-opened FILE* for each RX channel; NULL to disable
* @param[in] tx_files List of pre-opened FILE* for each TX channel; NULL to disable
* @param[in] nof_channels Number of channels per direction
* @param[in] base_srate Sample rate of RX and TX files
* @return SRSRAN_SUCCESS on success, otherwise error code
*/
SRSRAN_API int rf_file_open_file(void** h, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate);
#endif // SRSRAN_RF_FILE_IMP_H

@ -0,0 +1,98 @@
/**
*
* \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 "rf_file_imp_trx.h"
#include <srsran/phy/utils/vector.h>
#include <stdlib.h>
#include <string.h>
int rf_file_rx_open(rf_file_rx_t* q, rf_file_opts_t opts)
{
int ret = SRSRAN_ERROR;
if (q) {
// Zero object
memset(q, 0, sizeof(rf_file_rx_t));
// Copy id
strncpy(q->id, opts.id, FILE_ID_STRLEN - 1);
q->id[FILE_ID_STRLEN - 1] = '\0';
// Assign file
q->file = opts.file;
// Configure formats
q->sample_format = opts.sample_format;
q->frequency_mhz = opts.frequency_mhz;
q->temp_buffer = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!q->temp_buffer) {
fprintf(stderr, "Error: allocating rx buffer\n");
goto clean_exit;
}
q->temp_buffer_convert = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!q->temp_buffer_convert) {
fprintf(stderr, "Error: allocating rx buffer\n");
goto clean_exit;
}
if (pthread_mutex_init(&q->mutex, NULL)) {
fprintf(stderr, "Error: creating mutex\n");
goto clean_exit;
}
q->running = true;
ret = SRSRAN_SUCCESS;
}
clean_exit:
return ret;
}
int rf_file_rx_baseband(rf_file_rx_t* q, cf_t* buffer, uint32_t nsamples)
{
uint32_t sample_sz = sizeof(cf_t);
int ret = fread(buffer, sample_sz, nsamples, q->file);
if (ret > 0) {
return ret;
} else {
return SRSRAN_ERROR_RX_EOF;
}
}
bool rf_file_rx_match_freq(rf_file_rx_t* q, uint32_t freq_hz)
{
bool ret = false;
if (q) {
ret = (q->frequency_mhz == 0 || q->frequency_mhz == freq_hz);
}
return ret;
}
void rf_file_rx_close(rf_file_rx_t* q)
{
rf_file_info(q->id, "Closing ...\n");
q->running = false;
if (q->temp_buffer) {
free(q->temp_buffer);
}
if (q->temp_buffer_convert) {
free(q->temp_buffer_convert);
}
// not touching q->file as we don't know if we need to close it ourselves
}

@ -0,0 +1,106 @@
/**
*
* \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_RF_FILE_IMP_TRX_H
#define SRSRAN_RF_FILE_IMP_TRX_H
#include "srsran/config.h"
#include <math.h>
#include <pthread.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
/* Definitions */
#define VERBOSE (0)
#define NSAMPLES2NBYTES(X) (((uint32_t)(X)) * sizeof(cf_t))
#define NBYTES2NSAMPLES(X) ((X) / sizeof(cf_t))
#define FILE_MAX_BUFFER_SIZE (NSAMPLES2NBYTES(3072000)) // 10 subframes at 20 MHz
#define FILE_TIMEOUT_MS (1000)
#define FILE_BASERATE_DEFAULT_HZ (23040000)
#define FILE_ID_STRLEN 16
#define FILE_MAX_GAIN_DB (30.0f)
#define FILE_MIN_GAIN_DB (0.0f)
typedef enum { FILERF_TYPE_FC32 = 0, FILERF_TYPE_SC16 } rf_file_format_t;
typedef struct {
char id[FILE_ID_STRLEN];
rf_file_format_t sample_format;
FILE* file;
uint64_t nsamples;
bool running;
pthread_mutex_t mutex;
cf_t* zeros;
void* temp_buffer_convert;
uint32_t frequency_mhz;
int32_t sample_offset;
} rf_file_tx_t;
typedef struct {
char id[FILE_ID_STRLEN];
rf_file_format_t sample_format;
FILE* file;
uint64_t nsamples;
bool running;
pthread_t thread;
pthread_mutex_t mutex;
cf_t* temp_buffer;
void* temp_buffer_convert;
uint32_t frequency_mhz;
} rf_file_rx_t;
typedef struct {
const char* id;
rf_file_format_t sample_format;
FILE* file;
uint32_t frequency_mhz;
} rf_file_opts_t;
/*
* Common functions
*/
SRSRAN_API void rf_file_info(char* id, const char* format, ...);
SRSRAN_API void rf_file_error(char* id, const char* format, ...);
SRSRAN_API int rf_file_handle_error(char* id, const char* text);
/*
* Transmitter functions
*/
SRSRAN_API int rf_file_tx_open(rf_file_tx_t* q, rf_file_opts_t opts);
SRSRAN_API int rf_file_tx_align(rf_file_tx_t* q, uint64_t ts);
SRSRAN_API int rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples);
SRSRAN_API int rf_file_tx_get_nsamples(rf_file_tx_t* q);
SRSRAN_API int rf_file_tx_zeros(rf_file_tx_t* q, uint32_t nsamples);
SRSRAN_API bool rf_file_tx_match_freq(rf_file_tx_t* q, uint32_t freq_hz);
SRSRAN_API void rf_file_tx_close(rf_file_tx_t* q);
/*
* Receiver functions
*/
SRSRAN_API int rf_file_rx_open(rf_file_rx_t* q, rf_file_opts_t opts);
SRSRAN_API int rf_file_rx_baseband(rf_file_rx_t* q, cf_t* buffer, uint32_t nsamples);
SRSRAN_API bool rf_file_rx_match_freq(rf_file_rx_t* q, uint32_t freq_hz);
SRSRAN_API void rf_file_rx_close(rf_file_rx_t* q);
#endif // SRSRAN_RF_FILE_IMP_TRX_H

@ -0,0 +1,189 @@
/**
*
* \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 "rf_file_imp_trx.h"
#include <errno.h>
#include <inttypes.h>
#include <srsran/config.h>
#include <srsran/phy/utils/vector.h>
#include <stdlib.h>
#include <string.h>
int rf_file_tx_open(rf_file_tx_t* q, rf_file_opts_t opts)
{
int ret = SRSRAN_ERROR;
if (q) {
// Zero object
memset(q, 0, sizeof(rf_file_tx_t));
// Copy id
strncpy(q->id, opts.id, FILE_ID_STRLEN - 1);
q->id[FILE_ID_STRLEN - 1] = '\0';
// Assign file
q->file = opts.file;
// Configure formats
q->sample_format = opts.sample_format;
q->frequency_mhz = opts.frequency_mhz;
q->temp_buffer_convert = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!q->temp_buffer_convert) {
fprintf(stderr, "Error: allocating tx buffer\n");
goto clean_exit;
}
if (pthread_mutex_init(&q->mutex, NULL)) {
fprintf(stderr, "Error: creating mutex\n");
goto clean_exit;
}
q->zeros = srsran_vec_malloc(FILE_MAX_BUFFER_SIZE);
if (!q->zeros) {
fprintf(stderr, "Error: allocating zeros\n");
goto clean_exit;
}
memset(q->zeros, 0, FILE_MAX_BUFFER_SIZE);
q->running = true;
ret = SRSRAN_SUCCESS;
}
clean_exit:
return ret;
}
static int _rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples)
{
int n = SRSRAN_ERROR;
// convert samples if necessary
void* buf = (buffer) ? buffer : q->zeros;
uint32_t sample_sz = sizeof(cf_t);
if (q->sample_format == FILERF_TYPE_SC16) {
buf = q->temp_buffer_convert;
sample_sz = 2 * sizeof(short);
srsran_vec_convert_fi((float*)buffer, INT16_MAX, (short*)q->temp_buffer_convert, 2 * nsamples);
}
size_t ret = fwrite(buf, (size_t)sample_sz, (size_t)nsamples, q->file);
if (ret < (size_t)nsamples) {
rf_file_error(q->id,
"[file] Error: transmitter expected %d bytes and sent %d. %s.\n",
NSAMPLES2NBYTES(nsamples),
ret,
strerror(errno));
n = SRSRAN_ERROR;
goto clean_exit;
}
// Increment sample counter
q->nsamples += nsamples;
n = nsamples;
clean_exit:
return n;
}
int rf_file_tx_align(rf_file_tx_t* q, uint64_t ts)
{
pthread_mutex_lock(&q->mutex);
int64_t nsamples = (int64_t)ts - (int64_t)q->nsamples;
if (nsamples > 0) {
rf_file_info(q->id, " - Detected Tx gap of %d samples.\n", nsamples);
_rf_file_tx_baseband(q, q->zeros, (uint32_t)nsamples);
}
pthread_mutex_unlock(&q->mutex);
return (int)nsamples;
}
int rf_file_tx_baseband(rf_file_tx_t* q, cf_t* buffer, uint32_t nsamples)
{
int n;
pthread_mutex_lock(&q->mutex);
if (q->sample_offset > 0) {
_rf_file_tx_baseband(q, q->zeros, (uint32_t)q->sample_offset);
q->sample_offset = 0;
} else if (q->sample_offset < 0) {
n = SRSRAN_MIN(-q->sample_offset, nsamples);
buffer += n;
nsamples -= n;
q->sample_offset += n;
if (nsamples == 0) {
return n;
}
}
n = _rf_file_tx_baseband(q, buffer, nsamples);
pthread_mutex_unlock(&q->mutex);
return n;
}
int rf_file_tx_get_nsamples(rf_file_tx_t* q)
{
pthread_mutex_lock(&q->mutex);
int ret = q->nsamples;
pthread_mutex_unlock(&q->mutex);
return ret;
}
int rf_file_tx_zeros(rf_file_tx_t* q, uint32_t nsamples)
{
pthread_mutex_lock(&q->mutex);
rf_file_info(q->id, " - Tx %d Zeros.\n", nsamples);
_rf_file_tx_baseband(q, q->zeros, (uint32_t)nsamples);
pthread_mutex_unlock(&q->mutex);
return (int)nsamples;
}
bool rf_file_tx_match_freq(rf_file_tx_t* q, uint32_t freq_hz)
{
bool ret = false;
if (q) {
ret = (q->frequency_mhz == 0 || q->frequency_mhz == freq_hz);
}
return ret;
}
void rf_file_tx_close(rf_file_tx_t* q)
{
rf_file_info(q->id, "Closing ...\n");
pthread_mutex_lock(&q->mutex);
q->running = false;
pthread_mutex_unlock(&q->mutex);
pthread_mutex_destroy(&q->mutex);
if (q->zeros) {
free(q->zeros);
}
if (q->temp_buffer_convert) {
free(q->temp_buffer_convert);
}
// not touching q->file as we don't know if we need to close it ourselves
}

@ -0,0 +1,327 @@
/**
*
* \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 "rf_file_imp.h"
#include "srsran/common/tsan_options.h"
#include "srsran/phy/common/timestamp.h"
#include "srsran/phy/utils/debug.h"
#include <complex.h>
#include <pthread.h>
#include <srsran/phy/common/phy_common.h>
#include <srsran/phy/utils/vector.h>
#include <stdlib.h>
#define PRINT_SAMPLES 0
#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[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;
void* ue_rx_thread_function(void* args)
{
char rf_args[RF_PARAM_LEN];
strncpy(rf_args, (char*)args, RF_PARAM_LEN - 1);
rf_args[RF_PARAM_LEN - 1] = 0;
// sleep(1);
printf("opening rx device with args=%s\n", rf_args);
if (srsran_rf_open_devname(&ue_radio, "file", rf_args, NOF_RX_ANT)) {
fprintf(stderr, "Error opening rf\n");
exit(-1);
}
// receive 5 subframes at once (i.e. mimic initial rx that receives one slot)
uint32_t num_slots = NUM_SF / 5;
uint32_t num_samps_per_slot = SF_LEN * 5;
uint32_t num_rxed_samps = 0;
for (uint32_t i = 0; i < num_slots; ++i) {
void* data_ptr[SRSRAN_MAX_PORTS] = {NULL};
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 rx device\n");
srsran_rf_close(&ue_radio);
return NULL;
}
void enb_tx_function(const char* tx_args, bool timed_tx)
{
char rf_args[RF_PARAM_LEN];
strncpy(rf_args, tx_args, RF_PARAM_LEN - 1);
rf_args[RF_PARAM_LEN - 1] = 0;
printf("opening tx device with args=%s\n", rf_args);
if (srsran_rf_open_devname(&enb_radio, "file", rf_args, NOF_RX_ANT)) {
fprintf(stderr, "Error opening rf\n");
exit(-1);
}
// 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[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};
for (int c = 0; c < NOF_RX_ANT; c++) {
data_ptr[c] = &enb_tx_buffer[c][num_txed_samples];
}
int ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false);
num_txed_samples += SF_LEN;
// from here on, all transmissions are timed relative to the last rx time
srsran_timestamp_t rx_time, tx_time;
for (uint32_t i = 0; i < NUM_SF - ((timed_tx) ? TX_OFFSET_MS : 1); ++i) {
// first recv samples
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
for (int c = 0; c < NOF_RX_ANT; c++) {
data_ptr[c] = &enb_tx_buffer[c][num_txed_samples];
}
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
// subframes)
srsran_timestamp_copy(&tx_time, &rx_time);
srsran_timestamp_add(&tx_time, 0, TX_OFFSET_MS * 1e-3);
ret = srsran_rf_send_timed_multi(
&enb_radio, (void**)data_ptr, SF_LEN, tx_time.full_secs, tx_time.frac_secs, true, true, false);
} else {
// normal tx
ret = srsran_rf_send_multi(&enb_radio, (void**)data_ptr, SF_LEN, true, true, false);
}
if (ret != SRSRAN_SUCCESS) {
fprintf(stderr, "Error sending data\n");
exit(-1);
}
num_txed_samples += SF_LEN;
}
printf("transmitted %d samples in %d subframes\n", num_txed_samples, NUM_SF);
printf("closing tx device\n");
srsran_rf_close(&enb_radio);
}
int run_test(const char* rx_args, const char* tx_args, bool timed_tx)
{
int ret = SRSRAN_ERROR;
// make sure we can receive in slots
if (NUM_SF % 5 != 0) {
fprintf(stderr, "number of subframes must be multiple of 5\n");
goto exit;
}
// write to file(s)
enb_tx_function(tx_args, timed_tx);
// read from file(s)
ue_rx_thread_function((void*)rx_args);
// channel-wise comparison
for (int c = 0; c < NOF_RX_ANT; c++) {
// subframe-wise compare tx'ed and rx'ed data (stop 3 subframes earlier for timed tx)
for (uint32_t i = 0; i < NUM_SF - (timed_tx ? 3 : 0); ++i) {
uint32_t sf_offet = 0;
if (timed_tx && i >= 1) {
// for timed transmission, the enb inserts 3 zero subframes after the first untimed tx
sf_offet = (TX_OFFSET_MS - 1) * SF_LEN;
}
#if PRINT_SAMPLES
// print first 10 samples for each SF
printf("enb_tx_buffer sf%d:\n", i);
srsran_vec_fprint_c(stdout, &enb_tx_buffer[c][i * SF_LEN], 10);
printf("ue_rx_buffer sf%d:\n", i);
srsran_vec_fprint_c(stdout, &ue_rx_buffer[c][sf_offet + i * SF_LEN], 10);
#endif
#if 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;
exit:
return ret;
}
int param_test(const char* args_param, const int num_channels)
{
char rf_args[RF_PARAM_LEN] = {};
strncpy(rf_args, (char*)args_param, RF_PARAM_LEN - 1);
rf_args[RF_PARAM_LEN - 1] = 0;
printf("opening tx device with args=%s\n", rf_args);
if (srsran_rf_open_devname(&enb_radio, "file", rf_args, num_channels)) {
fprintf(stderr, "Error opening rf\n");
return SRSRAN_ERROR;
}
srsran_rf_close(&enb_radio);
return SRSRAN_SUCCESS;
}
void create_file(const char* filename)
{
FILE* f = fopen(filename, "w");
fclose(f);
}
void remove_file(const char* filename)
{
remove(filename);
}
int main()
{
// create files for testing
create_file("rx_file0");
create_file("rx_file1");
create_file("rx_file2");
create_file("rx_file3");
// two RX files
if (param_test("rx_file=rx_file0,"
"rx_file1=rx_file1",
2)) {
fprintf(stderr, "Param test failed!\n");
return SRSRAN_ERROR;
}
// multiple RX files, no channel index provided
if (param_test("rx_file=rx_file0,"
"rx_file=rx_file1,"
"rx_file=rx_file2,"
"rx_file=rx_file3,"
"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_file0=rx_file0,"
"tx_file0=tx_file0,"
"base_srate=1.92e6",
1)) {
fprintf(stderr, "Param test failed!\n");
return SRSRAN_ERROR;
}
// two RX, two TX
if (param_test("rx_file0=rx_file0,"
"rx_file1=rx_file1,"
"tx_file0=tx_file0,"
"tx_file1=tx_file1",
2)) {
fprintf(stderr, "Param test failed!\n");
return SRSRAN_ERROR;
}
#if NOF_RX_ANT == 1
// single tx, single rx with continuous transmissions (no decimation, no timed tx)
if (run_test("rx_file=tx_file0,base_srate=1.92e6", "tx_file=tx_file0,base_srate=1.92e6", false) != SRSRAN_SUCCESS) {
fprintf(stderr, "Single tx, single rx test failed (no decimation, no timed tx)!\n");
return -1;
}
#endif
// up to 4 trx radios with continous tx (no decimation, no timed tx)
if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3,base_srate=1.92e6",
"tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3,base_srate=1.92e6",
false) != SRSRAN_SUCCESS) {
fprintf(stderr, "Multi TRx radio test failed (no decimation, no timed tx)!\n");
return -1;
}
// up to 4 trx radios with continous tx (with decimation, no timed tx)
if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3",
"tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3",
false) != SRSRAN_SUCCESS) {
fprintf(stderr, "Multi TRx radio test failed (with decimation, no timed tx)!\n");
return -1;
}
// up to 4 trx radios with continous tx (with decimation, timed tx)
if (run_test("rx_file=tx_file0,rx_file=tx_file1,rx_file=tx_file2,rx_file=tx_file3",
"tx_file=tx_file0,tx_file=tx_file1,tx_file=tx_file2,tx_file=tx_file3",
true) != SRSRAN_SUCCESS) {
fprintf(stderr, "Two TRx radio test failed (with decimation, timed tx)!\n");
return -1;
}
// clean workspace
remove_file("rx_file0");
remove_file("rx_file1");
remove_file("rx_file2");
remove_file("rx_file3");
remove_file("tx_file0");
remove_file("tx_file1");
remove_file("tx_file2");
remove_file("tx_file3");
fprintf(stdout, "Test passed!\n");
return SRSRAN_SUCCESS;
}

@ -148,6 +148,14 @@ int srsran_rf_open_devname(srsran_rf_t* rf, const char* devname, char* args, uin
return SRSRAN_ERROR;
}
int srsran_rf_open_file(srsran_rf_t* rf, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate)
{
rf->dev = &dev_file;
// file abstraction has custom "open" function with file-related args
return rf_file_open_file(&rf->handler, rx_files, tx_files, nof_channels, base_srate);
}
const char* srsran_rf_name(srsran_rf_t* rf)
{
return ((rf_dev_t*)rf->dev)->srsran_rf_devname(rf->handler);

@ -66,7 +66,7 @@ typedef struct {
pthread_mutex_t rx_gain_mutex;
} rf_zmq_handler_t;
void update_rates(rf_zmq_handler_t* handler, double srate);
static void update_rates(rf_zmq_handler_t* handler, double srate);
/*
* Static Atributes
@ -970,8 +970,7 @@ int rf_zmq_send_timed_multi(void* h,
}
// Scale according to current gain
// TODO: document baseband scaling for ZMQ with gain settings, etc. before enabling
// srsran_vec_sc_prod_cfc(buf, tx_gain, buf, nsamples_baseband);
srsran_vec_sc_prod_cfc(buf, tx_gain, buf, nsamples_baseband);
// Finally, transmit baseband
int n = rf_zmq_tx_baseband(&handler->transmitter[i], buf, nsamples_baseband);

@ -536,21 +536,10 @@ bool srsran_ssb_send(srsran_ssb_t* q, uint32_t sf_idx)
return (sf_idx % q->cfg.periodicity_ms == 0);
}
int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out)
static int ssb_encode(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, cf_t ssb_grid[SRSRAN_SSB_NOF_RE])
{
// Verify input parameters
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_encode) {
ERROR("SSB is not configured for encode");
return SRSRAN_ERROR;
}
uint32_t N_id_1 = SRSRAN_NID_1_NR(N_id);
uint32_t N_id_2 = SRSRAN_NID_2_NR(N_id);
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
// Put PSS
if (srsran_pss_nr_put(ssb_grid, N_id_2, q->cfg.beta_pss) < SRSRAN_SUCCESS) {
@ -589,6 +578,64 @@ int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* m
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
SRSRAN_API int
srsran_ssb_put_grid(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, cf_t* re_grid, uint32_t grid_bw_sc)
{
// Verify input parameters
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || re_grid == NULL ||
grid_bw_sc * SRSRAN_NRE < SRSRAN_SSB_BW_SUBC) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_encode) {
ERROR("SSB is not configured for encode");
return SRSRAN_ERROR;
}
// Put signals in SSB grid
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_encode(q, N_id, msg, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Putting SSB in grid");
return SRSRAN_ERROR;
}
// First symbol in the half frame
uint32_t l_first = q->l_first[msg->ssb_idx];
// Frequency offset fom the bottom of the grid
uint32_t f_offset = grid_bw_sc / 2 + q->f_offset - SRSRAN_SSB_BW_SUBC / 2;
// Put SSB grid in the actual resource grid
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
srsran_vec_cf_copy(
&re_grid[grid_bw_sc * (l_first + l) + f_offset], &ssb_grid[SRSRAN_SSB_BW_SUBC * l], SRSRAN_SSB_BW_SUBC);
}
return SRSRAN_SUCCESS;
}
int srsran_ssb_add(srsran_ssb_t* q, uint32_t N_id, const srsran_pbch_msg_nr_t* msg, const cf_t* in, cf_t* out)
{
// Verify input parameters
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || in == NULL || out == NULL) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_encode) {
ERROR("SSB is not configured for encode");
return SRSRAN_ERROR;
}
// Put signals in SSB grid
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
if (ssb_encode(q, N_id, msg, ssb_grid) < SRSRAN_SUCCESS) {
ERROR("Putting SSB in grid");
return SRSRAN_ERROR;
}
// Select start symbol from SSB candidate index
int t_offset = ssb_get_t_offset(q, msg->ssb_idx);
if (t_offset < SRSRAN_SUCCESS) {
@ -1141,6 +1188,46 @@ static int ssb_decode_pbch(srsran_ssb_t* q,
return SRSRAN_SUCCESS;
}
int srsran_ssb_decode_grid(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,
uint32_t ssb_idx,
const cf_t* re_grid,
uint32_t grid_bw_sc,
srsran_pbch_msg_nr_t* msg)
{
// Verify input parameters
if (q == NULL || N_id >= SRSRAN_NOF_NID_NR || msg == NULL || re_grid == NULL || grid_bw_sc < SRSRAN_SSB_BW_SUBC) {
return SRSRAN_ERROR_INVALID_INPUTS;
}
if (!q->args.enable_encode) {
ERROR("SSB is not configured for encode");
return SRSRAN_ERROR;
}
// First symbol in the half frame
uint32_t l_first = q->l_first[ssb_idx];
// Frequency offset fom the bottom of the grid
uint32_t f_offset = grid_bw_sc / 2 + q->f_offset - SRSRAN_SSB_BW_SUBC / 2;
// Get SSB grid from resource grid
cf_t ssb_grid[SRSRAN_SSB_NOF_RE] = {};
for (uint32_t l = 0; l < SRSRAN_SSB_DURATION_NSYMB; l++) {
srsran_vec_cf_copy(
&ssb_grid[SRSRAN_SSB_BW_SUBC * l], &re_grid[grid_bw_sc * (l_first + l) + f_offset], SRSRAN_SSB_BW_SUBC);
}
// Decode PBCH
if (ssb_decode_pbch(q, N_id, n_hf, ssb_idx, ssb_grid, msg) < SRSRAN_SUCCESS) {
ERROR("Error decoding");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int srsran_ssb_decode_pbch(srsran_ssb_t* q,
uint32_t N_id,
uint32_t n_hf,

@ -145,6 +145,9 @@ target_link_libraries(ssb_measure_test srsran_phy)
add_executable(ssb_decode_test ssb_decode_test.c)
target_link_libraries(ssb_decode_test srsran_phy)
add_executable(ssb_grid_test ssb_grid_test.c)
target_link_libraries(ssb_grid_test srsran_phy)
# For 1.0 GHz and 3.5 GHz Center frequencies
foreach (CELL_FREQ 1000000 3500000)
# For each supported Cell/Carrier subcarrier spacing
@ -165,6 +168,10 @@ foreach (CELL_FREQ 1000000 3500000)
# Test SSB PBCH decoding
add_nr_test(ssb_decode_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_decode_test
-F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN})
# Test SSB grid put/get decoding
add_nr_test(ssb_grid_test_${CELL_FREQ}000_${CELL_SCS}_${SSB_FREQ}000_${SSB_SCS}_${SSB_PATTERN} ssb_grid_test
-F ${CELL_FREQ}000 -S ${CELL_SCS} -f ${SSB_FREQ}000 -s ${SSB_SCS} -P ${SSB_PATTERN})
endforeach ()
endforeach ()
endforeach ()

@ -38,7 +38,7 @@ static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_1
static double carrier_freq_hz = 3.5e9 + 960e3;
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
static double ssb_freq_hz = 3.5e9;
static srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_A;
static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_A;
// Channel parameters
static cf_t wideband_gain = 1.0f + 0.5 * I;

@ -29,7 +29,7 @@
#include <stdlib.h>
// NR parameters
static srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_C;
static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_C;
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
static srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_TDD;

@ -0,0 +1,180 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2021 Software Radio Systems Limited
*
* By using this file, you agree to the terms and conditions set
* forth in the LICENSE file which can be found at the top level of
* the distribution.
*
*/
#include "srsran/common/test_common.h"
#include "srsran/phy/channel/ch_awgn.h"
#include "srsran/phy/sync/ssb.h"
#include "srsran/phy/utils/debug.h"
#include "srsran/phy/utils/vector.h"
#include <complex.h>
#include <getopt.h>
#include <srsran/phy/utils/random.h>
#include <stdlib.h>
// NR parameters
static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_15kHz;
static double carrier_freq_hz = 3.5e9 + 960e3;
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
static double ssb_freq_hz = 3.5e9;
static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_A;
static uint32_t ssb_idx = 0; // SSB candidate index to test
static uint32_t pci = 123; // N_id
// Test context
static srsran_random_t random_gen = NULL;
static double srate_hz = 0.0f; // Base-band sampling rate
static cf_t* grid = NULL; // Resource grid
static uint32_t grid_bw_sc = 52 * SRSRAN_NRE; // Resource grid bandwidth in subcarriers
static void usage(char* prog)
{
printf("Usage: %s [v]\n", prog);
printf("\t-s SSB subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(ssb_scs));
printf("\t-f SSB center frequency [default, %.3f MHz]\n", ssb_freq_hz / 1e6);
printf("\t-S cell/carrier subcarrier spacing [default, %s kHz]\n", srsran_subcarrier_spacing_to_str(carrier_scs));
printf("\t-F cell/carrier center frequency in Hz [default, %.3f MHz]\n", carrier_freq_hz / 1e6);
printf("\t-P SSB pattern [default, %s]\n", srsran_ssb_pattern_to_str(ssb_pattern));
printf("\t-v [set srsran_verbose to debug, default none]\n");
}
static void parse_args(int argc, char** argv)
{
int opt;
while ((opt = getopt(argc, argv, "SsFfPv")) != -1) {
switch (opt) {
case 's':
ssb_scs = srsran_subcarrier_spacing_from_str(argv[optind]);
if (ssb_scs == srsran_subcarrier_spacing_invalid) {
ERROR("Invalid SSB subcarrier spacing %s\n", argv[optind]);
exit(-1);
}
break;
case 'f':
ssb_freq_hz = strtod(argv[optind], NULL);
break;
case 'S':
carrier_scs = srsran_subcarrier_spacing_from_str(argv[optind]);
if (carrier_scs == srsran_subcarrier_spacing_invalid) {
ERROR("Invalid Cell/Carrier subcarrier spacing %s\n", argv[optind]);
exit(-1);
}
break;
case 'F':
carrier_freq_hz = strtod(argv[optind], NULL);
break;
case 'P':
ssb_pattern = srsran_ssb_pattern_fom_str(argv[optind]);
break;
case 'v':
increase_srsran_verbose_level();
break;
default:
usage(argv[0]);
exit(-1);
}
}
}
static void gen_pbch_msg(srsran_pbch_msg_nr_t* pbch_msg)
{
// Default all to zero
SRSRAN_MEM_ZERO(pbch_msg, srsran_pbch_msg_nr_t, 1);
// Generate payload
srsran_random_bit_vector(random_gen, pbch_msg->payload, SRSRAN_PBCH_MSG_NR_SZ);
pbch_msg->ssb_idx = ssb_idx;
pbch_msg->crc = true;
}
static int test_case(srsran_ssb_t* ssb)
{
// 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);
// Build PBCH message
srsran_pbch_msg_nr_t pbch_msg_tx = {};
gen_pbch_msg(&pbch_msg_tx);
// Print encoded PBCH message
char str[512] = {};
srsran_pbch_msg_info(&pbch_msg_tx, str, sizeof(str));
INFO("test_case - encoded pci=%d %s", pci, str);
// Add the SSB base-band
TESTASSERT(srsran_ssb_put_grid(ssb, pci, &pbch_msg_tx, grid, grid_bw_sc) == SRSRAN_SUCCESS);
// Decode
srsran_pbch_msg_nr_t pbch_msg_rx = {};
TESTASSERT(srsran_ssb_decode_grid(ssb, pci, pbch_msg_tx.hrf, pbch_msg_tx.ssb_idx, grid, grid_bw_sc, &pbch_msg_rx) ==
SRSRAN_SUCCESS);
// Print decoded PBCH message
srsran_pbch_msg_info(&pbch_msg_rx, str, sizeof(str));
INFO("test_case - decoded pci=%d %s crc=%s", pci, str, pbch_msg_rx.crc ? "OK" : "KO");
// Assert PBCH message CRC
TESTASSERT(pbch_msg_rx.crc);
TESTASSERT(memcmp(&pbch_msg_rx, &pbch_msg_tx, sizeof(srsran_pbch_msg_nr_t)) == 0);
return SRSRAN_SUCCESS;
}
int main(int argc, char** argv)
{
int ret = SRSRAN_ERROR;
parse_args(argc, argv);
random_gen = srsran_random_init(1234);
srate_hz = (double)SRSRAN_SUBC_SPACING_NR(carrier_scs) * srsran_min_symbol_sz_rb(grid_bw_sc / SRSRAN_NRE);
grid = srsran_vec_cf_malloc(grid_bw_sc * SRSRAN_NSYMB_PER_SLOT_NR);
srsran_ssb_t ssb = {};
srsran_ssb_args_t ssb_args = {};
ssb_args.enable_encode = true;
ssb_args.enable_decode = true;
ssb_args.enable_search = true;
if (grid == NULL) {
ERROR("Malloc");
goto clean_exit;
}
if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) {
ERROR("Init");
goto clean_exit;
}
if (test_case(&ssb) != SRSRAN_SUCCESS) {
ERROR("test case failed");
goto clean_exit;
}
ret = SRSRAN_SUCCESS;
clean_exit:
srsran_random_free(random_gen);
srsran_ssb_free(&ssb);
if (grid) {
free(grid);
}
return ret;
}

@ -33,7 +33,7 @@ static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spacing_1
static double carrier_freq_hz = 3.5e9 + 960e3;
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_30kHz;
static double ssb_freq_hz = 3.5e9;
static srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_A;
static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_A;
// Channel parameters
static int32_t delay_n = 2;

@ -36,7 +36,7 @@ static srsran_subcarrier_spacing_t carrier_scs = srsran_subcarrier_spaci
static double center_frequency_hz = 3.5e9;
static srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_15kHz;
static double ssb_frequency_hz = 3.5e9 - 960e3;
static srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_C;
static srsran_ssb_pattern_t ssb_pattern = SRSRAN_SSB_PATTERN_C;
static srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_TDD;
// Test and channel parameters

@ -845,6 +845,38 @@ TEST(
free(z);
srsran_cfo_free(&srsran_cfo);)
// This test compares the clipping method used for the CFR module in its default configuration to the original CFR
// algorithm. The original algorithm can still be used by defining CFR_PEAK_EXTRACTION in the CFR module.
TEST(
srsran_vec_gen_clip_env, MALLOC(cf_t, x); MALLOC(float, x_abs); MALLOC(float, env); float thres = 0.5f;
float alpha = 0.5f;
cf_t gold = 0.0f;
for (int i = 0; i < block_size; i++) {
x[i] = RANDOM_F();
env[i] = 0.0f;
x_abs[i] = cabsf(x[i]);
}
// current implementation generates an amplitude envelope which is then multiplied with the signal
TEST_CALL(srsran_vec_gen_clip_env(x_abs, thres, alpha, env, block_size))
// Recreates the original method for clipping the signal, skipping the low-pass filtering
for (int i = 0; i < block_size; i++) {
if (x_abs[i] <= thres) {
gold = x[i];
} else {
cf_t peak = x[i] - (thres * x[i] / x_abs[i]); // extract the peak
gold = x[i] - alpha * peak; // subtract the peak from the signal, scaled by alpha
}
// Compare the two clipping methods by applying the envelope to x and determining the error
mse += cabsf(gold - env[i] * x[i]);
} if (isnormal(mse)) { mse /= block_size; }
free(x);
free(x_abs);
free(env);)
int main(int argc, char** argv)
{
char func_names[MAX_FUNCTIONS][32];
@ -1023,6 +1055,10 @@ int main(int argc, char** argv)
test_srsran_cfo_correct_change(func_names[func_count], &timmings[func_count][size_count], block_size);
func_count++;
passed[func_count][size_count] =
test_srsran_vec_gen_clip_env(func_names[func_count], &timmings[func_count][size_count], block_size);
func_count++;
sizes[size_count] = block_size;
size_count++;
}

@ -589,8 +589,12 @@ int32_t srsran_vec_dot_prod_sss(const int16_t* x, const int16_t* y, const uint32
float srsran_vec_avg_power_cf(const cf_t* x, const uint32_t len)
{
if (!len) {
return 0;
} else {
return crealf(srsran_vec_dot_prod_conj_ccc(x, x, len)) / len;
}
}
float srsran_vec_avg_power_sf(const int16_t* x, const uint32_t len)
{
@ -636,6 +640,17 @@ float srsran_vec_avg_power_bf(const int8_t* x, const uint32_t len)
return acc;
}
float srsran_vec_avg_power_ff(const float* x, const uint32_t len)
{
if (!len) {
return 0;
} else {
float pwr_symb_avg = srsran_vec_dot_prod_fff(x, x, len);
pwr_symb_avg /= (float)len;
return pwr_symb_avg;
}
}
// Correlation assumes zero-mean x and y
float srsran_vec_corr_ccc(const cf_t* x, cf_t* y, const uint32_t len)
{
@ -846,3 +861,38 @@ float srsran_vec_estimate_frequency(const cf_t* x, int len)
{
return srsran_vec_estimate_frequency_simd(x, len);
}
// TODO: implement with SIMD
void srsran_vec_gen_clip_env(const float* x_abs, const float thres, const float alpha, float* env, const int len)
{
for (int i = 0; i < len; i++) {
env[i] = (x_abs[i] > thres) ? (1 - alpha) + alpha * thres / x_abs[i] : 1;
}
}
float srsran_vec_papr_c(const cf_t* in, const int len)
{
uint32_t max = srsran_vec_max_abs_ci(in, len);
float peak = SRSRAN_CSQABS(in[max]);
return peak / srsran_vec_avg_power_cf(in, len);
}
float srsran_vec_acpr_c(const cf_t* x_f, const uint32_t win_pos_len, const uint32_t win_neg_len, const uint32_t len)
{
// The adjacent channel cannot extend beyond the FFT len
const uint32_t ch_len = win_pos_len + win_neg_len;
const uint32_t adj_ch_len = ch_len > len / 2 ? len - ch_len : ch_len;
// Integrate positive half of the signal power spectrum
float signal_pwr = srsran_vec_dot_prod_conj_ccc(x_f, x_f, win_pos_len);
// Integrate negative halt of the signal power spectrum
signal_pwr += srsran_vec_dot_prod_conj_ccc(x_f + len - win_neg_len, x_f + len - win_neg_len, win_neg_len);
const float adj_ch_pwr = srsran_vec_dot_prod_conj_ccc(x_f + win_pos_len, x_f + win_pos_len, adj_ch_len);
if (isnormal(signal_pwr)) {
return adj_ch_pwr / signal_pwr;
} else {
return 0;
}
}

@ -112,12 +112,31 @@ int radio::init(const rf_args_t& args, phy_interface_radio* phy_)
rx_channel_mapping.set_config(nof_channels_x_dev, nof_antennas);
// Init and start Radios
if (args.device_name != "file" || device_args_list[0] != "auto") {
// regular RF device
for (uint32_t device_idx = 0; device_idx < (uint32_t)device_args_list.size(); device_idx++) {
if (not open_dev(device_idx, args.device_name, device_args_list[device_idx])) {
logger.error("Error opening RF device %d", device_idx);
return SRSRAN_ERROR;
}
}
} else {
// file-based RF device abstraction using pre-opened FILE* objects
if (args.rx_files == nullptr && args.tx_files == nullptr) {
logger.error("File-based RF device abstraction requested, but no files provided");
return SRSRAN_ERROR;
}
for (uint32_t device_idx = 0; device_idx < (uint32_t)device_args_list.size(); device_idx++) {
if (not open_dev(device_idx,
&args.rx_files[device_idx * nof_channels_x_dev],
&args.tx_files[device_idx * nof_channels_x_dev],
nof_channels_x_dev,
args.srate_hz)) {
logger.error("Error opening RF device %d", device_idx);
return SRSRAN_ERROR;
}
}
}
is_start_of_burst = true;
is_initialized = true;
@ -482,6 +501,29 @@ bool radio::open_dev(const uint32_t& device_idx, const std::string& device_name,
return true;
}
bool radio::open_dev(const uint32_t &device_idx, FILE** rx_files, FILE** tx_files, uint32_t nof_channels, uint32_t base_srate)
{
srsran_rf_t* rf_device = &rf_devices[device_idx];
srsran::console("Opening %d channels in RF device abstraction\n");
if (srsran_rf_open_file(rf_device, rx_files, tx_files, nof_channels, base_srate)) {
logger.error("Error opening RF device abstraction");
return false;
}
// Suppress radio stdout
srsran_rf_suppress_stdout(rf_device);
// Register handler for processing O/U/L
srsran_rf_register_error_handler(rf_device, rf_msg_callback, this);
// Get device info
rf_info[device_idx] = *srsran_rf_get_info(rf_device);
return true;
}
bool radio::tx_dev(const uint32_t& device_idx, rf_buffer_interface& buffer, const srsran_timestamp_t& tx_time_)
{
uint32_t nof_samples = buffer.get_nof_samples();

@ -108,7 +108,7 @@ void rlc::get_metrics(rlc_metrics_t& m, const uint32_t nof_tti)
double rx_rate_mbps = (nof_tti > 0) ? ((metrics.num_rx_pdu_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0;
double tx_rate_mbps = (nof_tti > 0) ? ((metrics.num_tx_pdu_bytes * 8 / (double)1e6) / (nof_tti / 1000.0)) : 0.0;
logger.info("lcid=%d, rx_rate_mbps=%4.2f (real=%4.2f), tx_rate_mbps=%4.2f (real=%4.2f)",
logger.debug("lcid=%d, rx_rate_mbps=%4.2f (real=%4.2f), tx_rate_mbps=%4.2f (real=%4.2f)",
it->first,
rx_rate_mbps,
rx_rate_mbps_real_time,
@ -120,7 +120,7 @@ void rlc::get_metrics(rlc_metrics_t& m, const uint32_t nof_tti)
// Add multicast metrics
for (rlc_map_t::iterator it = rlc_array_mrb.begin(); it != rlc_array_mrb.end(); ++it) {
rlc_bearer_metrics_t metrics = it->second->get_metrics();
logger.info("MCH_LCID=%d, rx_rate_mbps=%4.2f",
logger.debug("MCH_LCID=%d, rx_rate_mbps=%4.2f",
it->first,
(metrics.num_rx_pdu_bytes * 8 / static_cast<double>(1e6)) / secs.count());
m.bearer[it->first] = metrics;

@ -20,6 +20,7 @@
*/
#include "srsran/support/emergency_handlers.h"
#include "srsran/config.h"
#include "srsran/support/srsran_assert.h"
namespace {
@ -38,7 +39,7 @@ static constexpr unsigned max_handlers = 12;
static handler_instance registered_handlers[max_handlers];
static std::atomic<unsigned> num_handlers;
void add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data)
int add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* data)
{
// Reserve a slot in the array.
auto pos = num_handlers.fetch_add(1);
@ -46,13 +47,25 @@ void add_emergency_cleanup_handler(emergency_cleanup_callback callback, void* da
// Check if we have space in the array.
if (pos >= max_handlers) {
srsran_assert(0, "Exceeded the emergency cleanup handler registered limit");
return;
return SRSRAN_ERROR;
}
// Order is important here: write last the callback member as it is used to signal that the handler is valid when
// reading the array.
registered_handlers[pos].data.store(data);
registered_handlers[pos].callback.store(callback);
return pos;
}
void remove_emergency_cleanup_handler(int id)
{
if (id < 0 || static_cast<unsigned>(id) >= num_handlers) {
srsran_assert(0, "Invalid emergency handler id");
return;
}
registered_handlers[id].callback.store(nullptr);
}
void execute_emergency_cleanup_handlers()

@ -326,6 +326,32 @@ enable = false
#fd_hz = -750.0
#init_time_s = 0.0
#####################################################################
# CFR configuration options
#
# The CFR module provides crest factor reduction for the transmitted signal.
#
# enable: Enable or disable the CFR. Default: disabled
#
# mode: manual: CFR threshold is set by cfr_manual_thres (default).
# auto_ema: CFR threshold is adaptive based on the signal PAPR. Power avg. with Exponential Moving Average.
# The time constant of the averaging can be tweaked with the ema_alpha parameter.
# auto_cma: CFR threshold is adaptive based on the signal PAPR. Power avg. with Cumulative Moving Average.
# Use with care, as CMA's increasingly slow response may be unsuitable for most use cases.
#
# strength: Ratio between amplitude-limited vs unprocessed signal (0 to 1). Default: 1
# manual_thres: Fixed manual clipping threshold for CFR manual mode. Default: 0.5
# auto_target_papr: Signal PAPR target (in dB) in CFR auto modes. output PAPR can be higher due to peak smoothing. Default: 8
# ema_alpha: Alpha coefficient for the power average in auto_ema mode. Default: 1/7
#
#####################################################################
[cfr]
#enable = false
#mode = manual
#manual_thres = 0.5
#strength = 1
#auto_target_papr = 8
#ema_alpha = 0.0143
#####################################################################
# Expert configuration options

@ -151,6 +151,8 @@ public:
// eNodeB command interface
void cmd_cell_gain(uint32_t cell_id, float gain) override;
void cmd_cell_measure() override;
void toggle_padding() override;
void tti_clock() override;

@ -47,6 +47,8 @@ public:
virtual void get_metrics(std::vector<phy_metrics_t>& m) = 0;
virtual void cmd_cell_gain(uint32_t cell_idx, float gain_db) = 0;
virtual void cmd_cell_measure() = 0;
};
} // namespace srsenb

@ -75,6 +75,7 @@ public:
void get_metrics(std::vector<phy_metrics_t>& metrics) override;
void cmd_cell_gain(uint32_t cell_id, float gain_db) override;
void cmd_cell_measure() override;
void radio_overflow() override{};
void radio_failure() override{};

@ -131,7 +131,7 @@ public:
cc_idx -= cell_list_lte.size();
if (cc_idx < cell_list_nr.size()) {
ret = cell_list_nr[cc_idx].ul_freq_hz;
ret = cell_list_nr[cc_idx].carrier.ul_center_frequency_hz;
}
return ret;
@ -146,7 +146,7 @@ public:
cc_idx -= cell_list_lte.size();
if (cc_idx < cell_list_nr.size()) {
ret = cell_list_nr[cc_idx].dl_freq_hz;
ret = cell_list_nr[cc_idx].carrier.dl_center_frequency_hz;
}
return ret;
@ -175,6 +175,45 @@ public:
return c;
}
void set_cell_measure_trigger()
{
// Trigger on LTE cell
for (auto it_lte = cell_list_lte.begin(); it_lte != cell_list_lte.end(); ++it_lte) {
it_lte->dl_measure = true;
}
// Trigger on NR cell
for (auto it_nr = cell_list_nr.begin(); it_nr != cell_list_nr.end(); ++it_nr) {
it_nr->dl_measure = true;
}
}
bool get_cell_measure_trigger(uint32_t cc_idx)
{
if (cc_idx < cell_list_lte.size()) {
return cell_list_lte.at(cc_idx).dl_measure;
}
cc_idx -= cell_list_lte.size();
if (cc_idx < cell_list_nr.size()) {
return cell_list_nr.at(cc_idx).dl_measure;
}
return false;
}
void clear_cell_measure_trigger(uint32_t cc_idx)
{
if (cc_idx < cell_list_lte.size()) {
cell_list_lte.at(cc_idx).dl_measure = false;
}
cc_idx -= cell_list_lte.size();
if (cc_idx < cell_list_nr.size()) {
cell_list_nr.at(cc_idx).dl_measure = false;
}
}
void set_cell_gain(uint32_t cell_id, float gain_db)
{
// Find LTE cell
@ -217,6 +256,11 @@ public:
return 0.0f;
}
// Common CFR configuration
srsran_cfr_cfg_t cfr_config = {};
void set_cfr_config(srsran_cfr_cfg_t cfr_cfg) { cfr_config = cfr_cfg; }
srsran_cfr_cfg_t get_cfr_config() { return cfr_config; }
// Common Physical Uplink DMRS configuration
srsran_refsignal_dmrs_pusch_cfg_t dmrs_pusch_cfg = {};

@ -41,10 +41,20 @@ struct phy_cell_cfg_t {
uint32_t root_seq_idx;
uint32_t num_ra_preambles;
float gain_db;
bool dl_measure;
};
typedef std::vector<phy_cell_cfg_t> phy_cell_cfg_list_t;
struct cfr_args_t {
bool enable = false;
srsran_cfr_mode_t mode = SRSRAN_CFR_THR_MANUAL;
float manual_thres = 0.5f;
float strength = 1.0f;
float auto_target_papr = 8.0f;
float ema_alpha = 1.0f / (float)SRSRAN_CP_NORM_NSYMB;
};
struct phy_args_t {
std::string type;
srsran::phy_log_args_t log;
@ -65,6 +75,7 @@ struct phy_args_t {
bool extended_cp = false;
srsran::channel::args_t dl_channel_args;
srsran::channel::args_t ul_channel_args;
cfr_args_t cfr_args;
};
struct phy_cfg_t {
@ -78,6 +89,8 @@ struct phy_cfg_t {
asn1::rrc::pusch_cfg_common_s pusch_cnfg;
asn1::rrc::pucch_cfg_common_s pucch_cnfg;
asn1::rrc::srs_ul_cfg_common_c srs_ul_cnfg;
srsran_cfr_cfg_t cfr_config;
};
} // namespace srsenb

@ -228,6 +228,11 @@ void enb::cmd_cell_gain(uint32_t cell_id, float gain)
phy->cmd_cell_gain(cell_id, gain);
}
void enb::cmd_cell_measure()
{
phy->cmd_cell_measure();
}
std::string enb::get_build_mode()
{
return std::string(srsran_get_build_mode());

@ -1016,7 +1016,7 @@ static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cf
parse_opt_field(cell_cfg.phy_cell.rf_port, cellroot, "rf_port");
HANDLEPARSERCODE(parse_required_field(cell_cfg.phy_cell.carrier.pci, cellroot, "pci"));
HANDLEPARSERCODE(parse_required_field(cell_cfg.phy_cell.cell_id, cellroot, "cell_id"));
HANDLEPARSERCODE(parse_required_field(cell_cfg.phy_cell.root_seq_idx, cellroot, "root_seq_idx"));
HANDLEPARSERCODE(parse_required_field(cell_cfg.prach_root_seq_idx, cellroot, "root_seq_idx"));
HANDLEPARSERCODE(parse_required_field(cell_cfg.tac, cellroot, "tac"));
cell_cfg.phy_cell.carrier.pci = cell_cfg.phy_cell.carrier.pci % SRSRAN_NOF_NID_NR;
@ -1099,9 +1099,9 @@ static int parse_nr_cell_list(all_args_t* args, rrc_nr_cfg_t* rrc_cfg_nr, rrc_cf
if (it->ul_arfcn != 0) {
// Check if ul_arfcn is valid for the given band
bool ul_arfcn_valid = false;
std::vector<uint32_t> bands = band_helper.get_bands_nr(it->ul_arfcn);
for (uint32_t band_idx = 0; band_idx < bands.size(); band_idx++) {
if (bands.at(band_idx) == it->band) {
std::vector<uint32_t> ul_bands = band_helper.get_bands_nr(it->ul_arfcn);
for (uint32_t band_idx = 0; band_idx < ul_bands.size(); band_idx++) {
if (ul_bands.at(band_idx) == it->band) {
ul_arfcn_valid = true;
}
}
@ -1162,6 +1162,30 @@ int parse_cell_cfg(all_args_t* args_, srsran_cell_t* cell)
return SRSRAN_SUCCESS;
}
// Parse the relevant CFR configuration params
int parse_cfr_args(all_args_t* args, srsran_cfr_cfg_t* cfr_config)
{
cfr_config->cfr_enable = args->phy.cfr_args.enable;
cfr_config->cfr_mode = args->phy.cfr_args.mode;
cfr_config->alpha = args->phy.cfr_args.strength;
cfr_config->manual_thr = args->phy.cfr_args.manual_thres;
cfr_config->max_papr_db = args->phy.cfr_args.auto_target_papr;
cfr_config->ema_alpha = args->phy.cfr_args.ema_alpha;
if (!srsran_cfr_params_valid(cfr_config)) {
fprintf(stderr,
"Invalid CFR parameters: cfr_mode=%d, alpha=%.2f, manual_thr=%.2f, \n "
"max_papr_db=%.2f, ema_alpha=%.2f\n",
cfr_config->cfr_mode,
cfr_config->alpha,
cfr_config->manual_thr,
cfr_config->max_papr_db,
cfr_config->ema_alpha);
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t* phy_cfg_)
{
// Parse config files
@ -1281,6 +1305,12 @@ int parse_cfg_files(all_args_t* args_, rrc_cfg_t* rrc_cfg_, rrc_nr_cfg_t* rrc_nr
}
}
// Parse CFR args
if (parse_cfr_args(args_, &phy_cfg_->cfr_config) < SRSRAN_SUCCESS) {
fprintf(stderr, "Error parsing CFR configuration\n");
return SRSRAN_ERROR;
}
return SRSRAN_SUCCESS;
}
@ -1572,12 +1602,8 @@ int set_derived_args_nr(all_args_t* args_, rrc_nr_cfg_t* rrc_nr_cfg_, phy_cfg_t*
// phy_cell_cfg.root_seq_idx = cfg.root_seq_idx;
// PRACH
cfg.phy_cell.prach.hs_flag = phy_cfg_->prach_cnfg.prach_cfg_info.high_speed_flag;
// PDSCH
cfg.phy_cell.pdsch.rs_power = phy_cfg_->pdsch_cnfg.ref_sig_pwr;
cfg.phy_cell.pdsch.p_b = phy_cfg_->pdsch_cnfg.p_b;
cfg.pdsch_rs_power = phy_cfg_->pdsch_cnfg.ref_sig_pwr;
}
rrc_nr_cfg_->enb_id = args_->enb.enb_id;
rrc_nr_cfg_->mcc = args_->stack.s1ap.mcc;

@ -65,6 +65,7 @@ void parse_args(all_args_t* args, int argc, char* argv[])
string mcc;
string mnc;
string enb_id;
string cfr_mode;
bool use_standard_lte_rates = false;
// Command line only options
@ -221,6 +222,14 @@ void parse_args(all_args_t* args, int argc, char* argv[])
("channel.ul.hst.fd_hz", bpo::value<float>(&args->phy.ul_channel_args.hst_fd_hz)->default_value(+750.0f), "Doppler frequency in Hz")
("channel.ul.hst.init_time_s", bpo::value<float>(&args->phy.ul_channel_args.hst_init_time_s)->default_value(0), "Initial time in seconds")
/* CFR section */
("cfr.enable", bpo::value<bool>(&args->phy.cfr_args.enable)->default_value(args->phy.cfr_args.enable), "CFR enable")
("cfr.mode", bpo::value<string>(&cfr_mode)->default_value("manual"), "CFR mode")
("cfr.manual_thres", bpo::value<float>(&args->phy.cfr_args.manual_thres)->default_value(args->phy.cfr_args.manual_thres), "Fixed manual clipping threshold for CFR manual mode")
("cfr.strength", bpo::value<float>(&args->phy.cfr_args.strength)->default_value(args->phy.cfr_args.strength), "CFR ratio between amplitude-limited vs original signal (0 to 1)")
("cfr.auto_target_papr", bpo::value<float>(&args->phy.cfr_args.auto_target_papr)->default_value(args->phy.cfr_args.auto_target_papr), "Signal PAPR target (in dB) in CFR auto modes")
("cfr.ema_alpha", bpo::value<float>(&args->phy.cfr_args.ema_alpha)->default_value(args->phy.cfr_args.ema_alpha), "Alpha coefficient for the power average in auto_ema mode (0 to 1)")
/* Expert section */
("expert.metrics_period_secs", bpo::value<float>(&args->general.metrics_period_secs)->default_value(1.0), "Periodicity for metrics in seconds.")
("expert.metrics_csv_enable", bpo::value<bool>(&args->general.metrics_csv_enable)->default_value(false), "Write metrics to CSV file.")
@ -378,6 +387,20 @@ void parse_args(all_args_t* args, int argc, char* argv[])
exit(1);
}
// convert CFR mode
if (!cfr_mode.empty()) {
if (cfr_mode == "manual") {
args->phy.cfr_args.mode = SRSRAN_CFR_THR_MANUAL;
} else if (cfr_mode == "auto_cma") {
args->phy.cfr_args.mode = SRSRAN_CFR_THR_AUTO_CMA;
} else if (cfr_mode == "auto_ema") {
args->phy.cfr_args.mode = SRSRAN_CFR_THR_AUTO_EMA;
} else {
cout << "Error, invalid CFR mode: " << cfr_mode << endl;
exit(1);
}
}
// Apply all_level to any unset layers
if (vm.count("log.all_level")) {
if (!vm.count("log.rf_level")) {
@ -474,6 +497,9 @@ static void execute_cmd(metrics_stdout* metrics, srsenb::enb_command_interface*
cout << "Enter t to restart trace." << endl;
}
metrics->toggle_print(do_metrics);
} else if (cmd[0] == "m") {
// Trigger cell measurements
control->cmd_cell_measure();
} else if (cmd[0] == "sleep") {
if (cmd.size() != 2) {
cout << "Usage: " << cmd[0] << " [number of seconds]" << endl;
@ -516,6 +542,7 @@ static void execute_cmd(metrics_stdout* metrics, srsenb::enb_command_interface*
} else {
cout << "Available commands: " << endl;
cout << " t: starts console trace" << endl;
cout << " m: downlink signal measurements" << endl;
cout << " q: quit srsenb" << endl;
cout << " cell_gain: set relative cell gain" << endl;
cout << " sleep: pauses the commmand line operation for a given time in seconds" << endl;

@ -19,6 +19,8 @@
*
*/
#include <iomanip>
#include "srsran/common/threads.h"
#include "srsran/srsran.h"
@ -91,6 +93,7 @@ void cc_worker::init(phy_common* phy_, uint32_t cc_idx_)
srsran_cell_t cell = phy_->get_cell(cc_idx);
uint32_t nof_prb = phy_->get_nof_prb(cc_idx);
uint32_t sf_len = SRSRAN_SF_LEN_PRB(nof_prb);
srsran_cfr_cfg_t cfr_config = phy_->get_cfr_config();
// Init cell here
for (uint32_t p = 0; p < phy->get_nof_ports(cc_idx); p++) {
@ -115,6 +118,10 @@ void cc_worker::init(phy_common* phy_, uint32_t cc_idx_)
ERROR("Error initiating ENB DL (cc=%d)", cc_idx);
return;
}
if (srsran_enb_dl_set_cfr(&enb_dl, &cfr_config) < SRSRAN_SUCCESS) {
ERROR("Error setting the CFR");
return;
}
if (srsran_enb_ul_init(&enb_ul, signal_buffer_rx[0], nof_prb)) {
ERROR("Error initiating ENB UL");
return;
@ -260,6 +267,20 @@ void cc_worker::work_dl(const srsran_dl_sf_cfg_t& dl_sf_cfg,
srsran_vec_sc_prod_cfc(signal_buffer_tx[i], scale, signal_buffer_tx[i], sf_len);
}
}
// Measure PAPR if flag was triggered
bool cell_meas_flag = phy->get_cell_measure_trigger(cc_idx);
if (cell_meas_flag) {
uint32_t sf_len = SRSRAN_SF_LEN_PRB(enb_dl.cell.nof_prb);
for (uint32_t i = 0; i < enb_dl.cell.nof_ports; i++) {
// PAPR measure
float papr_db = 10.0f * log10(srsran_vec_papr_c(signal_buffer_tx[i], sf_len));
std::cout << "Cell #" << cc_idx << " port #" << i << " PAPR = " << std::setprecision(4) << papr_db << " dB "
<< std::endl;
}
// clear measurement flag on cell
phy->clear_cell_measure_trigger(cc_idx);
}
}
bool cc_worker::decode_pusch_rnti(stack_interface_phy_lte::ul_sched_grant_t& ul_grant,

@ -174,6 +174,9 @@ int phy::init_lte(const phy_args_t& args,
workers_common.params = args;
workers_common.init(cfg.phy_cell_cfg, cfg.phy_cell_cfg_nr, radio, stack_lte_);
if (cfg.cfr_config.cfr_enable) {
workers_common.set_cfr_config(cfg.cfr_config);
}
parse_common_config(cfg);
@ -281,6 +284,11 @@ void phy::cmd_cell_gain(uint32_t cell_id, float gain_db)
workers_common.set_cell_gain(cell_id, gain_db);
}
void phy::cmd_cell_measure()
{
workers_common.set_cell_measure_trigger();
}
/***** RRC->PHY interface **********/
void phy::set_config(uint16_t rnti, const phy_rrc_cfg_list_t& phy_cfg_list)

@ -1218,8 +1218,8 @@ private:
srslog::basic_logger& logger;
args_t args = {}; ///< Test arguments
srsenb::phy_args_t phy_args; ///< PHY arguments
srsenb::phy_cfg_t phy_cfg; ///< eNb Cell/Carrier configuration
srsenb::phy_args_t phy_args = {}; ///< PHY arguments
srsenb::phy_cfg_t phy_cfg = {}; ///< eNb Cell/Carrier configuration
srsenb::phy_interface_rrc_lte::phy_rrc_cfg_list_t phy_rrc_cfg; ///< UE PHY configuration
uint64_t tti_counter = 0;

@ -31,14 +31,8 @@ struct phy_cell_cfg_nr_t {
srsran_carrier_nr_t carrier;
uint32_t rf_port;
uint32_t cell_id;
double dl_freq_hz;
double ul_freq_hz;
uint32_t root_seq_idx;
uint32_t num_ra_preambles;
float gain_db;
srsran_pdcch_cfg_nr_t pdcch = {}; ///< Common CORESET and Search Space configuration
srsran_pdsch_cfg_t pdsch = {};
srsran_prach_cfg_t prach = {};
bool dl_measure;
};
using phy_cell_cfg_list_nr_t = std::vector<phy_cell_cfg_nr_t>;

@ -72,7 +72,6 @@ struct sched_nr_bwp_cfg_t {
srsran_sch_hl_cfg_nr_t pdsch = {};
srsran_sch_hl_cfg_nr_t pusch = {};
srsran_pucch_nr_hl_cfg_t pucch = {};
srsran_prach_cfg_t prach = {};
srsran_harq_ack_cfg_hl_t harq_ack = {};
uint32_t rar_window_size = 10; // See TS 38.331, ra-ResponseWindow: {1, 2, 4, 8, 10, 20, 40, 80}
uint32_t numerology_idx = 0;

@ -149,14 +149,11 @@ private:
// derived
uint32_t slot_dur_ms = 0;
srslog::basic_logger& logger;
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::sib1_s sib1;
asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_l_ sibs;
srsran::unique_byte_buffer_t mib_buffer = nullptr;
std::vector<srsran::unique_byte_buffer_t> sib_buffer;
std::unique_ptr<const asn1::rrc_nr::cell_group_cfg_s> master_cell_group;
srsran::phy_cfg_nr_t default_phy_ue_cfg_nr;

@ -30,37 +30,31 @@
namespace srsenb {
// TODO: Make this common to NR and LTE
struct rrc_nr_cfg_sr_t {
uint32_t period;
// asn1::rrc::sched_request_cfg_c::setup_s_::dsr_trans_max_e_ dsr_max;
uint32_t nof_prb;
uint32_t sf_mapping[80];
uint32_t nof_subframes;
};
// Cell/Sector configuration for NR cells
struct rrc_cell_cfg_nr_t {
phy_cell_cfg_nr_t phy_cell; // already contains all PHY-related parameters (i.e. RF port, PCI, etc.)
uint32_t tac; // Tracking area code
uint32_t dl_arfcn; // DL freq already included in phy_cell
uint32_t ul_arfcn; // UL freq also in phy_cell
uint32_t dl_arfcn; // DL freq already included in carrier
uint32_t ul_arfcn; // UL freq also in carrier
uint32_t dl_absolute_freq_point_a; // derived from DL ARFCN
uint32_t ul_absolute_freq_point_a; // derived from UL ARFCN
uint32_t band;
uint32_t prach_root_seq_idx;
uint32_t num_ra_preambles;
uint32_t coreset0_idx; // Table 13-{1,...15} row index
srsran_duplex_mode_t duplex_mode;
double ssb_freq_hz;
uint32_t ssb_absolute_freq_point; // derived from DL ARFCN (SSB arfcn)
srsran_subcarrier_spacing_t ssb_scs;
srsran_ssb_patern_t ssb_pattern;
srsran_ssb_pattern_t ssb_pattern;
asn1::rrc_nr::pdcch_cfg_common_s pdcch_cfg_common;
asn1::rrc_nr::pdcch_cfg_s pdcch_cfg_ded;
int8_t pdsch_rs_power;
};
typedef std::vector<rrc_cell_cfg_nr_t> rrc_cell_list_nr_t;
struct rrc_nr_cfg_t {
rrc_nr_cfg_sr_t sr_cfg;
rrc_cfg_cqi_t cqi_cfg;
rrc_cell_list_nr_t cell_list;
uint32_t inactivity_timeout_ms = 100000;
uint32_t enb_id;

@ -26,6 +26,10 @@
namespace srsenb {
// Helper methods
uint32_t coreset_get_bw(const asn1::rrc_nr::ctrl_res_set_s& coreset);
int coreset_get_pdcch_nr_max_candidates(const asn1::rrc_nr::ctrl_res_set_s& coreset, uint32_t aggregation_level);
void generate_default_nr_cell(rrc_cell_cfg_nr_t& cell);
int set_derived_nr_cell_params(bool is_sa, rrc_cell_cfg_nr_t& cell);
@ -33,10 +37,7 @@ int set_derived_nr_rrc_params(rrc_nr_cfg_t& rrc_cfg);
// Tests to ensure validity of config
int check_nr_cell_cfg_valid(const rrc_cell_cfg_nr_t& cell, bool is_sa);
int check_nr_phy_cell_cfg_valid(const phy_cell_cfg_nr_t& phy_cell);
int check_nr_pdcch_cfg_valid(const srsran_pdcch_cfg_nr_t& pdcch);
int check_rrc_nr_cfg_valid(const rrc_nr_cfg_t& cfg);
} // namespace srsenb

@ -40,6 +40,12 @@ public:
asn1::rrc_nr::sib1_s sib1;
srsran::unique_byte_buffer_t packed_sib1;
asn1::rrc_nr::subcarrier_spacing_e ssb_scs;
srsran_ssb_pattern_t ssb_pattern;
double ssb_center_freq_hz;
double dl_freq_hz;
bool is_standalone;
const asn1::rrc_nr::serving_cell_cfg_common_sib_s& serv_cell_cfg_common() const
{
return sib1.serving_cell_cfg_common;
@ -72,6 +78,8 @@ private:
std::vector<std::unique_ptr<du_cell_config> > cells;
};
void fill_phy_pdcch_cfg_common(const du_cell_config& cell, srsran_pdcch_cfg_nr_t* pdcch);
} // namespace srsenb
#endif // SRSRAN_RRC_NR_DU_MANAGER_H

@ -84,7 +84,9 @@ srsran::phy_cfg_nr_t get_common_ue_phy_cfg(const sched_nr_cell_cfg_t& cfg)
ue_phy_cfg.pdsch = cfg.bwps[0].pdsch;
ue_phy_cfg.pusch = cfg.bwps[0].pusch;
ue_phy_cfg.pucch = cfg.bwps[0].pucch;
ue_phy_cfg.prach = cfg.bwps[0].prach;
srsran::make_phy_rach_cfg(cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(),
cfg.tdd_ul_dl_cfg_common.has_value() ? SRSRAN_DUPLEX_MODE_TDD : SRSRAN_DUPLEX_MODE_FDD,
&ue_phy_cfg.prach);
ue_phy_cfg.harq_ack = cfg.bwps[0].harq_ack;
ue_phy_cfg.csi = {}; // disable CSI until RA is complete
ue_phy_cfg.carrier.pci = cfg.pci;

@ -69,6 +69,8 @@ inline sched_nr_cell_cfg_t get_default_cell_cfg(const srsran::phy_cfg_nr_t& phy_
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.ul_cfg_common.init_ul_bwp.rach_cfg_common_present = true;
srsran::fill_rach_cfg_common(phy_cfg.prach, cell_cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common.set_setup());
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;
@ -86,7 +88,6 @@ inline sched_nr_cell_cfg_t get_default_cell_cfg(const srsran::phy_cfg_nr_t& phy_
cell_cfg.bwps[0].pdsch = phy_cfg.pdsch;
cell_cfg.bwps[0].pusch = phy_cfg.pusch;
cell_cfg.bwps[0].pucch = phy_cfg.pucch;
cell_cfg.bwps[0].prach = phy_cfg.prach;
cell_cfg.bwps[0].harq_ack = phy_cfg.harq_ack;
cell_cfg.bwps[0].rb_width = phy_cfg.carrier.nof_prb;

@ -508,32 +508,6 @@ int fill_csi_meas_from_enb_cfg(const rrc_nr_cfg_t& cfg, csi_meas_cfg_s& csi_meas
return SRSRAN_SUCCESS;
}
/// Fill InitDlBwp with gNB config
int fill_pdcch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pdcch_cfg_s& pdcch_cfg)
{
auto& cell_cfg = cfg.cell_list.at(cc);
for (uint32_t ss_idx = 1; ss_idx < SRSRAN_UE_DL_NR_MAX_NOF_SEARCH_SPACE; ss_idx++) {
if (cell_cfg.phy_cell.pdcch.search_space_present[ss_idx]) {
auto& search_space_cfg = cell_cfg.phy_cell.pdcch.search_space[ss_idx];
if (search_space_cfg.type != srsran_search_space_type_ue) {
// Only add UE-specific search spaces at this stage
continue;
}
// Add UE-specific SearchSpace
pdcch_cfg.search_spaces_to_add_mod_list.push_back({});
set_search_space_from_phy_cfg(search_space_cfg, pdcch_cfg.search_spaces_to_add_mod_list.back());
// Add CORESET associated with SearchSpace
uint32_t coreset_id = search_space_cfg.coreset_id;
auto& coreset_cfg = cell_cfg.phy_cell.pdcch.coreset[coreset_id];
pdcch_cfg.ctrl_res_set_to_add_mod_list.push_back({});
set_coreset_from_phy_cfg(coreset_cfg, pdcch_cfg.ctrl_res_set_to_add_mod_list.back());
}
}
return SRSRAN_SUCCESS;
}
void fill_pdsch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pdsch_cfg_s& out)
{
out.dmrs_dl_for_pdsch_map_type_a_present = true;
@ -581,7 +555,7 @@ void fill_pdsch_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pdsch_cfg
int fill_init_dl_bwp_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_dl_ded_s& init_dl_bwp)
{
init_dl_bwp.pdcch_cfg_present = true;
HANDLE_ERROR(fill_pdcch_cfg_from_enb_cfg(cfg, cc, init_dl_bwp.pdcch_cfg.set_setup()));
init_dl_bwp.pdcch_cfg.set_setup() = cfg.cell_list[cc].pdcch_cfg_ded;
init_dl_bwp.pdsch_cfg_present = true;
fill_pdsch_cfg_from_enb_cfg(cfg, cc, init_dl_bwp.pdsch_cfg.set_setup());
@ -755,23 +729,7 @@ int fill_serv_cell_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, serving_ce
return SRSRAN_SUCCESS;
}
int fill_pdcch_cfg_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, pdcch_cfg_common_s& pdcch_cfg_common)
{
pdcch_cfg_common.common_ctrl_res_set_present = true;
set_coreset_from_phy_cfg(cfg.cell_list[cc].phy_cell.pdcch.coreset[1], pdcch_cfg_common.common_ctrl_res_set);
pdcch_cfg_common.common_search_space_list.push_back({});
set_search_space_from_phy_cfg(cfg.cell_list[cc].phy_cell.pdcch.search_space[1],
pdcch_cfg_common.common_search_space_list.back());
pdcch_cfg_common.ra_search_space_present = true;
pdcch_cfg_common.ra_search_space = cfg.cell_list[cc].phy_cell.pdcch.ra_search_space.id;
if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) {
pdcch_cfg_common.ext = false;
}
return SRSRAN_SUCCESS;
}
void fill_pdcch_cfg_common(const rrc_nr_cfg_t& cfg, uint32_t cc, pdcch_cfg_common_s& out);
/// Fill FrequencyInfoDL with gNB config
int fill_freq_info_dl_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, freq_info_dl_s& freq_info_dl)
@ -803,9 +761,8 @@ int fill_freq_info_dl_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, freq_in
int fill_init_dl_bwp_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_dl_common_s& init_dl_bwp)
{
init_dl_bwp.pdcch_cfg_common_present = true;
HANDLE_ERROR(fill_pdcch_cfg_common_from_enb_cfg(cfg, cc, init_dl_bwp.pdcch_cfg_common.set_setup()));
fill_pdcch_cfg_common(cfg, cc, init_dl_bwp.pdcch_cfg_common.set_setup());
// TODO: ADD missing fields
return SRSRAN_SUCCESS;
}
@ -848,11 +805,48 @@ int fill_freq_info_ul_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, freq_in
return SRSRAN_SUCCESS;
}
int fill_rach_cfg_common(const rrc_nr_cfg_t& cfg, uint32_t cc, rach_cfg_common_s& rach)
{
// rach-ConfigGeneric
rach.rach_cfg_generic.prach_cfg_idx = 0;
if (cfg.cell_list[cc].duplex_mode == SRSRAN_DUPLEX_MODE_TDD) {
// Note: Give more time margin to fit RAR
rach.rach_cfg_generic.prach_cfg_idx = 8;
}
rach.rach_cfg_generic.msg1_fdm.value = rach_cfg_generic_s::msg1_fdm_opts::one;
rach.rach_cfg_generic.msg1_freq_start = 1; // zero not supported with current PRACH implementation
rach.rach_cfg_generic.zero_correlation_zone_cfg = 0;
rach.rach_cfg_generic.preamb_rx_target_pwr = -110;
rach.rach_cfg_generic.preamb_trans_max.value = rach_cfg_generic_s::preamb_trans_max_opts::n7;
rach.rach_cfg_generic.pwr_ramp_step.value = rach_cfg_generic_s::pwr_ramp_step_opts::db4;
rach.rach_cfg_generic.ra_resp_win.value = rach_cfg_generic_s::ra_resp_win_opts::sl10;
// totalNumberOfRA-Preambles
if (cfg.cell_list[cc].num_ra_preambles != 64) {
rach.total_nof_ra_preambs_present = true;
rach.total_nof_ra_preambs = cfg.cell_list[cc].num_ra_preambles;
}
// ssb-perRACH-OccasionAndCB-PreamblesPerSSB
rach.ssb_per_rach_occasion_and_cb_preambs_per_ssb_present = true;
if (not asn1::number_to_enum(rach.ssb_per_rach_occasion_and_cb_preambs_per_ssb.set_one(),
cfg.cell_list[cc].num_ra_preambles)) {
get_logger(cfg).error("Invalid number of RA preambles=%d", cfg.cell_list[cc].num_ra_preambles);
return -1;
}
rach.ra_contention_resolution_timer.value = rach_cfg_common_s::ra_contention_resolution_timer_opts::sf64;
rach.prach_root_seq_idx.set_l839() = cfg.cell_list[cc].prach_root_seq_idx;
rach.restricted_set_cfg.value = rach_cfg_common_s::restricted_set_cfg_opts::unrestricted_set;
return SRSRAN_SUCCESS;
}
/// Fill InitUlBwp with gNB config
int fill_init_ul_bwp_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_ul_common_s& init_ul_bwp)
{
init_ul_bwp.rach_cfg_common_present = true;
set_rach_cfg_common(cfg.cell_list[cc].phy_cell.prach, init_ul_bwp.rach_cfg_common.set_setup());
HANDLE_ERROR(fill_rach_cfg_common(cfg, cc, init_ul_bwp.rach_cfg_common.set_setup()));
// TODO: Add missing fields
@ -880,7 +874,7 @@ int fill_serv_cell_common_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, ser
{
auto& cell_cfg = cfg.cell_list.at(cc);
serv_common.ss_pbch_block_pwr = cell_cfg.phy_cell.pdsch.rs_power;
serv_common.ss_pbch_block_pwr = cell_cfg.pdsch_rs_power;
serv_common.n_timing_advance_offset_present = true;
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;
@ -935,7 +929,9 @@ int fill_recfg_with_sync_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, recf
/// Fill spCellConfig with gNB config
int fill_sp_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, sp_cell_cfg_s& sp_cell)
{
if (not cfg.is_standalone) {
sp_cell.recfg_with_sync_present = true;
}
HANDLE_ERROR(fill_recfg_with_sync_from_enb_cfg(cfg, cc, sp_cell.recfg_with_sync));
sp_cell.sp_cell_cfg_ded_present = true;
@ -1063,7 +1059,6 @@ int fill_master_cell_cfg_from_enb_cfg(const rrc_nr_cfg_t& cfg, uint32_t cc, asn1
// spCellConfig -- Need M
out.sp_cell_cfg_present = true;
fill_sp_cell_cfg_from_enb_cfg(cfg, cc, out.sp_cell_cfg);
out.sp_cell_cfg.recfg_with_sync_present = false;
return SRSRAN_SUCCESS;
}
@ -1095,26 +1090,26 @@ int fill_mib_from_enb_cfg(const rrc_cell_cfg_nr_t& cell_cfg, asn1::rrc_nr::mib_s
return SRSRAN_SUCCESS;
}
void fill_pdcch_cfg_common(const rrc_cell_cfg_nr_t& cell_cfg, pdcch_cfg_common_s& cfg)
// Called for SA and NSA
void fill_pdcch_cfg_common(const rrc_nr_cfg_t& cfg, uint32_t cc, pdcch_cfg_common_s& out)
{
cfg.ctrl_res_set_zero_present = true; // may be disabled later if called by sib1 generation
cfg.ctrl_res_set_zero = 0;
cfg.common_ctrl_res_set_present = false;
auto& cell_cfg = cfg.cell_list[cc];
cfg.search_space_zero_present = true;
cfg.search_space_zero = 0;
out.ctrl_res_set_zero_present = false;
out.search_space_zero_present = false;
cfg.common_search_space_list.resize(1);
set_search_space_from_phy_cfg(cell_cfg.phy_cell.pdcch.search_space[1], cfg.common_search_space_list[0]);
out.common_ctrl_res_set_present = cell_cfg.pdcch_cfg_common.common_ctrl_res_set_present;
out.common_ctrl_res_set = cell_cfg.pdcch_cfg_common.common_ctrl_res_set;
out.common_search_space_list = cell_cfg.pdcch_cfg_common.common_search_space_list;
cfg.search_space_sib1_present = true;
cfg.search_space_sib1 = 0;
cfg.search_space_other_sys_info_present = true;
cfg.search_space_other_sys_info = 1;
cfg.paging_search_space_present = true;
cfg.paging_search_space = 1;
cfg.ra_search_space_present = true;
cfg.ra_search_space = 1;
out.search_space_sib1_present = true;
out.search_space_sib1 = 0;
out.search_space_other_sys_info_present = true;
out.search_space_other_sys_info = 1;
out.paging_search_space_present = true;
out.paging_search_space = 1;
out.ra_search_space_present = true;
out.ra_search_space = 1;
}
void fill_pdsch_cfg_common(const rrc_cell_cfg_nr_t& cell_cfg, pdsch_cfg_common_s& cfg)
@ -1124,78 +1119,84 @@ void fill_pdsch_cfg_common(const rrc_cell_cfg_nr_t& cell_cfg, pdsch_cfg_common_s
cfg.pdsch_time_domain_alloc_list[0].start_symbol_and_len = 40;
}
void fill_init_dl_bwp(const rrc_cell_cfg_nr_t& cell_cfg, bwp_dl_common_s& cfg)
// Called for SA
void fill_init_dl_bwp(const rrc_nr_cfg_t& cfg, uint32_t cc, bwp_dl_common_s& out)
{
cfg.generic_params.location_and_bw = 14025;
cfg.generic_params.subcarrier_spacing = (subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs;
auto& cell_cfg = cfg.cell_list[cc];
out.generic_params.location_and_bw = 14025;
out.generic_params.subcarrier_spacing = (subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs;
cfg.pdcch_cfg_common_present = true;
fill_pdcch_cfg_common(cell_cfg, cfg.pdcch_cfg_common.set_setup());
cfg.pdsch_cfg_common_present = true;
fill_pdsch_cfg_common(cell_cfg, cfg.pdsch_cfg_common.set_setup());
out.pdcch_cfg_common_present = true;
fill_pdcch_cfg_common(cfg, cc, out.pdcch_cfg_common.set_setup());
out.pdsch_cfg_common_present = true;
fill_pdsch_cfg_common(cell_cfg, out.pdsch_cfg_common.set_setup());
}
void fill_dl_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, dl_cfg_common_sib_s& cfg)
void fill_dl_cfg_common_sib(const rrc_nr_cfg_t& cfg, uint32_t cc, dl_cfg_common_sib_s& out)
{
auto& cell_cfg = cfg.cell_list[cc];
uint32_t scs_hz = SRSRAN_SUBC_SPACING_NR(cell_cfg.phy_cell.carrier.scs);
uint32_t prb_bw = scs_hz * SRSRAN_NRE;
srsran::srsran_band_helper band_helper;
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;
out.freq_info_dl.freq_band_list.resize(1);
out.freq_info_dl.freq_band_list[0].freq_band_ind_nr_present = true;
out.freq_info_dl.freq_band_list[0].freq_band_ind_nr = cell_cfg.band;
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 = cell_cfg.phy_cell.carrier.offset_to_carrier;
cfg.freq_info_dl.scs_specific_carrier_list[0].subcarrier_spacing =
out.freq_info_dl.offset_to_point_a = offset_point_a_prbs;
out.freq_info_dl.scs_specific_carrier_list.resize(1);
out.freq_info_dl.scs_specific_carrier_list[0].offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier;
out.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;
out.freq_info_dl.scs_specific_carrier_list[0].carrier_bw = cell_cfg.phy_cell.carrier.nof_prb;
fill_init_dl_bwp(cell_cfg, cfg.init_dl_bwp);
fill_init_dl_bwp(cfg, cc, out.init_dl_bwp);
// disable InitialBWP-Only fields
cfg.init_dl_bwp.pdcch_cfg_common.setup().ctrl_res_set_zero_present = false;
cfg.init_dl_bwp.pdcch_cfg_common.setup().search_space_zero_present = false;
out.init_dl_bwp.pdcch_cfg_common.setup().ctrl_res_set_zero_present = false;
out.init_dl_bwp.pdcch_cfg_common.setup().search_space_zero_present = false;
cfg.bcch_cfg.mod_period_coeff.value = bcch_cfg_s::mod_period_coeff_opts::n4;
out.bcch_cfg.mod_period_coeff.value = bcch_cfg_s::mod_period_coeff_opts::n4;
cfg.pcch_cfg.default_paging_cycle.value = paging_cycle_opts::rf128;
cfg.pcch_cfg.nand_paging_frame_offset.set_one_t();
cfg.pcch_cfg.ns.value = pcch_cfg_s::ns_opts::one;
out.pcch_cfg.default_paging_cycle.value = paging_cycle_opts::rf128;
out.pcch_cfg.nand_paging_frame_offset.set_one_t();
out.pcch_cfg.ns.value = pcch_cfg_s::ns_opts::one;
}
void fill_ul_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, ul_cfg_common_sib_s& cfg)
void fill_ul_cfg_common_sib(const rrc_nr_cfg_t& cfg, uint32_t cc, ul_cfg_common_sib_s& out)
{
auto& cell_cfg = cfg.cell_list[cc];
srsran::srsran_band_helper band_helper;
cfg.freq_info_ul.freq_band_list.resize(1);
cfg.freq_info_ul.freq_band_list[0].freq_band_ind_nr_present = true;
cfg.freq_info_ul.freq_band_list[0].freq_band_ind_nr = cell_cfg.band;
out.freq_info_ul.freq_band_list.resize(1);
out.freq_info_ul.freq_band_list[0].freq_band_ind_nr_present = true;
out.freq_info_ul.freq_band_list[0].freq_band_ind_nr = cell_cfg.band;
cfg.freq_info_ul.absolute_freq_point_a_present = true;
cfg.freq_info_ul.absolute_freq_point_a =
out.freq_info_ul.absolute_freq_point_a_present = true;
out.freq_info_ul.absolute_freq_point_a =
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 = cell_cfg.phy_cell.carrier.offset_to_carrier;
cfg.freq_info_ul.scs_specific_carrier_list[0].subcarrier_spacing =
out.freq_info_ul.scs_specific_carrier_list.resize(1);
out.freq_info_ul.scs_specific_carrier_list[0].offset_to_carrier = cell_cfg.phy_cell.carrier.offset_to_carrier;
out.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;
out.freq_info_ul.scs_specific_carrier_list[0].carrier_bw = cell_cfg.phy_cell.carrier.nof_prb;
cfg.freq_info_ul.p_max_present = true;
cfg.freq_info_ul.p_max = 10;
out.freq_info_ul.p_max_present = true;
out.freq_info_ul.p_max = 10;
cfg.init_ul_bwp.generic_params.location_and_bw = 14025;
cfg.init_ul_bwp.generic_params.subcarrier_spacing.value =
out.init_ul_bwp.generic_params.location_and_bw = 14025;
out.init_ul_bwp.generic_params.subcarrier_spacing.value =
(subcarrier_spacing_opts::options)cell_cfg.phy_cell.carrier.scs;
cfg.init_ul_bwp.rach_cfg_common_present = true;
set_rach_cfg_common(cell_cfg.phy_cell.prach, cfg.init_ul_bwp.rach_cfg_common.set_setup());
out.init_ul_bwp.rach_cfg_common_present = true;
fill_rach_cfg_common(cfg, cc, out.init_ul_bwp.rach_cfg_common.set_setup());
cfg.init_ul_bwp.pusch_cfg_common_present = true;
pusch_cfg_common_s& pusch = cfg.init_ul_bwp.pusch_cfg_common.set_setup();
out.init_ul_bwp.pusch_cfg_common_present = true;
pusch_cfg_common_s& pusch = out.init_ul_bwp.pusch_cfg_common.set_setup();
pusch.pusch_time_domain_alloc_list.resize(1);
pusch.pusch_time_domain_alloc_list[0].k2_present = true;
pusch.pusch_time_domain_alloc_list[0].k2 = 4;
@ -1204,39 +1205,41 @@ void fill_ul_cfg_common_sib(const rrc_cell_cfg_nr_t& cell_cfg, ul_cfg_common_sib
pusch.p0_nominal_with_grant_present = true;
pusch.p0_nominal_with_grant = -76;
cfg.init_ul_bwp.pucch_cfg_common_present = true;
pucch_cfg_common_s& pucch = cfg.init_ul_bwp.pucch_cfg_common.set_setup();
out.init_ul_bwp.pucch_cfg_common_present = true;
pucch_cfg_common_s& pucch = out.init_ul_bwp.pucch_cfg_common.set_setup();
pucch.pucch_res_common_present = true;
pucch.pucch_res_common = 11;
pucch.pucch_group_hop.value = pucch_cfg_common_s::pucch_group_hop_opts::neither;
pucch.p0_nominal_present = true;
pucch.p0_nominal = -90;
cfg.time_align_timer_common.value = time_align_timer_opts::infinity;
out.time_align_timer_common.value = time_align_timer_opts::infinity;
}
int 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_nr_cfg_t& cfg, uint32_t cc, serving_cell_cfg_common_sib_s& out)
{
fill_dl_cfg_common_sib(cell_cfg, cfg.dl_cfg_common);
auto& cell_cfg = cfg.cell_list[cc];
fill_dl_cfg_common_sib(cfg, cc, out.dl_cfg_common);
cfg.ul_cfg_common_present = true;
fill_ul_cfg_common_sib(cell_cfg, cfg.ul_cfg_common);
out.ul_cfg_common_present = true;
fill_ul_cfg_common_sib(cfg, cc, out.ul_cfg_common);
cfg.ssb_positions_in_burst.in_one_group.from_number(0x80);
out.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::ms10;
out.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;
out.n_timing_advance_offset_present = true;
out.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);
out.tdd_ul_dl_cfg_common_present = true;
fill_tdd_ul_dl_config_common(cell_cfg, out.tdd_ul_dl_cfg_common);
}
cfg.ss_pbch_block_pwr = cell_cfg.phy_cell.pdsch.rs_power;
out.ss_pbch_block_pwr = cell_cfg.pdsch_rs_power;
return SRSRAN_SUCCESS;
}
@ -1283,7 +1286,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;
HANDLE_ERROR(fill_serv_cell_cfg_common_sib(cell_cfg, sib1.serving_cell_cfg_common));
HANDLE_ERROR(fill_serv_cell_cfg_common_sib(cfg, cc, 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;

@ -80,17 +80,16 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_,
du_cfg = std::make_unique<du_config_manager>(cfg);
for (uint32_t i = 0; i < cfg.cell_list.size(); ++i) {
du_cfg->add_cell();
int ret = du_cfg->add_cell();
srsran_assert(ret == SRSRAN_SUCCESS, "Failed to configure NR cell %d", i);
}
// Generate cell config structs
cell_ctxt.reset(new cell_ctxt_t{});
if (cfg.is_standalone) {
std::unique_ptr<cell_group_cfg_s> master_cell_group{new cell_group_cfg_s{}};
cell_ctxt = std::make_unique<cell_ctxt_t>();
std::unique_ptr<cell_group_cfg_s> master_cell_group = std::make_unique<cell_group_cfg_s>();
int ret = fill_master_cell_cfg_from_enb_cfg(cfg, 0, *master_cell_group);
srsran_assert(ret == SRSRAN_SUCCESS, "Failed to configure MasterCellGroup");
cell_ctxt->master_cell_group = std::move(master_cell_group);
}
// derived
slot_dur_ms = 1;
@ -100,20 +99,6 @@ int rrc_nr::init(const rrc_nr_cfg_t& cfg_,
return SRSRAN_ERROR;
}
// Fill base ASN1 cell config.
int ret = fill_sp_cell_cfg_from_enb_cfg(cfg, UE_PSCELL_CC_IDX, base_sp_cell_cfg);
srsran_assert(ret == SRSRAN_SUCCESS, "Failed to configure cell");
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 = &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");
config_phy(); // if PHY is not yet initialized, config will be stored and applied on initialization
config_mac();
@ -197,16 +182,15 @@ int rrc_nr::add_user(uint16_t rnti, uint32_t pcell_cc_idx, bool start_msg3_timer
{
if (users.contains(rnti) == 0) {
// If in the ue ctor, "start_msg3_timer" is set to true, this will start the MSG3 RX TIMEOUT at ue creation
users.insert(rnti, std::unique_ptr<ue>(new ue(this, rnti, pcell_cc_idx, start_msg3_timer)));
users.insert(rnti, std::make_unique<ue>(this, rnti, pcell_cc_idx, start_msg3_timer));
rlc->add_user(rnti);
pdcp->add_user(rnti);
logger.info("Added new user rnti=0x%x", rnti);
return SRSRAN_SUCCESS;
} else {
}
logger.error("Adding user rnti=0x%x (already exists)", rnti);
return SRSRAN_ERROR;
}
}
/* @brief PUBLIC function, gets called by mac_nr::rach_detected
*
@ -296,16 +280,17 @@ void rrc_nr::config_phy()
{
srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {};
common_cfg.carrier = cfg.cell_list[0].phy_cell.carrier;
common_cfg.pdcch = cfg.cell_list[0].phy_cell.pdcch;
common_cfg.prach = cfg.cell_list[0].phy_cell.prach;
fill_phy_pdcch_cfg_common(du_cfg->cell(0), &common_cfg.pdcch);
bool ret = srsran::fill_phy_pdcch_cfg(
cell_ctxt->master_cell_group->sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(), &common_cfg.pdcch);
srsran_assert(ret, "Failed to generate Dedicated PDCCH config");
srsran::make_phy_rach_cfg(du_cfg->cell(0).serv_cell_cfg_common().ul_cfg_common.init_ul_bwp.rach_cfg_common.setup(),
cfg.cell_list[0].duplex_mode,
&common_cfg.prach);
common_cfg.duplex_mode = cfg.cell_list[0].duplex_mode;
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();
ret = srsran::fill_phy_ssb_cfg(
cfg.cell_list[0].phy_cell.carrier, du_cfg->cell(0).serv_cell_cfg_common(), &common_cfg.ssb);
srsran_assert(ret, "Failed to generate PHY config");
if (phy->set_common_cfg(common_cfg) < SRSRAN_SUCCESS) {
logger.error("Couldn't set common PHY config");
return;
@ -325,7 +310,10 @@ void rrc_nr::config_mac()
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[cc].phy_cell.pdcch;
fill_phy_pdcch_cfg_common(du_cfg->cell(cc), &cell.bwps[0].pdcch);
bool ret = srsran::fill_phy_pdcch_cfg(
cell_ctxt->master_cell_group->sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(), &cell.bwps[0].pdcch);
srsran_assert(ret, "Failed to generate Dedicated PDCCH config");
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;
@ -341,20 +329,17 @@ void rrc_nr::config_mac()
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);
bool valid_cfg = srsran::make_pdsch_cfg_from_serv_cell(cell_ctxt->master_cell_group->sp_cell_cfg.sp_cell_cfg_ded,
&cell.bwps[0].pdsch);
srsran_assert(valid_cfg, "Invalid NR cell configuration.");
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;
} else {
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.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;
if (not cfg.is_standalone) {
const serving_cell_cfg_common_s& serv_cell =
cell_ctxt->master_cell_group->sp_cell_cfg.recfg_with_sync.sp_cell_cfg_common;
// Derive cell config from ASN1
cell.ssb_scs = serv_cell.ssb_subcarrier_spacing;
}
// Set SIB1 and SI messages
@ -365,8 +350,8 @@ void rrc_nr::config_mac()
cell.sibs[i].period_rf = 16; // SIB1 is always 16 rf
cell.sibs[i].si_window_slots = 160;
} else {
cell.sibs[i].period_rf = cell_ctxt->sib1.si_sched_info.sched_info_list[i - 1].si_periodicity.to_number();
cell.sibs[i].si_window_slots = cell_ctxt->sib1.si_sched_info.si_win_len.to_number();
cell.sibs[i].period_rf = du_cfg->cell(0).sib1.si_sched_info.sched_info_list[i - 1].si_periodicity.to_number();
cell.sibs[i].si_window_slots = du_cfg->cell(0).sib1.si_sched_info.si_win_len.to_number();
}
}
@ -384,23 +369,22 @@ int32_t rrc_nr::generate_sibs()
}
// SIB1 packing
fill_sib1_from_enb_cfg(cfg, 0, cell_ctxt->sib1);
si_sched_info_s::sched_info_list_l_& sched_info = cell_ctxt->sib1.si_sched_info.sched_info_list;
const si_sched_info_s::sched_info_list_l_& sched_info = du_cfg->cell(0).sib1.si_sched_info.sched_info_list;
// SI messages packing
cell_ctxt->sibs.resize(1);
sib2_s& sib2 = cell_ctxt->sibs[0].set_sib2();
sib2.cell_resel_info_common.q_hyst.value = asn1::rrc_nr::sib2_s::cell_resel_info_common_s_::q_hyst_opts::db5;
sib2.cell_resel_info_common.q_hyst.value = sib2_s::cell_resel_info_common_s_::q_hyst_opts::db5;
// msg is array of SI messages, each SI message msg[i] may contain multiple SIBs
// all SIBs in a SI message msg[i] share the same periodicity
const uint32_t nof_messages =
cell_ctxt->sib1.si_sched_info_present ? cell_ctxt->sib1.si_sched_info.sched_info_list.size() : 0;
du_cfg->cell(0).sib1.si_sched_info_present ? du_cfg->cell(0).sib1.si_sched_info.sched_info_list.size() : 0;
cell_ctxt->sib_buffer.reserve(nof_messages + 1);
asn1::dyn_array<bcch_dl_sch_msg_s> msg(nof_messages + 1);
// Copy SIB1 to first SI message
msg[0].msg.set_c1().set_sib_type1() = cell_ctxt->sib1;
msg[0].msg.set_c1().set_sib_type1() = du_cfg->cell(0).sib1;
// Copy rest of SIBs
for (uint32_t sched_info_elem = 0; sched_info_elem < nof_messages; sched_info_elem++) {
@ -443,10 +427,10 @@ int32_t rrc_nr::generate_sibs()
int rrc_nr::read_pdu_bcch_bch(const uint32_t tti, srsran::byte_buffer_t& buffer)
{
if (cell_ctxt->mib_buffer == nullptr || buffer.get_tailroom() < cell_ctxt->mib_buffer->N_bytes) {
if (du_cfg->cell(0).packed_mib == nullptr || buffer.get_tailroom() < du_cfg->cell(0).packed_mib->N_bytes) {
return SRSRAN_ERROR;
}
buffer = *cell_ctxt->mib_buffer;
buffer = *du_cfg->cell(0).packed_mib;
return SRSRAN_SUCCESS;
}

@ -37,60 +37,87 @@
} \
} while (0)
using namespace asn1::rrc_nr;
namespace srsenb {
/// Generate default phy cell configuration
void generate_default_nr_phy_cell(phy_cell_cfg_nr_t& phy_cell)
uint32_t coreset_get_bw(const asn1::rrc_nr::ctrl_res_set_s& coreset)
{
phy_cell = {};
uint32_t prb_count = 0;
phy_cell.carrier.scs = srsran_subcarrier_spacing_15kHz;
phy_cell.carrier.nof_prb = 52;
phy_cell.carrier.max_mimo_layers = 1;
// Iterate all the frequency domain resources bit-map...
for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
// ... and count 6 PRB for every frequency domain resource that it is enabled
if (coreset.freq_domain_res.get(i)) {
prb_count += 6;
}
}
phy_cell.dl_freq_hz = 0; // auto set
phy_cell.ul_freq_hz = 0;
phy_cell.num_ra_preambles = 8;
// Return the total count of physical resource blocks
return prb_count;
}
// PRACH
phy_cell.prach.is_nr = true;
phy_cell.prach.config_idx = 0;
phy_cell.prach.root_seq_idx = 1;
phy_cell.prach.freq_offset = 1; // msg1-FrequencyStart (zero not supported with current PRACH implementation)
phy_cell.prach.zero_corr_zone = 0;
phy_cell.prach.num_ra_preambles = phy_cell.num_ra_preambles;
phy_cell.prach.hs_flag = false;
phy_cell.prach.tdd_config.configured = false;
int coreset_get_pdcch_nr_max_candidates(const asn1::rrc_nr::ctrl_res_set_s& coreset, uint32_t aggregation_level)
{
uint32_t coreset_bw = coreset_get_bw(coreset);
uint32_t nof_cce = (coreset_bw * coreset.dur) / 6;
// PDCCH
// - Add CORESET#2 as UE-specific
phy_cell.pdcch.coreset_present[2] = true;
phy_cell.pdcch.coreset[2].id = 2;
phy_cell.pdcch.coreset[2].duration = 1;
phy_cell.pdcch.coreset[2].mapping_type = srsran_coreset_mapping_type_non_interleaved;
phy_cell.pdcch.coreset[2].precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
uint32_t L = 1U << aggregation_level;
uint32_t nof_candidates = nof_cce / L;
return SRSRAN_MIN(nof_candidates, SRSRAN_SEARCH_SPACE_MAX_NOF_CANDIDATES_NR);
}
ctrl_res_set_s make_default_coreset(uint8_t coreset_id, uint32_t nof_prb)
{
ctrl_res_set_s coreset;
coreset.ctrl_res_set_id = coreset_id;
// Generate frequency resources for the full BW
for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
phy_cell.pdcch.coreset[2].freq_resources[i] = i < SRSRAN_FLOOR(phy_cell.carrier.nof_prb, 6);
coreset.freq_domain_res.set(coreset.freq_domain_res.length() - i - 1, i < SRSRAN_FLOOR(nof_prb, 6));
}
// - Add SearchSpace#2 as UE-specific
phy_cell.pdcch.search_space_present[2] = true;
phy_cell.pdcch.search_space[2].id = 2;
phy_cell.pdcch.search_space[2].coreset_id = 2;
phy_cell.pdcch.search_space[2].type = srsran_search_space_type_ue;
// Generate frequency resources for the full BW
for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) {
phy_cell.pdcch.search_space[2].nof_candidates[L] =
SRSRAN_MIN(2, srsran_pdcch_nr_max_candidates_coreset(&phy_cell.pdcch.coreset[2], L));
coreset.dur = 1;
coreset.cce_reg_map_type.set_non_interleaved();
coreset.precoder_granularity.value = ctrl_res_set_s::precoder_granularity_opts::same_as_reg_bundle;
return coreset;
}
phy_cell.pdcch.search_space[2].nof_formats = 2;
phy_cell.pdcch.search_space[2].formats[0] = srsran_dci_format_nr_0_0; // DCI format for PUSCH
phy_cell.pdcch.search_space[2].formats[1] = srsran_dci_format_nr_1_0; // DCI format for PDSCH
phy_cell.pdcch.search_space[2].duration = 1;
// PDSCH
phy_cell.pdsch.rs_power = 0;
phy_cell.pdsch.p_b = 0;
search_space_s make_default_common_search_space(uint8_t ss_id, const ctrl_res_set_s& cs)
{
search_space_s ss;
ss.search_space_id = ss_id;
ss.ctrl_res_set_id_present = true;
ss.ctrl_res_set_id = cs.ctrl_res_set_id;
ss.dur_present = false; // false for duration=1
ss.monitoring_slot_periodicity_and_offset_present = true;
ss.monitoring_slot_periodicity_and_offset.set_sl1();
ss.monitoring_symbols_within_slot_present = true;
ss.monitoring_symbols_within_slot.from_number(0b10000000000000);
ss.search_space_type_present = true;
ss.search_space_type.set_common().dci_format0_minus0_and_format1_minus0_present = true;
ss.nrof_candidates_present = true;
uint32_t nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 0), 2);
asn1::number_to_enum(ss.nrof_candidates.aggregation_level1, nof_cand);
nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 1), 2);
asn1::number_to_enum(ss.nrof_candidates.aggregation_level2, nof_cand);
nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 2), 2);
asn1::number_to_enum(ss.nrof_candidates.aggregation_level4, nof_cand);
nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 3), 2);
asn1::number_to_enum(ss.nrof_candidates.aggregation_level8, nof_cand);
nof_cand = SRSRAN_MIN(coreset_get_pdcch_nr_max_candidates(cs, 4), 2);
asn1::number_to_enum(ss.nrof_candidates.aggregation_level16, nof_cand);
return ss;
}
/// Generate default phy cell configuration
void generate_default_nr_phy_cell(phy_cell_cfg_nr_t& phy_cell)
{
phy_cell = {};
phy_cell.carrier.scs = srsran_subcarrier_spacing_15kHz;
phy_cell.carrier.nof_prb = 52;
phy_cell.carrier.max_mimo_layers = 1;
}
/// Generate default rrc nr cell configuration
@ -99,30 +126,21 @@ void generate_default_nr_cell(rrc_cell_cfg_nr_t& cell)
cell = {};
cell.coreset0_idx = 6;
cell.ssb_absolute_freq_point = 0; // auto derived
cell.num_ra_preambles = 8;
generate_default_nr_phy_cell(cell.phy_cell);
}
/// Generate CORESET#0 and SSB absolute frequency (if not specified)
int derive_coreset0_params(rrc_cell_cfg_nr_t& cell)
{
// Generate CORESET#0
cell.phy_cell.pdcch.coreset_present[0] = true;
// Get pointA and SSB absolute frequencies
double pointA_abs_freq_Hz =
cell.phy_cell.carrier.dl_center_frequency_hz -
cell.phy_cell.carrier.nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(cell.phy_cell.carrier.scs) / 2;
double ssb_abs_freq_Hz = cell.phy_cell.carrier.ssb_center_freq_hz;
// Calculate integer SSB to pointA frequency offset in Hz
uint32_t ssb_pointA_freq_offset_Hz =
(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_scs,
cell.phy_cell.carrier.scs,
cell.coreset0_idx,
&cell.phy_cell.pdcch.coreset[0]);
ERROR_IF_NOT(ret == SRSRAN_SUCCESS, "Failed to generate CORESET#0");
return SRSRAN_SUCCESS;
// PDCCH
// - Add CORESET#2 as UE-specific
cell.pdcch_cfg_ded.ctrl_res_set_to_add_mod_list.resize(1);
auto& coreset2 = cell.pdcch_cfg_ded.ctrl_res_set_to_add_mod_list[0];
coreset2 = make_default_coreset(2, cell.phy_cell.carrier.nof_prb);
// - Add SearchSpace#2 as UE-specific -> CORESET#2
cell.pdcch_cfg_ded.search_spaces_to_add_mod_list.resize(1);
auto& ss2 = cell.pdcch_cfg_ded.search_spaces_to_add_mod_list[0];
ss2 = make_default_common_search_space(2, coreset2);
ss2.search_space_type.set_ue_specific().dci_formats.value = asn1::rrc_nr::search_space_s::search_space_type_c_::
ue_specific_s_::dci_formats_opts::formats0_minus0_and_minus1_minus0;
}
int derive_ssb_params(bool is_sa,
@ -192,25 +210,21 @@ int derive_phy_cell_freq_params(uint32_t dl_arfcn, uint32_t ul_arfcn, phy_cell_c
srsran::srsran_band_helper band_helper;
// derive DL freq from ARFCN
if (phy_cell.dl_freq_hz == 0) {
phy_cell.dl_freq_hz = band_helper.nr_arfcn_to_freq(dl_arfcn);
if (phy_cell.carrier.dl_center_frequency_hz == 0) {
phy_cell.carrier.dl_center_frequency_hz = band_helper.nr_arfcn_to_freq(dl_arfcn);
}
// derive UL freq from ARFCN
if (phy_cell.ul_freq_hz == 0) {
if (phy_cell.carrier.ul_center_frequency_hz == 0) {
// auto-detect UL frequency
if (ul_arfcn == 0) {
// derive UL ARFCN from given DL ARFCN
ul_arfcn = band_helper.get_ul_arfcn_from_dl_arfcn(dl_arfcn);
ERROR_IF_NOT(ul_arfcn > 0, "Can't derive UL ARFCN from DL ARFCN %d", dl_arfcn);
}
phy_cell.ul_freq_hz = band_helper.nr_arfcn_to_freq(ul_arfcn);
phy_cell.carrier.ul_center_frequency_hz = band_helper.nr_arfcn_to_freq(ul_arfcn);
}
// copy center frequencies
phy_cell.carrier.dl_center_frequency_hz = phy_cell.dl_freq_hz;
phy_cell.carrier.ul_center_frequency_hz = phy_cell.ul_freq_hz;
return SRSRAN_SUCCESS;
}
@ -252,75 +266,33 @@ int set_derived_nr_cell_params(bool is_sa, rrc_cell_cfg_nr_t& cell)
cell.ssb_absolute_freq_point = band_helper.freq_to_nr_arfcn(cell.ssb_freq_hz);
// Derive remaining config params
if (is_sa) {
derive_coreset0_params(cell);
cell.phy_cell.pdcch.search_space_present[0] = true;
cell.phy_cell.pdcch.search_space[0].id = 0;
cell.phy_cell.pdcch.search_space[0].coreset_id = 0;
cell.phy_cell.pdcch.search_space[0].type = srsran_search_space_type_common_0;
cell.phy_cell.pdcch.search_space[0].nof_candidates[0] = 1;
cell.phy_cell.pdcch.search_space[0].nof_candidates[1] = 1;
cell.phy_cell.pdcch.search_space[0].nof_candidates[2] = 1;
cell.phy_cell.pdcch.search_space[0].nof_candidates[3] = 0;
cell.phy_cell.pdcch.search_space[0].nof_candidates[4] = 0;
cell.phy_cell.pdcch.search_space[0].nof_formats = 1;
cell.phy_cell.pdcch.search_space[0].formats[0] = srsran_dci_format_nr_1_0;
cell.phy_cell.pdcch.search_space[0].duration = 1;
cell.phy_cell.pdcch.search_space_present[1] = true;
cell.phy_cell.pdcch.search_space[1].id = 1;
cell.phy_cell.pdcch.search_space[1].coreset_id = 0;
cell.phy_cell.pdcch.search_space[1].type = srsran_search_space_type_common_1;
cell.phy_cell.pdcch.search_space[1].nof_candidates[0] = 0;
cell.phy_cell.pdcch.search_space[1].nof_candidates[1] = 0;
cell.phy_cell.pdcch.search_space[1].nof_candidates[2] = 1;
cell.phy_cell.pdcch.search_space[1].nof_candidates[3] = 0;
cell.phy_cell.pdcch.search_space[1].nof_candidates[4] = 0;
cell.phy_cell.pdcch.search_space[1].nof_formats = 2;
cell.phy_cell.pdcch.search_space[1].formats[0] = srsran_dci_format_nr_0_0; // DCI format for PUSCH
cell.phy_cell.pdcch.search_space[1].formats[1] = srsran_dci_format_nr_1_0; // DCI format for PDSCH
cell.phy_cell.pdcch.search_space[1].duration = 1;
} else {
if (not is_sa) {
// Configure CORESET#1
cell.phy_cell.pdcch.coreset_present[1] = true;
cell.phy_cell.pdcch.coreset[1].id = 1;
cell.phy_cell.pdcch.coreset[1].duration = 1;
cell.phy_cell.pdcch.coreset[1].mapping_type = srsran_coreset_mapping_type_non_interleaved;
cell.phy_cell.pdcch.coreset[1].precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
// Generate frequency resources for the full BW
for (uint32_t i = 0; i < SRSRAN_CORESET_FREQ_DOMAIN_RES_SIZE; i++) {
cell.phy_cell.pdcch.coreset[1].freq_resources[i] = i < SRSRAN_FLOOR(cell.phy_cell.carrier.nof_prb, 6);
cell.pdcch_cfg_common.common_ctrl_res_set_present = true;
cell.pdcch_cfg_common.common_ctrl_res_set = make_default_coreset(1, cell.phy_cell.carrier.nof_prb);
}
// Configure SearchSpace#1
cell.pdcch_cfg_common.common_search_space_list.resize(1);
auto& ss1 = cell.pdcch_cfg_common.common_search_space_list[0];
if (is_sa) {
// Configure SearchSpace#1 -> CORESET#0
ctrl_res_set_s dummy_coreset = make_default_coreset(0, cell.phy_cell.carrier.nof_prb);
ss1 = make_default_common_search_space(1, dummy_coreset);
ss1.nrof_candidates.aggregation_level1.value = search_space_s::nrof_candidates_s_::aggregation_level1_opts::n0;
ss1.nrof_candidates.aggregation_level2.value = search_space_s::nrof_candidates_s_::aggregation_level2_opts::n0;
ss1.nrof_candidates.aggregation_level4.value = search_space_s::nrof_candidates_s_::aggregation_level4_opts::n1;
ss1.nrof_candidates.aggregation_level8.value = search_space_s::nrof_candidates_s_::aggregation_level8_opts::n0;
ss1.nrof_candidates.aggregation_level16.value = search_space_s::nrof_candidates_s_::aggregation_level16_opts::n0;
} else {
// Configure SearchSpace#1 -> CORESET#1
cell.phy_cell.pdcch.search_space_present[1] = true;
cell.phy_cell.pdcch.search_space[1].id = 1;
cell.phy_cell.pdcch.search_space[1].coreset_id = 1;
cell.phy_cell.pdcch.search_space[1].type = srsran_search_space_type_common_3;
// Generate frequency resources for the full BW
for (uint32_t L = 0; L < SRSRAN_SEARCH_SPACE_NOF_AGGREGATION_LEVELS_NR; L++) {
cell.phy_cell.pdcch.search_space[1].nof_candidates[L] =
SRSRAN_MIN(2, srsran_pdcch_nr_max_candidates_coreset(&cell.phy_cell.pdcch.coreset[1], L));
}
cell.phy_cell.pdcch.search_space[1].nof_formats = 2;
cell.phy_cell.pdcch.search_space[1].formats[0] = srsran_dci_format_nr_0_0; // DCI format for PUSCH
cell.phy_cell.pdcch.search_space[1].formats[1] = srsran_dci_format_nr_1_0; // DCI format for PDSCH
cell.phy_cell.pdcch.search_space[1].duration = 1;
ss1 = make_default_common_search_space(1, cell.pdcch_cfg_common.common_ctrl_res_set);
// cell.phy_cell.pdcch.search_space[1].type = srsran_search_space_type_common_3;
}
cell.pdcch_cfg_common.ra_search_space_present = true;
cell.pdcch_cfg_common.ra_search_space = ss1.search_space_id;
cell.phy_cell.pdcch.ra_search_space_present = true;
cell.phy_cell.pdcch.ra_search_space = cell.phy_cell.pdcch.search_space[1];
cell.phy_cell.pdcch.ra_search_space.type = srsran_search_space_type_common_1;
// Derive remaining PHY cell params
cell.phy_cell.prach.num_ra_preambles = cell.phy_cell.num_ra_preambles;
cell.phy_cell.prach.tdd_config.configured = (cell.duplex_mode == SRSRAN_DUPLEX_MODE_TDD);
if (cell.duplex_mode == SRSRAN_DUPLEX_MODE_TDD) {
// Note: Give more time margin to fit RAR
cell.phy_cell.prach.config_idx = 8;
}
return check_nr_cell_cfg_valid(cell, is_sa);
return SRSRAN_SUCCESS;
}
int set_derived_nr_rrc_params(rrc_nr_cfg_t& rrc_cfg)
@ -331,24 +303,6 @@ int set_derived_nr_rrc_params(rrc_nr_cfg_t& rrc_cfg)
return SRSRAN_SUCCESS;
}
int check_nr_cell_cfg_valid(const rrc_cell_cfg_nr_t& cell, bool is_sa)
{
// verify SSB params are consistent
HANDLE_ERROR(check_nr_phy_cell_cfg_valid(cell.phy_cell));
if (is_sa) {
ERROR_IF_NOT(cell.phy_cell.pdcch.coreset_present[0], "CORESET#0 must be defined in Standalone mode");
}
return SRSRAN_SUCCESS;
}
int check_nr_phy_cell_cfg_valid(const phy_cell_cfg_nr_t& phy_cell)
{
HANDLE_ERROR(check_nr_pdcch_cfg_valid(phy_cell.pdcch));
return SRSRAN_SUCCESS;
}
int check_nr_pdcch_cfg_valid(const srsran_pdcch_cfg_nr_t& pdcch)
{
// Verify Search Spaces
@ -371,15 +325,4 @@ int check_nr_pdcch_cfg_valid(const srsran_pdcch_cfg_nr_t& pdcch)
return SRSRAN_SUCCESS;
}
int check_rrc_nr_cfg_valid(const rrc_nr_cfg_t& cfg)
{
ERROR_IF_NOT(cfg.cell_list.size() > 0, "The number of NR cells must be positive");
for (const rrc_cell_cfg_nr_t& cell : cfg.cell_list) {
HANDLE_ERROR(check_nr_cell_cfg_valid(cell, cfg.is_standalone));
}
return SRSRAN_SUCCESS;
}
} // namespace srsenb

@ -21,6 +21,7 @@
#include "srsgnb/hdr/stack/rrc/rrc_nr_du_manager.h"
#include "srsgnb/hdr/stack/rrc/cell_asn1_config.h"
#include "srsran/asn1/rrc_nr_utils.h"
#include "srsran/common/string_helpers.h"
using namespace asn1::rrc_nr;
@ -91,15 +92,70 @@ int du_config_manager::add_cell()
}
cell.packed_sib1->N_bytes = bref.distance_bytes();
}
if (cfg.is_standalone) {
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());
}
// Generate SSB SCS
srsran_subcarrier_spacing_t ssb_scs;
if (not srsran::fill_ssb_pattern_scs(cfg.cell_list[cell.cc].phy_cell.carrier, &cell.ssb_pattern, &ssb_scs)) {
return SRSRAN_ERROR;
}
cell.ssb_scs.value = (subcarrier_spacing_e::options)ssb_scs;
cell.ssb_center_freq_hz = cfg.cell_list[cell.cc].ssb_freq_hz;
cell.dl_freq_hz = cfg.cell_list[cell.cc].phy_cell.carrier.dl_center_frequency_hz;
cell.is_standalone = cfg.is_standalone;
cells.push_back(std::move(obj));
return SRSRAN_SUCCESS;
}
void fill_phy_pdcch_cfg_common(const du_cell_config& cell, srsran_pdcch_cfg_nr_t* pdcch)
{
const serving_cell_cfg_common_sib_s& serv_cell = cell.serv_cell_cfg_common();
const pdcch_cfg_common_s& pdcch_common = serv_cell.dl_cfg_common.init_dl_bwp.pdcch_cfg_common.setup();
bool is_sa = cell.is_standalone;
uint8_t coreset0_idx = cell.mib.pdcch_cfg_sib1.ctrl_res_set_zero;
auto scs = (srsran_subcarrier_spacing_t)serv_cell.dl_cfg_common.init_dl_bwp.generic_params.subcarrier_spacing.value;
auto ssb_scs = (srsran_subcarrier_spacing_t)cell.ssb_scs.value;
uint32_t nof_prb = serv_cell.dl_cfg_common.freq_info_dl.scs_specific_carrier_list[0].carrier_bw;
if (is_sa) {
// Generate CORESET#0
pdcch->coreset_present[0] = true;
// Get pointA and SSB absolute frequencies
double pointA_abs_freq_Hz = cell.dl_freq_hz - nof_prb * SRSRAN_NRE * SRSRAN_SUBC_SPACING_NR(scs) / 2;
double ssb_abs_freq_Hz = cell.ssb_center_freq_hz;
// Calculate integer SSB to pointA frequency offset in Hz
uint32_t ssb_pointA_freq_offset_Hz =
(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.pci, ssb_pointA_freq_offset_Hz, ssb_scs, scs, coreset0_idx, &pdcch->coreset[0]);
srsran_assert(ret == SRSRAN_SUCCESS, "Failed to generate CORESET#0");
// Generate SearchSpace#0
pdcch->search_space_present[0] = true;
pdcch->search_space[0].id = 0;
pdcch->search_space[0].coreset_id = 0;
pdcch->search_space[0].type = srsran_search_space_type_common_0;
pdcch->search_space[0].nof_candidates[0] = 1;
pdcch->search_space[0].nof_candidates[1] = 1;
pdcch->search_space[0].nof_candidates[2] = 1;
pdcch->search_space[0].nof_candidates[3] = 0;
pdcch->search_space[0].nof_candidates[4] = 0;
pdcch->search_space[0].nof_formats = 1;
pdcch->search_space[0].formats[0] = srsran_dci_format_nr_1_0;
pdcch->search_space[0].duration = 1;
}
// Generate Common CORESETs and Search Spaces
bool ret = srsran::fill_phy_pdcch_cfg_common(pdcch_common, pdcch);
srsran_assert(ret, "PDCCH Config Common");
}
} // namespace srsenb

@ -48,7 +48,9 @@ rrc_nr::ue::ue(rrc_nr* parent_, uint16_t rnti_, uint32_t pcell_cc_idx, bool star
if (not parent->cfg.is_standalone) {
// Add the final PDCCH config in case of NSA
uecfg.phy_cfg.pdcch = parent->cfg.cell_list[0].phy_cell.pdcch;
srsran::fill_phy_pdcch_cfg(
parent->cell_ctxt->master_cell_group->sp_cell_cfg.sp_cell_cfg_ded.init_dl_bwp.pdcch_cfg.setup(),
&uecfg.phy_cfg.pdcch);
} else {
cell_group_cfg = *parent->cell_ctxt->master_cell_group;
next_cell_group_cfg = cell_group_cfg;
@ -1488,13 +1490,11 @@ int rrc_nr::ue::update_mac(const cell_group_cfg_s& cell_group_config, bool is_co
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) {
uecfg.phy_cfg.pdcch.search_space_present[ss.search_space_id] = true;
uecfg.phy_cfg.pdcch.search_space[ss.search_space_id] =
parent->cfg.cell_list[0].phy_cell.pdcch.search_space[ss.search_space_id];
srsran::make_phy_search_space_cfg(ss, &uecfg.phy_cfg.pdcch.search_space[ss.search_space_id]);
}
for (auto& cs : pdcch.ctrl_res_set_to_add_mod_list) {
uecfg.phy_cfg.pdcch.coreset_present[cs.ctrl_res_set_id] = true;
uecfg.phy_cfg.pdcch.coreset[cs.ctrl_res_set_id] =
parent->cfg.cell_list[0].phy_cell.pdcch.coreset[cs.ctrl_res_set_id];
srsran::make_phy_coreset_cfg(cs, &uecfg.phy_cfg.pdcch.coreset[cs.ctrl_res_set_id]);
}
}

@ -167,7 +167,6 @@ void test_rrc_sa_ngap_integration(ngap_args_t ngap_args)
rrc_cfg_nr.cell_list[0].duplex_mode = SRSRAN_DUPLEX_MODE_FDD;
rrc_cfg_nr.is_standalone = true;
set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]);
srsran_assert(check_rrc_nr_cfg_valid(rrc_cfg_nr) == SRSRAN_SUCCESS, "Invalid RRC NR configuration");
TESTASSERT(
rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, &ngap_obj, &gtpu_obj, bearer_mapper, nullptr) ==

@ -58,7 +58,7 @@ void test_sib_generation()
enb_bearer_manager bearer_mapper;
// set cfg
rrc_nr_cfg_t rrc_cfg_nr = {};
rrc_nr_cfg_t rrc_cfg_nr;
rrc_cfg_nr.cell_list.emplace_back();
generate_default_nr_cell(rrc_cfg_nr.cell_list[0]);
rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500;
@ -71,7 +71,6 @@ void test_sib_generation()
srsran::string_to_mcc("001", &rrc_cfg_nr.mcc);
srsran::string_to_mnc("01", &rrc_cfg_nr.mnc);
set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]);
srsran_assert(check_rrc_nr_cfg_valid(rrc_cfg_nr) == SRSRAN_SUCCESS, "Invalid RRC NR configuration");
TESTASSERT(
rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr, bearer_mapper, nullptr) ==
@ -116,7 +115,7 @@ int test_rrc_setup()
rrc_nr rrc_obj(&task_sched);
// set cfg
rrc_nr_cfg_t rrc_cfg_nr = rrc_nr_cfg_t{};
rrc_nr_cfg_t rrc_cfg_nr;
rrc_cfg_nr.cell_list.emplace_back();
generate_default_nr_cell(rrc_cfg_nr.cell_list[0]);
rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500;
@ -128,7 +127,6 @@ int test_rrc_setup()
srsran::string_to_mcc("001", &rrc_cfg_nr.mcc);
srsran::string_to_mnc("01", &rrc_cfg_nr.mnc);
set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]);
srsran_assert(check_rrc_nr_cfg_valid(rrc_cfg_nr) == SRSRAN_SUCCESS, "Invalid RRC NR configuration");
TESTASSERT(
rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, nullptr, nullptr, bearer_mapper, nullptr) ==
SRSRAN_SUCCESS);
@ -159,7 +157,7 @@ void test_rrc_sa_connection()
rrc_nr rrc_obj(&task_sched);
// set cfg
rrc_nr_cfg_t rrc_cfg_nr = rrc_nr_cfg_t{};
rrc_nr_cfg_t rrc_cfg_nr;
rrc_cfg_nr.cell_list.emplace_back();
generate_default_nr_cell(rrc_cfg_nr.cell_list[0]);
rrc_cfg_nr.cell_list[0].phy_cell.carrier.pci = 500;
@ -172,7 +170,6 @@ void test_rrc_sa_connection()
srsran::string_to_mcc("001", &rrc_cfg_nr.mcc);
srsran::string_to_mnc("01", &rrc_cfg_nr.mnc);
set_derived_nr_cell_params(rrc_cfg_nr.is_standalone, rrc_cfg_nr.cell_list[0]);
srsran_assert(check_rrc_nr_cfg_valid(rrc_cfg_nr) == SRSRAN_SUCCESS, "Invalid RRC NR configuration");
TESTASSERT(
rrc_obj.init(rrc_cfg_nr, &phy_obj, &mac_obj, &rlc_obj, &pdcp_obj, &ngap_obj, nullptr, bearer_mapper, nullptr) ==

@ -41,7 +41,7 @@ public:
double center_freq_hz;
double ssb_freq_hz;
srsran_subcarrier_spacing_t ssb_scs;
srsran_ssb_patern_t ssb_pattern;
srsran_ssb_pattern_t ssb_pattern;
srsran_duplex_mode_t duplex_mode;
};

@ -68,7 +68,12 @@ public:
bool start_cell_search(const cell_search_args_t& req) final;
bool start_cell_select(const cell_select_args_t& req) final;
void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final { return workers.get_metrics(*m); };
void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final
{
if (rat == srsran::srsran_rat_t::nr) {
return workers.get_metrics(*m);
}
};
void srsran_phy_logger(phy_logger_level_t log_level, char* str);
private:

@ -70,7 +70,6 @@ public:
const rrc_nr_args_t& args_);
void stop();
void init_core_less();
void get_metrics(rrc_nr_metrics_t& m);
@ -152,11 +151,13 @@ private:
void send_security_mode_complete();
// helpers
void set_phy_default_config();
void handle_sib1(const asn1::rrc_nr::sib1_s& sib1);
bool handle_rrc_setup(const asn1::rrc_nr::rrc_setup_s& setup);
void handle_rrc_reconfig(const asn1::rrc_nr::rrc_recfg_s& reconfig);
void handle_dl_info_transfer(const asn1::rrc_nr::dl_info_transfer_s& dl_info_transfer);
void handle_security_mode_command(const asn1::rrc_nr::security_mode_cmd_s& smc);
void handle_rrc_release(const asn1::rrc_nr::rrc_release_s& rrc_release);
void generate_as_keys();
srsran::task_sched_handle task_sched;
@ -205,8 +206,10 @@ private:
// Stores the state of the PHY configuration setting
enum {
PHY_CFG_STATE_NONE = 0,
PHY_CFG_STATE_APPLY_SP_CELL,
PHY_CFG_STATE_RA_COMPLETED,
PHY_CFG_STATE_SA_SIB_CFG,
PHY_CFG_STATE_SA_FULL_CFG,
PHY_CFG_STATE_NSA_APPLY_SP_CELL,
PHY_CFG_STATE_NSA_RA_COMPLETED,
} phy_cfg_state;
rrc_nr_args_t args = {};

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save