SRSUE: Integrated NR workers

This commit is contained in:
Xavier Arteaga 2020-11-20 12:12:29 +01:00 committed by Andre Puschmann
parent 6fb56a3c62
commit ac930003be
25 changed files with 418 additions and 108 deletions

View File

@ -486,7 +486,9 @@ typedef struct {
int worker_cpu_mask = -1;
int sync_cpu_affinity = -1;
uint32_t nof_carriers = 1;
uint32_t nof_lte_carriers = 1;
uint32_t nof_nr_carriers = 0;
uint32_t nr_nof_prb = 50;
uint32_t nof_rx_ant = 1;
std::string equalizer_mode = "mmse";
int cqi_max = 15;

View File

@ -272,6 +272,13 @@ SRSLTE_API uint32_t srslte_coreset_get_bw(const srslte_coreset_t* coreset);
*/
SRSLTE_API uint32_t srslte_coreset_get_sz(const srslte_coreset_t* coreset);
/**
* @brief Get the NR PDSCH mapping type in string
* @param mapping_type Mapping type
* @return Constant string with PDSCH mapping type
*/
SRSLTE_API const char* srslte_pdsch_mapping_type_to_str(srslte_pdsch_mapping_type_t mapping_type);
/**
* @brief Get the MCS table string
* @param mcs_table MCS table value

View File

@ -34,6 +34,7 @@
typedef struct SRSLTE_API {
srslte_sch_nr_args_t sch;
bool measure_evm;
bool measure_time;
} srslte_pdsch_args_t;
/**
@ -50,6 +51,8 @@ typedef struct SRSLTE_API {
cf_t* x[SRSLTE_MAX_LAYERS_NR]; ///< PDSCH modulated bits
srslte_modem_table_t modem_tables[SRSLTE_MOD_NITEMS]; ///< Modulator tables
srslte_evm_buffer_t* evm_buffer;
bool meas_time_en;
uint32_t meas_time_us;
} srslte_pdsch_nr_t;
/**
@ -82,16 +85,17 @@ SRSLTE_API int srslte_pdsch_nr_decode(srslte_pdsch_nr_t* q,
cf_t* sf_symbols[SRSLTE_MAX_PORTS],
srslte_pdsch_res_nr_t data[SRSLTE_MAX_TB]);
SRSLTE_API uint32_t srslte_pdsch_nr_grant_rx_info(srslte_pdsch_grant_nr_t* grant,
srslte_pdsch_res_nr_t res[SRSLTE_MAX_CODEWORDS],
char* str,
uint32_t str_len);
SRSLTE_API uint32_t srslte_pdsch_nr_rx_info(const srslte_pdsch_nr_t* q,
const srslte_pdsch_cfg_nr_t* cfg,
const srslte_pdsch_grant_nr_t* grant,
const srslte_pdsch_res_nr_t* res,
char* str,
uint32_t str_len);
SRSLTE_API uint32_t srslte_pdsch_nr_rx_info(srslte_pdsch_cfg_nr_t* cfg,
srslte_pdsch_res_nr_t res[SRSLTE_MAX_CODEWORDS],
char* str,
uint32_t str_len);
SRSLTE_API uint32_t srslte_pdsch_nr_tx_info(srslte_pdsch_cfg_nr_t* cfg, char* str, uint32_t str_len);
SRSLTE_API uint32_t srslte_pdsch_nr_tx_info(const srslte_pdsch_nr_t* q,
const srslte_pdsch_cfg_nr_t* cfg,
const srslte_pdsch_grant_nr_t* grant,
char* str,
uint32_t str_len);
#endif // srslte_pdsch_nr_H

View File

@ -138,4 +138,6 @@ SRSLTE_API int srslte_dlsch_nr_decode(srslte_sch_nr_t* q,
uint8_t* data,
bool* crc_ok);
SRSLTE_API int srslte_sch_nr_tb_info(const srslte_sch_tb_t* tb, char* str, uint32_t str_len);
#endif // SRSLTE_SCH_NR_H

View File

@ -24,6 +24,7 @@ extern "C" {
typedef struct SRSLTE_API {
srslte_pdsch_args_t pdsch;
uint32_t nof_rx_antennas;
uint32_t nof_max_prb;
} srslte_ue_dl_nr_args_t;
typedef struct SRSLTE_API {
@ -53,6 +54,14 @@ SRSLTE_API int srslte_ue_dl_nr_pdsch_get(srslte_ue_dl_nr_t* q,
const srslte_pdsch_cfg_nr_t* cfg,
const srslte_pdsch_grant_nr_t* grant,
srslte_pdsch_res_nr_t* res);
SRSLTE_API int srslte_ue_dl_nr_pdsch_info(const srslte_ue_dl_nr_t* q,
const srslte_pdsch_cfg_nr_t* cfg,
const srslte_pdsch_grant_nr_t* grant,
const srslte_pdsch_res_nr_t res[SRSLTE_MAX_CODEWORDS],
char* str,
uint32_t str_len);
#ifdef __cplusplus
}
#endif

View File

@ -424,6 +424,7 @@ int srslte_dmrs_pdsch_get_N_prb(const srslte_pdsch_cfg_nr_t* cfg, const srslte_p
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS] = {};
int ret = srslte_dmrs_pdsch_get_symbols_idx(cfg, grant, symbols);
if (ret < SRSLTE_SUCCESS) {
ERROR("Error getting PDSCH DMRS symbol indexes\n");
return SRSLTE_ERROR;
}
@ -667,7 +668,8 @@ int srslte_dmrs_pdsch_estimate(srslte_dmrs_pdsch_t* q,
// Get symbols indexes
uint32_t symbols[SRSLTE_DMRS_PDSCH_MAX_SYMBOLS] = {};
int nof_symbols = srslte_dmrs_pdsch_get_symbols_idx(pdsch_cfg, grant, symbols);
if (nof_symbols < SRSLTE_SUCCESS) {
if (nof_symbols <= SRSLTE_SUCCESS) {
ERROR("Error getting symbol indexes\n");
return SRSLTE_ERROR;
}
@ -681,6 +683,11 @@ int srslte_dmrs_pdsch_estimate(srslte_dmrs_pdsch_t* q,
nof_pilots_x_symbol = srslte_dmrs_pdsch_get_symbol(
q, pdsch_cfg, grant, cinit, delta, &sf_symbols[symbol_sz * l], &q->pilot_estimates[nof_pilots_x_symbol * i]);
if (nof_pilots_x_symbol == 0) {
ERROR("Error, no pilots extracted (i=%d, l=%d)\n", i, l);
return SRSLTE_ERROR;
}
}
// Perform measurements here
@ -700,14 +707,20 @@ int srslte_dmrs_pdsch_estimate(srslte_dmrs_pdsch_t* q,
(dmrs_cfg->type == srslte_dmrs_pdsch_type_1) ? nof_pilots_x_symbol * 2 : nof_pilots_x_symbol * 3;
if (dmrs_cfg->type == srslte_dmrs_pdsch_type_1) {
// Prepare interpolator
srslte_interp_linear_resize(&q->interpolator_type1, nof_pilots_x_symbol, 2);
if (srslte_interp_linear_resize(&q->interpolator_type1, nof_pilots_x_symbol, 2) < SRSLTE_SUCCESS) {
ERROR("Resizing interpolator nof_pilots_x_symbol=%d; M=%d;\n", nof_pilots_x_symbol, 2);
return SRSLTE_ERROR;
}
// Interpolate
srslte_interp_linear_offset(&q->interpolator_type1, q->pilot_estimates, ce, delta, 2 - delta);
} else {
// Prepare interpolator
srslte_interp_linear_resize(&q->interpolator_type2, nof_pilots_x_symbol, 3);
if (srslte_interp_linear_resize(&q->interpolator_type2, nof_pilots_x_symbol, 3) < SRSLTE_SUCCESS) {
ERROR("Resizing interpolator nof_pilots_x_symbol=%d; M=%d;\n", nof_pilots_x_symbol, 3);
return SRSLTE_ERROR;
}
// Interpolate
srslte_interp_linear_offset(&q->interpolator_type2, q->pilot_estimates, ce, delta, 3 - delta);

View File

@ -35,10 +35,21 @@ uint32_t srslte_coreset_get_sz(const srslte_coreset_t* coreset)
return srslte_coreset_get_bw(coreset) * SRSLTE_NRE * coreset->duration;
}
const char* srslte_pdsch_mapping_type_to_str(srslte_pdsch_mapping_type_t mapping_type)
{
switch (mapping_type) {
case srslte_pdsch_mapping_type_A:
return "A";
case srslte_pdsch_mapping_type_B:
return "B";
default:
return "undefined";
}
}
const char* srslte_mcs_table_to_str(srslte_mcs_table_t mcs_table)
{
switch (mcs_table) {
case srslte_mcs_table_64qam:
return "64qam";
case srslte_mcs_table_256qam:

View File

@ -69,6 +69,8 @@ int srslte_pdsch_nr_init_ue(srslte_pdsch_nr_t* q, const srslte_pdsch_args_t* arg
}
}
q->meas_time_en = args->measure_time;
return SRSLTE_SUCCESS;
}
@ -427,13 +429,17 @@ int srslte_pdsch_nr_encode(srslte_pdsch_nr_t* q,
uint8_t* data[SRSLTE_MAX_TB],
cf_t* sf_symbols[SRSLTE_MAX_PORTS])
{
uint32_t nof_cw = 0;
// Check input pointers
if (!q || !cfg || !grant || !data || !sf_symbols) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
struct timeval t[3];
if (q->meas_time_en) {
gettimeofday(&t[1], NULL);
}
// Check number of layers
if (q->max_layers < grant->nof_layers) {
ERROR("Error number of layers (%d) exceeds configured maximum (%d)\n", grant->nof_layers, q->max_layers);
@ -441,6 +447,7 @@ int srslte_pdsch_nr_encode(srslte_pdsch_nr_t* q,
}
// 7.3.1.1 and 7.3.1.2
uint32_t nof_cw = 0;
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) {
nof_cw += grant->tb[tb].enabled ? 1 : 0;
@ -466,6 +473,12 @@ int srslte_pdsch_nr_encode(srslte_pdsch_nr_t* q,
// 7.3.1.6 Mapping from virtual to physical resource blocks
srslte_pdsch_nr_put(q, cfg, grant, x[0], sf_symbols[0]);
if (q->meas_time_en) {
gettimeofday(&t[2], NULL);
get_time_interval(t);
q->meas_time_us = (uint32_t)t[0].tv_usec;
}
return SRSLTE_SUCCESS;
}
@ -542,6 +555,16 @@ int srslte_pdsch_nr_decode(srslte_pdsch_nr_t* q,
cf_t* sf_symbols[SRSLTE_MAX_PORTS],
srslte_pdsch_res_nr_t data[SRSLTE_MAX_TB])
{
// Check input pointers
if (!q || !cfg || !grant || !data || !sf_symbols) {
return SRSLTE_ERROR_INVALID_INPUTS;
}
struct timeval t[3];
if (q->meas_time_en) {
gettimeofday(&t[1], NULL);
}
uint32_t nof_cw = 0;
for (uint32_t tb = 0; tb < SRSLTE_MAX_TB; tb++) {
nof_cw += grant->tb[tb].enabled ? 1 : 0;
@ -591,5 +614,113 @@ int srslte_pdsch_nr_decode(srslte_pdsch_nr_t* q,
}
}
if (q->meas_time_en) {
gettimeofday(&t[2], NULL);
get_time_interval(t);
q->meas_time_us = (uint32_t)t[0].tv_usec;
}
return SRSLTE_SUCCESS;
}
static uint32_t srslte_pdsch_nr_grant_info(const srslte_pdsch_cfg_nr_t* cfg,
const srslte_pdsch_grant_nr_t* grant,
char* str,
uint32_t str_len)
{
uint32_t len = 0;
len = srslte_print_check(str, str_len, len, "rnti=0x%x", grant->rnti);
// Append time-domain resource mapping
len = srslte_print_check(str,
str_len,
len,
",k0=%d,S=%d,L=%d,mapping=%s",
grant->k0,
grant->S,
grant->L,
srslte_pdsch_mapping_type_to_str(grant->mapping));
// Skip frequency domain resources...
// ...
// Append spatial resources
len = srslte_print_check(str, str_len, len, ",Nl=%d", grant->nof_layers);
// Append scrambling ID
len = srslte_print_check(str, str_len, len, ",n_scid=%d", grant->n_scid);
// Append TB info
for (uint32_t i = 0; i < SRSLTE_MAX_TB; i++) {
len += srslte_sch_nr_tb_info(&grant->tb[i], &str[len], str_len - len);
}
return len;
}
uint32_t srslte_pdsch_nr_rx_info(const srslte_pdsch_nr_t* q,
const srslte_pdsch_cfg_nr_t* cfg,
const srslte_pdsch_grant_nr_t* grant,
const srslte_pdsch_res_nr_t res[SRSLTE_MAX_CODEWORDS],
char* str,
uint32_t str_len)
{
uint32_t len = 0;
len += srslte_pdsch_nr_grant_info(cfg, grant, &str[len], str_len - len);
if (q->evm_buffer != NULL) {
len = srslte_print_check(str, str_len, len, ",evm={", 0);
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
if (grant->tb[i].enabled && !isnan(res[i].evm)) {
len = srslte_print_check(str, str_len, len, "%.2f", res[i].evm);
if (i < SRSLTE_MAX_CODEWORDS - 1) {
if (grant->tb[i + 1].enabled) {
len = srslte_print_check(str, str_len, len, ",", 0);
}
}
}
}
len = srslte_print_check(str, str_len, len, "}", 0);
}
if (res != NULL) {
len = srslte_print_check(str, str_len, len, ",crc={", 0);
for (uint32_t i = 0; i < SRSLTE_MAX_CODEWORDS; i++) {
if (grant->tb[i].enabled) {
len = srslte_print_check(str, str_len, len, "%s", res[i].crc ? "OK" : "KO");
if (i < SRSLTE_MAX_CODEWORDS - 1) {
if (grant->tb[i + 1].enabled) {
len = srslte_print_check(str, str_len, len, ",", 0);
}
}
}
}
len = srslte_print_check(str, str_len, len, "}", 0);
}
if (q->meas_time_en) {
len = srslte_print_check(str, str_len, len, ", t=%d us\n", q->meas_time_us);
}
return len;
}
uint32_t srslte_pdsch_nr_tx_info(const srslte_pdsch_nr_t* q,
const srslte_pdsch_cfg_nr_t* cfg,
const srslte_pdsch_grant_nr_t* grant,
char* str,
uint32_t str_len)
{
uint32_t len = 0;
len += srslte_pdsch_nr_grant_info(cfg, grant, &str[len], str_len - len);
if (q->meas_time_en) {
len = srslte_print_check(str, str_len, len, ", t=%d us\n", q->meas_time_us);
}
return len;
}

View File

@ -208,6 +208,7 @@ int srslte_ra_dl_nr_slot_nof_re(const srslte_pdsch_cfg_nr_t* pdsch_cfg, const sr
// the number of REs for DM-RS per PRB in the scheduled duration
int n_prb_dmrs = srslte_dmrs_pdsch_get_N_prb(pdsch_cfg, grant);
if (n_prb_dmrs < SRSLTE_SUCCESS) {
ERROR("Invalid number of DMRS RE\n");
return SRSLTE_ERROR;
}
@ -358,7 +359,8 @@ int srslte_ra_nr_fill_tb(const srslte_pdsch_cfg_nr_t* pdsch_cfg,
// 1) The UE shall first determine the number of REs (N RE ) within the slot.
int N_re = srslte_ra_dl_nr_slot_nof_re(pdsch_cfg, grant);
if (N_re < SRSLTE_SUCCESS) {
if (N_re <= SRSLTE_SUCCESS) {
ERROR("Invalid number of RE\n");
return SRSLTE_ERROR;
}

View File

@ -639,3 +639,25 @@ int srslte_dlsch_nr_decode(srslte_sch_nr_t* q,
return SRSLTE_SUCCESS;
}
int srslte_sch_nr_tb_info(const srslte_sch_tb_t* tb, char* str, uint32_t str_len)
{
int len = 0;
if (tb->enabled) {
len += srslte_print_check(str,
str_len,
len,
"tb={mod=%s,Nl=%d,TBS=%d,R=%.3f,rv=%d,Nre=%d,Nbit=%d,cw=%d}",
srslte_mod_string(tb->mod),
tb->N_L,
tb->tbs,
tb->R,
tb->rv,
tb->nof_re,
tb->nof_bits,
tb->cw_idx);
}
return len;
}

View File

@ -55,14 +55,14 @@ int srslte_ue_dl_nr_init(srslte_ue_dl_nr_t* q, cf_t* input[SRSLTE_MAX_PORTS], co
return SRSLTE_ERROR;
}
if (ue_dl_alloc_prb(q, SRSLTE_MAX_PRB_NR)) {
if (ue_dl_alloc_prb(q, args->nof_max_prb)) {
ERROR("Error allocating\n");
return SRSLTE_ERROR;
}
srslte_ofdm_cfg_t fft_cfg = {};
fft_cfg.nof_prb = 100;
fft_cfg.symbol_sz = srslte_symbol_sz(100);
fft_cfg.nof_prb = args->nof_max_prb;
fft_cfg.symbol_sz = srslte_symbol_sz(args->nof_max_prb);
fft_cfg.keep_dc = true;
for (uint32_t i = 0; i < q->nof_rx_antennas; i++) {
@ -153,5 +153,29 @@ int srslte_ue_dl_nr_pdsch_get(srslte_ue_dl_nr_t* q,
return SRSLTE_ERROR;
}
if (SRSLTE_DEBUG_ENABLED && srslte_verbose >= SRSLTE_VERBOSE_INFO && !handler_registered) {
char str[512];
srslte_ue_dl_nr_pdsch_info(q, cfg, grant, res, str, sizeof(str));
INFO("PDSCH: %s\n", str);
}
return SRSLTE_SUCCESS;
}
int srslte_ue_dl_nr_pdsch_info(const srslte_ue_dl_nr_t* q,
const srslte_pdsch_cfg_nr_t* cfg,
const srslte_pdsch_grant_nr_t* grant,
const srslte_pdsch_res_nr_t* res,
char* str,
uint32_t str_len)
{
int len = 0;
// Append channel estimator info
// ...
// Append PDSCH info
len += srslte_pdsch_nr_rx_info(&q->pdsch, cfg, grant, res, &str[len], str_len - len);
return len;
}

View File

@ -94,8 +94,10 @@ int main(int argc, char** argv)
srslte_ue_dl_nr_args_t ue_dl_args = {};
ue_dl_args.nof_rx_antennas = 1;
ue_dl_args.nof_max_prb = carrier.nof_prb;
ue_dl_args.pdsch.sch.disable_simd = true;
ue_dl_args.pdsch.measure_evm = true;
ue_dl_args.pdsch.measure_time = true;
srslte_enb_dl_nr_args_t enb_dl_args = {};
enb_dl_args.nof_tx_antennas = 1;
@ -108,10 +110,6 @@ int main(int argc, char** argv)
goto clean_exit;
}
srslte_pdsch_args_t pdsch_args = {};
pdsch_args.sch.disable_simd = true;
pdsch_args.measure_evm = true;
if (srslte_ue_dl_nr_init(&ue_dl, &buffer, &ue_dl_args)) {
ERROR("Error UE DL\n");
goto clean_exit;

View File

@ -33,7 +33,6 @@ namespace nr {
typedef struct {
uint32_t nof_carriers;
uint32_t max_prb;
srslte_ue_dl_nr_args_t dl;
} phy_nr_args_t;
@ -50,9 +49,10 @@ public:
phy_nr_state()
{
args.nof_carriers = 1;
args.max_prb = SRSLTE_MAX_PRB;
args.dl.nof_rx_antennas = 1;
args.dl.nof_max_prb = 100;
args.dl.pdsch.measure_evm = true;
args.dl.pdsch.measure_time = true;
args.dl.pdsch.sch.disable_simd = true;
}
};
@ -72,12 +72,13 @@ public:
bool work_dl();
private:
srslte_dl_slot_cfg_t dl_slot_cfg = {};
uint32_t cc_idx = 0;
std::array<std::vector<cf_t>, SRSLTE_MAX_PORTS> rx_buffer = {};
std::array<std::vector<cf_t>, SRSLTE_MAX_PORTS> tx_buffer = {};
phy_nr_state* phy_state;
srslte_ue_dl_nr_t ue_dl = {};
srslte_dl_slot_cfg_t dl_slot_cfg = {};
uint32_t cc_idx = 0;
std::array<cf_t*, SRSLTE_MAX_PORTS> rx_buffer = {};
std::array<cf_t*, SRSLTE_MAX_PORTS> tx_buffer = {};
phy_nr_state* phy_state = nullptr;
srslte_ue_dl_nr_t ue_dl = {};
srslte::log* log_h = nullptr;
// Temporal attributes
srslte_softbuffer_rx_t softbuffer_rx = {};

View File

@ -25,6 +25,7 @@
#include "srslte/radio/radio.h"
#include "srslte/srslte.h"
#include "srsue/hdr/phy/lte/worker_pool.h"
#include "srsue/hdr/phy/nr/worker_pool.h"
#include "srsue/hdr/phy/ue_lte_phy_base.h"
#include "sync.h"
@ -66,7 +67,8 @@ private:
class phy final : public ue_lte_phy_base, public srslte::thread
{
public:
explicit phy(srslte::logger* logger_) : logger(logger_), lte_workers(MAX_WORKERS), common(), thread("PHY"){};
explicit phy(srslte::logger* logger_) :
logger(logger_), lte_workers(MAX_WORKERS), nr_workers(MAX_WORKERS), common(), thread("PHY"){};
~phy() final { stop(); }
// Init defined in base class
@ -177,6 +179,7 @@ private:
srsue::stack_interface_phy_lte* stack = nullptr;
lte::worker_pool lte_workers;
nr::worker_pool nr_workers;
phy_common common;
sync sfsync;
prach prach_buffer;

View File

@ -33,6 +33,7 @@
#include "srslte/phy/channel/channel.h"
#include "srslte/srslte.h"
#include "srsue/hdr/phy/lte/worker_pool.h"
#include "srsue/hdr/phy/nr/worker_pool.h"
#include "sync_state.h"
namespace srsue {
@ -52,7 +53,8 @@ public:
void init(srslte::radio_interface_phy* radio_,
stack_interface_phy_lte* _stack,
prach* prach_buffer,
lte::worker_pool* _workers_pool,
lte::worker_pool* _lte_workers_pool,
nr::worker_pool* _nr_workers_pool,
phy_common* _worker_com,
srslte::log* _log_h,
srslte::log* _log_phy_lib_h,
@ -161,10 +163,12 @@ private:
/**
* Helper method, executed when the UE is camping and in-sync
* @param worker Selected worker for the current TTI
* @param lte_worker Selected LTE worker for the current TTI
* @param nr_worker Selected NR worker for the current TTI
* @param sync_buffer Sub-frame buffer for the current TTI
*/
void run_camping_in_sync_state(lte::sf_worker* worker, srslte::rf_buffer_t& sync_buffer);
void
run_camping_in_sync_state(lte::sf_worker* lte_worker, nr::sf_worker* nr_worker, srslte::rf_buffer_t& sync_buffer);
/**
* Helper method, executed in a TTI basis for signaling to the stack a new TTI execution
@ -197,7 +201,8 @@ private:
stack_interface_phy_lte* stack = nullptr;
srslte::log* log_h = nullptr;
srslte::log* log_phy_lib_h = nullptr;
lte::worker_pool* workers_pool = nullptr;
lte::worker_pool* lte_worker_pool = nullptr;
nr::worker_pool* nr_worker_pool = nullptr;
srslte::radio_interface_phy* radio_h = nullptr;
phy_common* worker_com = nullptr;
prach* prach_buffer = nullptr;

View File

@ -426,6 +426,8 @@ static int parse_args(all_args_t* args, int argc, char* argv[])
("vnf.type", bpo::value<string>(&args->phy.vnf_args.type)->default_value("ue"), "VNF instance type [gnb,ue]")
("vnf.addr", bpo::value<string>(&args->phy.vnf_args.bind_addr)->default_value("localhost"), "Address to bind VNF interface")
("vnf.port", bpo::value<uint16_t>(&args->phy.vnf_args.bind_port)->default_value(3334), "Bind port")
("nr.nof_carriers", bpo::value<uint32_t>(&args->phy.nof_nr_carriers)->default_value(1), "Number of NR carriers")
("nr.nof_prb", bpo::value<uint32_t>(&args->phy.nr_nof_prb)->default_value(50), "NR carrier bandwidth")
;
// Positional options - config file location

View File

@ -839,7 +839,7 @@ void cc_worker::set_uci_ack(srslte_uci_data_t* uci_data,
uint32_t nof_configured_carriers = 0;
// Only PCell generates ACK for all SCell
for (uint32_t i = 0; i < phy->args->nof_carriers; i++) {
for (uint32_t i = 0; i < phy->args->nof_lte_carriers; i++) {
if (phy->cell_state.is_configured(i)) {
phy->get_dl_pending_ack(&sf_cfg_ul, i, &ack_info.cc[i]);
nof_configured_carriers++;

View File

@ -53,14 +53,14 @@ sf_worker::sf_worker(uint32_t max_prb, phy_common* phy_, srslte::log* log_h_)
log_h = log_h_;
// ue_sync in phy.cc requires a buffer for 3 subframes
for (uint32_t r = 0; r < phy->args->nof_carriers; r++) {
for (uint32_t r = 0; r < phy->args->nof_lte_carriers; r++) {
cc_workers.push_back(new cc_worker(r, max_prb, phy, log_h));
}
}
sf_worker::~sf_worker()
{
for (uint32_t r = 0; r < phy->args->nof_carriers; r++) {
for (uint32_t r = 0; r < phy->args->nof_lte_carriers; r++) {
delete cc_workers[r];
}
}
@ -214,8 +214,8 @@ void sf_worker::work_imp()
// Fill periodic CQI data; In case of periodic CSI report collision, lower carrier index have preference, so
// stop as soon as either CQI data is enabled or RI is carried
for (uint32_t carrier_idx = 0;
carrier_idx < phy->args->nof_carriers and not uci_data.cfg.cqi.data_enable and uci_data.cfg.cqi.ri_len == 0;
for (uint32_t carrier_idx = 0; carrier_idx < phy->args->nof_lte_carriers and not uci_data.cfg.cqi.data_enable and
uci_data.cfg.cqi.ri_len == 0;
carrier_idx++) {
if (phy->cell_state.is_active(carrier_idx, TTI_TX(tti))) {
cc_workers[carrier_idx]->set_uci_periodic_cqi(&uci_data);
@ -223,7 +223,7 @@ void sf_worker::work_imp()
}
// Loop through all carriers
for (uint32_t carrier_idx = 0; carrier_idx < phy->args->nof_carriers; carrier_idx++) {
for (uint32_t carrier_idx = 0; carrier_idx < phy->args->nof_lte_carriers; carrier_idx++) {
if (phy->cell_state.is_active(carrier_idx, tti)) {
tx_signal_ready |= cc_workers[carrier_idx]->work_ul(uci_cc_idx == carrier_idx ? &uci_data : nullptr);

View File

@ -26,17 +26,19 @@
namespace srsue {
namespace nr {
cc_worker::cc_worker(uint32_t cc_idx_, srslte::log* log, phy_nr_state* phy_state_) :
cc_idx(cc_idx_), phy_state(phy_state_)
cc_idx(cc_idx_), phy_state(phy_state_), log_h(log)
{
cf_t* buffer_c[SRSLTE_MAX_PORTS] = {};
// Allocate buffers
for (uint32_t i = 0; phy_state_->args.dl.nof_rx_antennas; i++) {
rx_buffer[i].resize(SRSLTE_SF_LEN_PRB(phy_state->args.max_prb));
buffer_c[i] = rx_buffer[i].data();
uint32_t sf_len = SRSLTE_SF_LEN_PRB(phy_state->args.dl.nof_max_prb);
for (uint32_t i = 0; i < phy_state_->args.dl.nof_rx_antennas; i++) {
rx_buffer[i] = srslte_vec_cf_malloc(sf_len);
tx_buffer[i] = srslte_vec_cf_malloc(sf_len);
buffer_c[i] = rx_buffer[i];
}
if (srslte_ue_dl_nr_init(&ue_dl, buffer_c, &phy_state_->args.dl)) {
if (srslte_ue_dl_nr_init(&ue_dl, buffer_c, &phy_state_->args.dl) < SRSLTE_SUCCESS) {
ERROR("Error initiating UE DL NR\n");
return;
}
@ -46,18 +48,29 @@ cc_worker::cc_worker(uint32_t cc_idx_, srslte::log* log, phy_nr_state* phy_state
ERROR("Error init soft-buffer\n");
return;
}
data.resize(SRSLTE_SCH_NR_MAX_NOF_CB_LDPC * SRSLTE_LDPC_MAX_LEN_ENCODED_CB);
data.resize(SRSLTE_SCH_NR_MAX_NOF_CB_LDPC * SRSLTE_LDPC_MAX_LEN_ENCODED_CB / 8);
}
cc_worker::~cc_worker()
{
srslte_ue_dl_nr_free(&ue_dl);
srslte_softbuffer_rx_free(&softbuffer_rx);
for (cf_t* p : rx_buffer) {
if (p != nullptr) {
free(p);
}
}
for (cf_t* p : tx_buffer) {
if (p != nullptr) {
free(p);
}
}
}
bool cc_worker::set_carrier(const srslte_carrier_nr_t* carrier)
{
if (srslte_ue_dl_nr_set_carrier(&ue_dl, carrier) < SRSLTE_SUCCESS) {
ERROR("Error setting carrier\n");
return false;
}
@ -75,7 +88,7 @@ cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx)
return nullptr;
}
return rx_buffer.at(antenna_idx).data();
return rx_buffer.at(antenna_idx);
}
uint32_t cc_worker::get_buffer_len()
@ -85,9 +98,9 @@ uint32_t cc_worker::get_buffer_len()
bool cc_worker::work_dl()
{
srslte_pdsch_grant_nr_t pdsch_grant = {};
srslte_pdsch_cfg_nr_t pdsch_cfg = phy_state->cfg.pdsch;
srslte_pdsch_res_nr_t pdsch_res = {};
srslte_pdsch_grant_nr_t pdsch_grant = {};
srslte_pdsch_cfg_nr_t pdsch_cfg = phy_state->cfg.pdsch;
std::array<srslte_pdsch_res_nr_t, SRSLTE_MAX_CODEWORDS> pdsch_res = {};
// Use grant default A time resources with m=0
if (srslte_ue_dl_nr_pdsch_time_resource_default_A(0, pdsch_cfg.dmrs_cfg_typeA.typeA_pos, &pdsch_grant) <
@ -97,27 +110,35 @@ bool cc_worker::work_dl()
}
pdsch_grant.nof_layers = ue_dl.carrier.max_mimo_layers;
pdsch_grant.dci_format = srslte_dci_format_nr_1_0;
pdsch_grant.rnti = 0x1234;
for (uint32_t i = 0; i < ue_dl.carrier.nof_prb; i++) {
pdsch_grant.prb_idx[i] = true;
}
if (srslte_ra_nr_fill_tb(&pdsch_cfg, &pdsch_grant, 20, &pdsch_grant.tb[0]) < SRSLTE_SUCCESS) {
if (srslte_ra_nr_fill_tb(&pdsch_cfg, &pdsch_grant, 27, &pdsch_grant.tb[0]) < SRSLTE_SUCCESS) {
ERROR("Error filing tb\n");
return false;
}
pdsch_res.payload = data.data();
pdsch_res[0].payload = data.data();
pdsch_grant.tb[0].softbuffer.rx = &softbuffer_rx;
srslte_softbuffer_rx_reset(pdsch_grant.tb[0].softbuffer.rx);
srslte_ue_dl_nr_estimate_fft(&ue_dl);
if (srslte_ue_dl_nr_pdsch_get(&ue_dl, &dl_slot_cfg, &pdsch_cfg, &pdsch_grant, &pdsch_res) < SRSLTE_SUCCESS) {
if (srslte_ue_dl_nr_pdsch_get(&ue_dl, &dl_slot_cfg, &pdsch_cfg, &pdsch_grant, pdsch_res.data()) < SRSLTE_SUCCESS) {
ERROR("Error decoding PDSCH\n");
return false;
}
// Logging
if (log_h->get_level() >= srslte::LOG_LEVEL_INFO) {
char str[512];
srslte_ue_dl_nr_pdsch_info(&ue_dl, &pdsch_cfg, &pdsch_grant, pdsch_res.data(), str, sizeof(str));
log_h->info("PDSCH: cc=%d, %s\n", cc_idx, str);
}
return true;
}

View File

@ -69,5 +69,5 @@ void sf_worker::work_imp()
}
}
}; // namespace nr
}; // namespace srsue
} // namespace nr
} // namespace srsue

View File

@ -27,6 +27,15 @@ worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers) {}
bool worker_pool::init(phy_common* common, srslte::logger* logger, int prio)
{
// Set NR arguments
phy_state.args.nof_carriers = common->args->nof_nr_carriers;
phy_state.args.dl.nof_max_prb = common->args->nr_nof_prb;
// Skip init of workers if no NR carriers
if (phy_state.args.nof_carriers == 0) {
return true;
}
// Create logs
// Create array of pointers to phy_logs
for (uint32_t i = 0; i < common->args->nof_phy_threads; i++) {
@ -41,9 +50,17 @@ bool worker_pool::init(phy_common* common, srslte::logger* logger, int prio)
// Add workers to workers pool and start threads
for (uint32_t i = 0; i < common->args->nof_phy_threads; i++) {
auto w = std::unique_ptr<sf_worker>(new sf_worker(common, &phy_state, (srslte::log*)log_vec[i].get()));
pool.init_worker(i, w.get(), prio, common->args->worker_cpu_mask);
workers.push_back(std::move(w));
auto w = new sf_worker(common, &phy_state, (srslte::log*)log_vec[i].get());
pool.init_worker(i, w, prio, common->args->worker_cpu_mask);
workers.push_back(std::unique_ptr<sf_worker>(w));
srslte_carrier_nr_t c = {};
c.nof_prb = phy_state.args.dl.nof_max_prb;
c.max_mimo_layers = 1;
if (not w->set_carrier_unlocked(0, &c)) {
return false;
}
}
return true;
@ -69,5 +86,5 @@ void worker_pool::stop()
pool.stop();
}
}; // namespace nr
}; // namespace srsue
} // namespace nr
} // namespace srsue

View File

@ -157,12 +157,14 @@ void phy::run_thread()
// Initialise workers
lte_workers.init(&common, logger, WORKERS_THREAD_PRIO);
nr_workers.init(&common, logger, WORKERS_THREAD_PRIO);
// Warning this must be initialized after all workers have been added to the pool
sfsync.init(radio,
stack,
&prach_buffer,
&lte_workers,
&nr_workers,
&common,
log_h.get(),
log_phy_lib_h.get(),
@ -198,6 +200,7 @@ void phy::stop()
if (is_configured) {
sfsync.stop();
lte_workers.stop();
nr_workers.stop();
prach_buffer.stop();
wait_thread_finish();
@ -213,7 +216,7 @@ void phy::get_metrics(phy_metrics_t* m)
m->info[0].pci = cell.id;
m->info[0].dl_earfcn = dl_earfcn;
for (uint32_t i = 1; i < args.nof_carriers; i++) {
for (uint32_t i = 1; i < args.nof_lte_carriers; i++) {
m->info[i].dl_earfcn = common.cell_state.get_earfcn(i);
m->info[i].pci = common.cell_state.get_pci(i);
}
@ -222,7 +225,7 @@ void phy::get_metrics(phy_metrics_t* m)
common.get_dl_metrics(m->dl);
common.get_ul_metrics(m->ul);
common.get_sync_metrics(m->sync);
m->nof_active_cc = args.nof_carriers;
m->nof_active_cc = args.nof_lte_carriers;
}
void phy::set_timeadv_rar(uint32_t ta_cmd)
@ -455,7 +458,7 @@ bool phy::set_config(srslte::phy_cfg_t config_, uint32_t cc_idx)
}
// Check parameters are valid
if (cc_idx >= args.nof_carriers) {
if (cc_idx >= args.nof_lte_carriers) {
srslte::console("Received SCell configuration for index %d but there are not enough CC workers available\n",
cc_idx);
return false;
@ -510,7 +513,7 @@ bool phy::set_scell(srslte_cell_t cell_info, uint32_t cc_idx, uint32_t earfcn)
}
// Check parameters are valid
if (cc_idx >= args.nof_carriers) {
if (cc_idx >= args.nof_lte_carriers) {
srslte::console("Received SCell configuration for index %d but there are not enough CC workers available\n",
cc_idx);
return false;

View File

@ -57,7 +57,8 @@ void phy_common::init(phy_args_t* _args,
// Instantiate UL channel emulator
if (args->ul_channel_args.enable) {
ul_channel = srslte::channel_ptr(new srslte::channel(args->ul_channel_args, args->nof_carriers * args->nof_rx_ant));
ul_channel =
srslte::channel_ptr(new srslte::channel(args->ul_channel_args, args->nof_lte_carriers * args->nof_rx_ant));
}
}
@ -363,7 +364,7 @@ bool phy_common::get_ul_pending_grant(srslte_ul_sf_cfg_t* sf, uint32_t cc_idx, u
uint32_t phy_common::get_ul_uci_cc(uint32_t tti_tx) const
{
std::lock_guard<std::mutex> lock(pending_ul_grant_mutex);
for (uint32_t cc = 0; cc < args->nof_carriers; cc++) {
for (uint32_t cc = 0; cc < args->nof_lte_carriers; cc++) {
const pending_ul_grant_t& grant = pending_ul_grant[cc][tti_tx];
if (grant.enable) {
return cc;
@ -789,7 +790,7 @@ void phy_common::get_dl_metrics(dl_metrics_t m[SRSLTE_MAX_CARRIERS])
{
std::unique_lock<std::mutex> lock(metrics_mutex);
for (uint32_t i = 0; i < args->nof_carriers; i++) {
for (uint32_t i = 0; i < args->nof_lte_carriers; i++) {
m[i] = dl_metrics[i];
dl_metrics[i] = {};
dl_metrics_count[i] = 0;
@ -816,7 +817,7 @@ void phy_common::get_ch_metrics(ch_metrics_t m[SRSLTE_MAX_CARRIERS])
{
std::unique_lock<std::mutex> lock(metrics_mutex);
for (uint32_t i = 0; i < args->nof_carriers; i++) {
for (uint32_t i = 0; i < args->nof_lte_carriers; i++) {
m[i] = ch_metrics[i];
ch_metrics[i] = {};
ch_metrics_count[i] = 0;
@ -836,7 +837,7 @@ void phy_common::get_ul_metrics(ul_metrics_t m[SRSLTE_MAX_CARRIERS])
{
std::unique_lock<std::mutex> lock(metrics_mutex);
for (uint32_t i = 0; i < args->nof_carriers; i++) {
for (uint32_t i = 0; i < args->nof_lte_carriers; i++) {
m[i] = ul_metrics[i];
ul_metrics[i] = {};
ul_metrics_count[i] = 0;
@ -857,7 +858,7 @@ void phy_common::get_sync_metrics(sync_metrics_t m[SRSLTE_MAX_CARRIERS])
{
std::unique_lock<std::mutex> lock(metrics_mutex);
for (uint32_t i = 0; i < args->nof_carriers; i++) {
for (uint32_t i = 0; i < args->nof_lte_carriers; i++) {
m[i] = sync_metrics[i];
sync_metrics[i] = {};
sync_metrics_count[i] = 0;
@ -920,7 +921,7 @@ void phy_common::reset()
// Release mapping of secondary cells
if (args != nullptr && radio_h != nullptr) {
for (uint32_t i = 1; i < args->nof_carriers; i++) {
for (uint32_t i = 1; i < args->nof_lte_carriers; i++) {
radio_h->release_freq(i);
}
}

View File

@ -48,22 +48,24 @@ static SRSLTE_AGC_CALLBACK(callback_set_rx_gain)
void sync::init(srslte::radio_interface_phy* _radio,
stack_interface_phy_lte* _stack,
prach* _prach_buffer,
lte::worker_pool* _workers_pool,
lte::worker_pool* _lte_workers_pool,
nr::worker_pool* _nr_workers_pool,
phy_common* _worker_com,
srslte::log* _log_h,
srslte::log* _log_phy_lib_h,
uint32_t prio,
int sync_cpu_affinity)
{
radio_h = _radio;
log_h = _log_h;
log_phy_lib_h = _log_phy_lib_h;
stack = _stack;
workers_pool = _workers_pool;
worker_com = _worker_com;
prach_buffer = _prach_buffer;
radio_h = _radio;
log_h = _log_h;
log_phy_lib_h = _log_phy_lib_h;
stack = _stack;
lte_worker_pool = _lte_workers_pool;
nr_worker_pool = _nr_workers_pool;
worker_com = _worker_com;
prach_buffer = _prach_buffer;
nof_rf_channels = worker_com->args->nof_carriers * worker_com->args->nof_rx_ant;
nof_rf_channels = worker_com->args->nof_lte_carriers * worker_com->args->nof_rx_ant;
if (nof_rf_channels == 0 || nof_rf_channels > SRSLTE_MAX_CHANNELS) {
Error("SYNC: Invalid number of RF channels (%d)\n", nof_rf_channels);
return;
@ -86,14 +88,14 @@ void sync::init(srslte::radio_interface_phy* _radio,
sfn_p.init(&ue_sync, worker_com->args, sf_buffer, sf_buffer.size(), log_h);
// Start intra-frequency measurement
for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) {
for (uint32_t i = 0; i < worker_com->args->nof_lte_carriers; i++) {
scell::intra_measure* q = new scell::intra_measure;
q->init(i, worker_com, this, log_h);
intra_freq_meas.push_back(std::unique_ptr<scell::intra_measure>(q));
}
// Allocate Secondary serving cell synchronization
for (uint32_t i = 1; i < worker_com->args->nof_carriers; i++) {
for (uint32_t i = 1; i < worker_com->args->nof_lte_carriers; i++) {
// Give the logical channel
scell_sync[i] = std::unique_ptr<scell::sync>(new scell::sync(this, i * worker_com->args->nof_rx_ant));
}
@ -400,7 +402,9 @@ void sync::run_sfn_sync_state()
}
}
void sync::run_camping_in_sync_state(lte::sf_worker* worker, srslte::rf_buffer_t& sync_buffer)
void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker,
nr::sf_worker* nr_worker,
srslte::rf_buffer_t& sync_buffer)
{
// Update logging TTI
log_h->step(tti);
@ -454,12 +458,12 @@ void sync::run_camping_in_sync_state(lte::sf_worker* worker, srslte::rf_buffer_t
}
}
Debug("SYNC: Worker %d synchronized\n", worker->get_id());
Debug("SYNC: Worker %d synchronized\n", lte_worker->get_id());
metrics.sfo = srslte_ue_sync_get_sfo(&ue_sync);
metrics.cfo = srslte_ue_sync_get_cfo(&ue_sync);
metrics.ta_us = worker_com->ta.get_usec();
for (uint32_t i = 0; i < worker_com->args->nof_carriers; i++) {
for (uint32_t i = 0; i < worker_com->args->nof_lte_carriers; i++) {
worker_com->set_sync_metrics(i, metrics);
}
@ -471,22 +475,22 @@ void sync::run_camping_in_sync_state(lte::sf_worker* worker, srslte::rf_buffer_t
}
}
worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : nullptr, prach_power);
lte_worker->set_prach(prach_ptr ? &prach_ptr[prach_sf_cnt * SRSLTE_SF_LEN_PRB(cell.nof_prb)] : nullptr, prach_power);
// Execute Serving Cell state FSM
worker_com->cell_state.run_tti(tti);
// Set CFO for all Carriers
for (uint32_t cc = 0; cc < worker_com->args->nof_carriers; cc++) {
worker->set_cfo_unlocked(cc, get_tx_cfo());
for (uint32_t cc = 0; cc < worker_com->args->nof_lte_carriers; cc++) {
lte_worker->set_cfo_unlocked(cc, get_tx_cfo());
worker_com->update_cfo_measurement(cc, srslte_ue_sync_get_cfo(&ue_sync));
}
worker->set_tti(tti);
lte_worker->set_tti(tti);
// Compute TX time: Any transmission happens in TTI+4 thus advance 4 ms the reception time
last_rx_time.add(FDD_HARQ_DELAY_DL_MS * 1e-3);
worker->set_tx_time(last_rx_time);
lte_worker->set_tx_time(last_rx_time);
// Advance/reset prach subframe pointer
if (prach_ptr) {
@ -497,37 +501,57 @@ void sync::run_camping_in_sync_state(lte::sf_worker* worker, srslte::rf_buffer_t
}
}
// Start worker
worker_com->semaphore.push(worker);
workers_pool->start_worker(worker);
// Start LTE worker
worker_com->semaphore.push(lte_worker);
lte_worker_pool->start_worker(lte_worker);
// Start NR worker only if present
if (nr_worker != nullptr) {
nr_worker_pool->start_worker(nr_worker);
}
}
void sync::run_camping_state()
{
lte::sf_worker* worker = (lte::sf_worker*)workers_pool->wait_worker(tti);
lte::sf_worker* lte_worker = lte_worker_pool->wait_worker(tti);
srslte::rf_buffer_t sync_buffer = {};
if (worker == nullptr) {
if (lte_worker == nullptr) {
// wait_worker() only returns NULL if it's being closed. Quit now to avoid unnecessary loops here
running = false;
return;
}
nr::sf_worker* nr_worker = nullptr;
if (worker_com->args->nof_nr_carriers > 0) {
nr_worker = nr_worker_pool->wait_worker(tti);
if (nr_worker == nullptr) {
running = false;
return;
}
}
// Map carrier/antenna buffers to worker buffers
for (uint32_t c = 0; c < worker_com->args->nof_carriers; c++) {
uint32_t cc_idx = 0;
for (uint32_t lte_cc_idx = 0; lte_cc_idx < worker_com->args->nof_lte_carriers; lte_cc_idx++, cc_idx++) {
for (uint32_t i = 0; i < worker_com->args->nof_rx_ant; i++) {
sync_buffer.set(c, i, worker_com->args->nof_rx_ant, worker->get_buffer(c, i));
sync_buffer.set(cc_idx, i, worker_com->args->nof_rx_ant, lte_worker->get_buffer(lte_cc_idx, i));
}
}
for (uint32_t nr_cc_idx = 0; nr_cc_idx < worker_com->args->nof_nr_carriers; nr_cc_idx++, cc_idx++) {
for (uint32_t i = 0; i < worker_com->args->nof_rx_ant; i++) {
sync_buffer.set(cc_idx, i, worker_com->args->nof_rx_ant, nr_worker->get_buffer(nr_cc_idx, i));
}
}
// Primary Cell (PCell) Synchronization
switch (srslte_ue_sync_zerocopy(&ue_sync, sync_buffer.to_cf_t(), worker->get_buffer_len())) {
switch (srslte_ue_sync_zerocopy(&ue_sync, sync_buffer.to_cf_t(), lte_worker->get_buffer_len())) {
case 1:
run_camping_in_sync_state(worker, sync_buffer);
run_camping_in_sync_state(lte_worker, nr_worker, sync_buffer);
break;
case 0:
Warning("SYNC: Out-of-sync detected in PSS/SSS\n");
out_of_sync();
worker->release();
lte_worker->release();
// Force decoding MIB, for making sure that the TTI will be right
if (!force_camping_sfn_sync) {
@ -537,7 +561,7 @@ void sync::run_camping_state()
break;
default:
radio_error();
worker->release();
lte_worker->release();
break;
}
@ -774,12 +798,12 @@ bool sync::set_cell(float cfo)
// Reset cell configuration
for (uint32_t i = 0; i < worker_com->args->nof_phy_threads; i++) {
(*workers_pool)[i]->reset_cell_unlocked(0);
(*lte_worker_pool)[i]->reset_cell_unlocked(0);
}
bool success = true;
for (uint32_t i = 0; i < worker_com->args->nof_phy_threads; i++) {
lte::sf_worker* w = (lte::sf_worker*)workers_pool->wait_worker_id(i);
lte::sf_worker* w = lte_worker_pool->wait_worker_id(i);
if (w) {
success &= w->set_cell_unlocked(0, cell);
w->release();

View File

@ -199,8 +199,16 @@ int ue::parse_args(const all_args_t& args_)
return SRSLTE_ERROR;
}
if (args.rf.nof_carriers <= args.phy.nof_nr_carriers) {
fprintf(stderr,
"Maximum number of carriers enough for NR and LTE (%d <= %d)\n",
args.rf.nof_carriers,
args.phy.nof_nr_carriers);
return SRSLTE_ERROR;
}
// replicate some RF parameter to make them available to PHY
args.phy.nof_carriers = args.rf.nof_carriers;
args.phy.nof_lte_carriers = args.rf.nof_carriers - args.phy.nof_nr_carriers;
args.phy.nof_rx_ant = args.rf.nof_antennas;
args.phy.agc_enable = args.rf.rx_gain < 0.0f;
@ -241,7 +249,7 @@ int ue::parse_args(const all_args_t& args_)
args.stack.rrc.ue_category = (uint32_t)strtoul(args.stack.rrc.ue_category_str.c_str(), nullptr, 10);
// Consider Carrier Aggregation support if more than one
args.stack.rrc.support_ca = (args.rf.nof_carriers > 1);
args.stack.rrc.support_ca = (args.phy.nof_lte_carriers > 1);
return SRSLTE_SUCCESS;
}