gnb,prach: add PRACH to gNB

rebase of Xavier's feature_gnb_prach branch
master
Xavier Arteaga 3 years ago committed by Andre Puschmann
parent fb92118bb7
commit b25681f9de

@ -162,42 +162,31 @@ public:
virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0; virtual void notify_pdcp_integrity_error(uint16_t rnti, uint32_t lcid) = 0;
}; };
class phy_interface_stack_nr class phy_interface_rrc_nr
{ {
public: public:
const static int MAX_DL_GRANTS = 4; /**
* @brief Describes physical layer configuration common among all the UEs for a given cell
typedef struct { */
// TODO: include NR related fields struct common_cfg_t {
} dl_sched_grant_t; srsran_carrier_nr_t carrier;
srsran_pdcch_cfg_nr_t pdcch;
typedef struct { srsran_prach_cfg_t prach;
bool mib_present; };
} bch_sched_t;
virtual int set_common_cfg(const common_cfg_t& common_cfg) = 0;
typedef struct { };
uint32_t tti;
uint32_t nof_grants; class phy_interface_mac_nr
dl_sched_grant_t pdsch[MAX_DL_GRANTS]; {
int beam_id; public:
} dl_config_request_t; // TBD
};
typedef struct {
bch_sched_t pbch; class phy_interface_stack_nr : public phy_interface_rrc_nr, public phy_interface_mac_nr
uint16_t length; {
uint16_t index; // index indicated in dl_config public:
uint8_t* data[SRSRAN_MAX_TB]; // always a pointer in our case // TBD
} tx_request_pdu_t;
typedef struct {
uint32_t tti;
uint32_t tb_len;
uint32_t nof_pdus;
tx_request_pdu_t pdus[MAX_DL_GRANTS];
} tx_request_t;
virtual int dl_config_request(const dl_config_request_t& request) = 0;
virtual int tx_request(const tx_request_t& request) = 0;
}; };
class stack_interface_mac class stack_interface_mac
@ -271,11 +260,17 @@ public:
// ... add signal measurements here // ... add signal measurements here
}; };
virtual int slot_indication(const srsran_slot_cfg_t& slot_cfg) = 0; struct rach_info_t {
virtual int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) = 0; uint32_t preamble;
virtual int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) = 0; uint32_t time_adv;
virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0; };
virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) = 0;
virtual int slot_indication(const srsran_slot_cfg_t& slot_cfg) = 0;
virtual int get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) = 0;
virtual int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) = 0;
virtual int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) = 0;
virtual int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) = 0;
virtual void rach_detected(const rach_info_t& rach_info) = 0;
}; };
class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr

@ -147,8 +147,14 @@ typedef struct SRSRAN_API {
uint32_t pid; ///< HARQ process number uint32_t pid; ///< HARQ process number
uint32_t dai; ///< Downlink assignment index uint32_t dai; ///< Downlink assignment index
uint32_t tpc; ///< TPC command for scheduled PUCCH uint32_t tpc; ///< TPC command for scheduled PUCCH
uint32_t pucch_resource; ///< PUCCH resource indicator uint32_t pucch_resource; ///< PUCCH resource indicator for HARQ feedback
///< @note PUCCH resource is selected from PUCCH-ResourceSet if available, otherwise the UE
///< shall pick a pucch-ResourceCommon from Table 9.2.1-1.
uint32_t harq_feedback; ///< PDSCH-to-HARQ_feedback timing indicator uint32_t harq_feedback; ///< PDSCH-to-HARQ_feedback timing indicator
///< @note harq_feedback for format 1_0 indicates the delay between the PDSCH reception and
///< the UL transmission timing
///< @note harq_feedback for format 1_1 is index of the delay indicated in DL data to UL ACK
///< dedicated configuration table
// P-RNTI specific fields // P-RNTI specific fields
uint32_t smi; ///< Short Messages Indicator uint32_t smi; ///< Short Messages Indicator

@ -197,6 +197,7 @@ void phy_cfg_nr_default_t::make_prach_default_lte(srsran_prach_cfg_t& prach)
prach.config_idx = 0; prach.config_idx = 0;
prach.freq_offset = 2; prach.freq_offset = 2;
prach.root_seq_idx = 0; prach.root_seq_idx = 0;
prach.is_nr = true;
} }
phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg) phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg)
@ -250,4 +251,4 @@ phy_cfg_nr_default_t::phy_cfg_nr_default_t(const reference_cfg_t& reference_cfg)
} }
} }
} // namespace srsran } // namespace srsran

@ -32,12 +32,11 @@ class slot_worker final : public srsran::thread_pool::worker
{ {
public: public:
struct args_t { struct args_t {
uint32_t cell_index = 0; uint32_t cell_index = 0;
srsran_carrier_nr_t carrier = {}; uint32_t nof_max_prb = SRSRAN_MAX_PRB_NR;
uint32_t nof_tx_ports = 1; uint32_t nof_tx_ports = 1;
uint32_t nof_rx_ports = 1; uint32_t nof_rx_ports = 1;
uint32_t pusch_max_nof_iter = 10; uint32_t pusch_max_nof_iter = 10;
srsran_pdcch_cfg_nr_t pdcch_cfg = {}; ///< PDCCH configuration
}; };
slot_worker(srsran::phy_common_interface& common_, stack_interface_phy_nr& stack_, srslog::basic_logger& logger); slot_worker(srsran::phy_common_interface& common_, stack_interface_phy_nr& stack_, srslog::basic_logger& logger);
@ -45,6 +44,8 @@ public:
bool init(const args_t& args); bool init(const args_t& args);
bool set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_);
/* Functions used by main PHY thread */ /* Functions used by main PHY thread */
cf_t* get_buffer_rx(uint32_t antenna_idx); cf_t* get_buffer_rx(uint32_t antenna_idx);
cf_t* get_buffer_tx(uint32_t antenna_idx); cf_t* get_buffer_tx(uint32_t antenna_idx);

@ -15,7 +15,9 @@
#include "slot_worker.h" #include "slot_worker.h"
#include "srsenb/hdr/phy/phy_interfaces.h" #include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsenb/hdr/phy/prach_worker.h"
#include "srsran/common/thread_pool.h" #include "srsran/common/thread_pool.h"
#include "srsran/interfaces/enb_mac_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h" #include "srsran/interfaces/gnb_interfaces.h"
namespace srsenb { namespace srsenb {
@ -23,11 +25,64 @@ namespace nr {
class worker_pool class worker_pool
{ {
private:
class prach_stack_adaptor_t : public stack_interface_phy_lte
{
private:
stack_interface_phy_nr& stack;
public:
prach_stack_adaptor_t(stack_interface_phy_nr& stack_) : stack(stack_)
{
// Do nothing
}
int sr_detected(uint32_t tti, uint16_t rnti) override { return 0; }
void rach_detected(uint32_t tti, uint32_t primary_cc_idx, uint32_t preamble_idx, uint32_t time_adv) override
{
stack_interface_phy_nr::rach_info_t rach_info = {};
rach_info.preamble = preamble_idx;
rach_info.time_adv = time_adv;
stack.rach_detected(rach_info);
}
int ri_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t ri_value) override { return 0; }
int pmi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t pmi_value) override { return 0; }
int cqi_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t cqi_value) override { return 0; }
int snr_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, float snr_db, ul_channel_t ch) override { return 0; }
int ta_info(uint32_t tti, uint16_t rnti, float ta_us) override { return 0; }
int ack_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t tb_idx, bool ack) override { return 0; }
int crc_info(uint32_t tti, uint16_t rnti, uint32_t cc_idx, uint32_t nof_bytes, bool crc_res) override { return 0; }
int push_pdu(uint32_t tti_rx,
uint16_t rnti,
uint32_t enb_cc_idx,
uint32_t nof_bytes,
bool crc_res,
uint32_t ul_nof_prbs) override
{
return 0;
}
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override { return 0; }
int get_mch_sched(uint32_t tti, bool is_mcch, dl_sched_list_t& dl_sched_res) override { return 0; }
int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override { return 0; }
void set_sched_dl_tti_mask(uint8_t* tti_mask, uint32_t nof_sfs) override {}
void tti_clock() override {}
};
srsran::phy_common_interface& common; srsran::phy_common_interface& common;
stack_interface_phy_nr& stack; stack_interface_phy_nr& stack;
srslog::sink& log_sink; srslog::sink& log_sink;
srsran::thread_pool pool; srsran::thread_pool pool;
std::vector<std::unique_ptr<slot_worker> > workers; std::vector<std::unique_ptr<slot_worker> > workers;
prach_worker_pool prach;
uint32_t current_tti = 0; ///< Current TTI, read and write from same thread
srslog::basic_logger& logger;
prach_stack_adaptor_t prach_stack_adaptor;
// Current configuration
std::mutex common_cfg_mutex;
srsran_carrier_nr_t carrier = {};
srsran_pdcch_cfg_nr_t pdcch_cfg = {};
public: public:
struct args_t { struct args_t {
@ -47,6 +102,7 @@ public:
slot_worker* wait_worker_id(uint32_t id); slot_worker* wait_worker_id(uint32_t id);
void start_worker(slot_worker* w); void start_worker(slot_worker* w);
void stop(); void stop();
int set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg);
}; };
} // namespace nr } // namespace nr

@ -77,6 +77,7 @@ public:
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override; int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override;
int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override; int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override;
int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override; int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override;
void rach_detected(const rach_info_t& rach_info) override;
private: private:
void run_thread() final; void run_thread() final;

@ -71,12 +71,9 @@ public:
int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override; int get_ul_sched(const srsran_slot_cfg_t& slot_cfg, ul_sched_t& ul_sched) override;
int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override; int pucch_info(const srsran_slot_cfg_t& slot_cfg, const pucch_info_t& pucch_info) override;
int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override; int pusch_info(const srsran_slot_cfg_t& slot_cfg, const pusch_info_t& pusch_info) override;
void rach_detected(const rach_info_t& rach_info) override;
private: private:
void get_dl_config(const uint32_t tti,
phy_interface_stack_nr::dl_config_request_t& config_request,
phy_interface_stack_nr::tx_request_t& tx_request);
// PDU processing // PDU processing
int handle_pdu(srsran::unique_byte_buffer_t pdu); int handle_pdu(srsran::unique_byte_buffer_t pdu);

@ -26,11 +26,12 @@ slot_worker::slot_worker(srsran::phy_common_interface& common_,
bool slot_worker::init(const args_t& args) bool slot_worker::init(const args_t& args)
{ {
// Calculate subframe length // Calculate subframe length
sf_len = SRSRAN_SF_LEN_PRB_NR(args.carrier.nof_prb); sf_len = SRSRAN_SF_LEN_PRB_NR(args.nof_max_prb);
// Copy common configurations // Copy common configurations
cell_index = args.cell_index; cell_index = args.cell_index;
pdcch_cfg = args.pdcch_cfg; // FIXME:
// pdcch_cfg = args.pdcch_cfg;
// Allocate Tx buffers // Allocate Tx buffers
tx_buffer.resize(args.nof_tx_ports); tx_buffer.resize(args.nof_tx_ports);
@ -55,10 +56,10 @@ bool slot_worker::init(const args_t& args)
// Prepare DL arguments // Prepare DL arguments
srsran_gnb_dl_args_t dl_args = {}; srsran_gnb_dl_args_t dl_args = {};
dl_args.pdsch.measure_time = true; dl_args.pdsch.measure_time = true;
dl_args.pdsch.max_layers = args.carrier.max_mimo_layers; dl_args.pdsch.max_layers = args.nof_tx_ports;
dl_args.pdsch.max_prb = args.carrier.nof_prb; dl_args.pdsch.max_prb = args.nof_max_prb;
dl_args.nof_tx_antennas = args.nof_tx_ports; dl_args.nof_tx_antennas = args.nof_tx_ports;
dl_args.nof_max_prb = args.carrier.nof_prb; dl_args.nof_max_prb = args.nof_max_prb;
// Initialise DL // Initialise DL
if (srsran_gnb_dl_init(&gnb_dl, tx_buffer.data(), &dl_args) < SRSRAN_SUCCESS) { if (srsran_gnb_dl_init(&gnb_dl, tx_buffer.data(), &dl_args) < SRSRAN_SUCCESS) {
@ -66,18 +67,13 @@ bool slot_worker::init(const args_t& args)
return false; return false;
} }
// Set gNb DL carrier
if (srsran_gnb_dl_set_carrier(&gnb_dl, &args.carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting DL carrier");
return false;
}
// Prepare UL arguments // Prepare UL arguments
srsran_gnb_ul_args_t ul_args = {}; srsran_gnb_ul_args_t ul_args = {};
ul_args.pusch.measure_time = true; ul_args.pusch.measure_time = true;
ul_args.pusch.max_layers = args.carrier.max_mimo_layers; ul_args.pusch.max_layers = args.nof_rx_ports;
ul_args.pusch.max_prb = args.carrier.nof_prb; ul_args.pusch.max_prb = args.nof_max_prb;
ul_args.nof_max_prb = args.carrier.nof_prb; ul_args.nof_max_prb = args.nof_max_prb;
// Initialise UL // Initialise UL
if (srsran_gnb_ul_init(&gnb_ul, rx_buffer[0], &ul_args) < SRSRAN_SUCCESS) { if (srsran_gnb_ul_init(&gnb_ul, rx_buffer[0], &ul_args) < SRSRAN_SUCCESS) {
@ -85,12 +81,6 @@ bool slot_worker::init(const args_t& args)
return false; return false;
} }
// Set gNb UL carrier
if (srsran_gnb_ul_set_carrier(&gnb_ul, &args.carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting UL carrier");
return false;
}
return true; return true;
} }
@ -157,7 +147,7 @@ bool slot_worker::work_ul()
return false; return false;
} }
// Decode PUCCH // For each PUCCH...
for (stack_interface_phy_nr::pucch_t& pucch : ul_sched.pucch) { for (stack_interface_phy_nr::pucch_t& pucch : ul_sched.pucch) {
stack_interface_phy_nr::pucch_info_t pucch_info = {}; stack_interface_phy_nr::pucch_info_t pucch_info = {};
pucch_info.uci_data.cfg = pucch.uci_cfg; pucch_info.uci_data.cfg = pucch.uci_cfg;
@ -188,7 +178,7 @@ bool slot_worker::work_ul()
} }
} }
// Decode PUSCH // For each PUSCH...
for (stack_interface_phy_nr::pusch_t& pusch : ul_sched.pusch) { for (stack_interface_phy_nr::pusch_t& pusch : ul_sched.pusch) {
// Get payload PDU // Get payload PDU
stack_interface_phy_nr::pusch_info_t pusch_info = {}; stack_interface_phy_nr::pusch_info_t pusch_info = {};
@ -197,7 +187,7 @@ bool slot_worker::work_ul()
pusch_info.pusch_data.tb[0].payload = pusch.data[0]; pusch_info.pusch_data.tb[0].payload = pusch.data[0];
pusch_info.pusch_data.tb[1].payload = pusch.data[1]; pusch_info.pusch_data.tb[1].payload = pusch.data[1];
// Decode PUCCH // Decode PUSCH
if (srsran_gnb_ul_get_pusch(&gnb_ul, &ul_slot_cfg, &pusch.sch, &pusch.sch.grant, &pusch_info.pusch_data) < if (srsran_gnb_ul_get_pusch(&gnb_ul, &ul_slot_cfg, &pusch.sch, &pusch.sch.grant, &pusch_info.pusch_data) <
SRSRAN_SUCCESS) { SRSRAN_SUCCESS) {
logger.error("Error getting PUSCH"); logger.error("Error getting PUSCH");
@ -340,6 +330,27 @@ void slot_worker::work_imp()
common.worker_end(this, true, tx_rf_buffer, tx_time, true); common.worker_end(this, true, tx_rf_buffer, tx_time, true);
} }
bool slot_worker::set_common_cfg(const srsran_carrier_nr_t& carrier, const srsran_pdcch_cfg_nr_t& pdcch_cfg_)
{
// Set gNb DL carrier
if (srsran_gnb_dl_set_carrier(&gnb_dl, &carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting DL carrier");
return false;
}
// Set gNb UL carrier
if (srsran_gnb_ul_set_carrier(&gnb_ul, &carrier) < SRSRAN_SUCCESS) {
logger.error("Error setting UL carrier");
return false;
}
pdcch_cfg = pdcch_cfg_;
// Update subframe length
sf_len = SRSRAN_SF_LEN_PRB_NR(carrier.nof_prb);
return true;
}
} // namespace nr } // namespace nr
} // namespace srsenb } // namespace srsenb

@ -18,15 +18,23 @@ worker_pool::worker_pool(srsran::phy_common_interface& common_,
stack_interface_phy_nr& stack_, stack_interface_phy_nr& stack_,
srslog::sink& log_sink_, srslog::sink& log_sink_,
uint32_t max_workers) : uint32_t max_workers) :
pool(max_workers), common(common_), stack(stack_), log_sink(log_sink_) pool(max_workers),
common(common_),
stack(stack_),
log_sink(log_sink_),
logger(srslog::fetch_basic_logger("PHY-NR", log_sink)),
prach_stack_adaptor(stack_)
{ {
// Do nothing // Do nothing
} }
bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_list) bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_list)
{ {
// Add workers to workers pool and start threads // Configure logger
srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level); srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level);
logger.set_level(log_level);
// Add workers to workers pool and start threads
for (uint32_t i = 0; i < args.nof_phy_threads; i++) { for (uint32_t i = 0; i < args.nof_phy_threads; i++) {
auto& log = srslog::fetch_basic_logger(fmt::format("{}PHY{}-NR", args.log.id_preamble, i), log_sink); auto& log = srslog::fetch_basic_logger(fmt::format("{}PHY{}-NR", args.log.id_preamble, i), log_sink);
log.set_level(log_level); log.set_level(log_level);
@ -39,11 +47,10 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li
slot_worker::args_t w_args = {}; slot_worker::args_t w_args = {};
uint32_t cell_index = 0; uint32_t cell_index = 0;
w_args.cell_index = cell_index; w_args.cell_index = cell_index;
w_args.carrier = cell_list[cell_index].carrier; w_args.nof_max_prb = cell_list[cell_index].carrier.nof_prb;
w_args.nof_tx_ports = cell_list[cell_index].carrier.max_mimo_layers; w_args.nof_tx_ports = cell_list[cell_index].carrier.max_mimo_layers;
w_args.nof_rx_ports = cell_list[cell_index].carrier.max_mimo_layers; w_args.nof_rx_ports = cell_list[cell_index].carrier.max_mimo_layers;
w_args.pusch_max_nof_iter = args.pusch_max_nof_iter; w_args.pusch_max_nof_iter = args.pusch_max_nof_iter;
w_args.pdcch_cfg = cell_list[cell_index].pdcch;
if (not w->init(w_args)) { if (not w->init(w_args)) {
return false; return false;
@ -55,12 +62,41 @@ bool worker_pool::init(const args_t& args, const phy_cell_cfg_list_nr_t& cell_li
void worker_pool::start_worker(slot_worker* w) void worker_pool::start_worker(slot_worker* w)
{ {
// Feed PRACH detection before start processing
prach.new_tti(0, current_tti, w->get_buffer_rx(0));
// Start actual worker
pool.start_worker(w); pool.start_worker(w);
} }
slot_worker* worker_pool::wait_worker(uint32_t tti) slot_worker* worker_pool::wait_worker(uint32_t tti)
{ {
return (slot_worker*)pool.wait_worker(tti); slot_worker* w = (slot_worker*)pool.wait_worker(tti);
// Only if a worker was available
if (w != nullptr) {
srsran_carrier_nr_t carrier_;
srsran_pdcch_cfg_nr_t pdcch_cfg_;
// Copy configuration
{
std::unique_lock<std::mutex> lock(common_cfg_mutex);
carrier_ = carrier;
pdcch_cfg_ = pdcch_cfg;
}
// Set worker configuration
if (not w->set_common_cfg(carrier_, pdcch_cfg_)) {
logger.error("Error setting common config");
return nullptr;
}
}
// Save current TTI
current_tti = tti;
// Return worker
return w;
} }
slot_worker* worker_pool::wait_worker_id(uint32_t id) slot_worker* worker_pool::wait_worker_id(uint32_t id)
@ -71,7 +107,42 @@ slot_worker* worker_pool::wait_worker_id(uint32_t id)
void worker_pool::stop() void worker_pool::stop()
{ {
pool.stop(); pool.stop();
prach.stop();
}
int worker_pool::set_common_cfg(const phy_interface_rrc_nr::common_cfg_t& common_cfg)
{
// Best effort to convert NR carrier into LTE cell
srsran_cell_t cell = {};
int ret = srsran_carrier_to_cell(&common_cfg.carrier, &cell);
if (ret < SRSRAN_SUCCESS) {
logger.error("Converting carrier to cell for PRACH (%d)", ret);
return SRSRAN_ERROR;
}
// Best effort to set up NR-PRACH config reused for NR
srsran_prach_cfg_t prach_cfg = common_cfg.prach;
uint32_t lte_nr_prach_offset = (common_cfg.carrier.nof_prb - cell.nof_prb) / 2;
if (prach_cfg.freq_offset < lte_nr_prach_offset) {
logger.error("prach_cfg.freq_offset=%d is not compatible with LTE", prach_cfg.freq_offset);
return SRSRAN_ERROR;
}
prach_cfg.freq_offset -= lte_nr_prach_offset;
prach_cfg.is_nr = true;
// Set the PRACH configuration
prach.init(0, cell, prach_cfg, &prach_stack_adaptor, logger, 0, 1);
prach.set_max_prach_offset_us(1000);
// Save current configuration
{
std::unique_lock<std::mutex> lock(common_cfg_mutex);
carrier = common_cfg.carrier;
pdcch_cfg = common_cfg.pdcch;
}
return SRSRAN_SUCCESS;
} }
} // namespace nr } // namespace nr
} // namespace srsenb } // namespace srsenb

@ -198,4 +198,6 @@ int gnb_stack_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interf
return m_mac->pusch_info(slot_cfg, pusch_info); return m_mac->pusch_info(slot_cfg, pusch_info);
} }
void gnb_stack_nr::rach_detected(const rach_info_t& rach_info) {}
} // namespace srsenb } // namespace srsenb

@ -89,114 +89,6 @@ void mac_nr::stop()
void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics) {} void mac_nr::get_metrics(srsenb::mac_metrics_t& metrics) {}
// Fills both, DL_CONFIG.request and TX.request structs
void mac_nr::get_dl_config(const uint32_t tti,
phy_interface_stack_nr::dl_config_request_t& config_request,
phy_interface_stack_nr::tx_request_t& tx_request)
{
// send MIB over BCH every 80ms
if (tti % 80 == 0) {
// try to read BCH PDU from RRC
if (rrc_h->read_pdu_bcch_bch(tti, bcch_bch_payload) == SRSRAN_SUCCESS) {
logger.info("Adding BCH in TTI=%d", tti);
tx_request.pdus[tx_request.nof_pdus].pbch.mib_present = true;
tx_request.pdus[tx_request.nof_pdus].data[0] = bcch_bch_payload->msg;
tx_request.pdus[tx_request.nof_pdus].length = bcch_bch_payload->N_bytes;
tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus;
tx_request.nof_pdus++;
if (pcap) {
pcap->write_dl_bch(bcch_bch_payload->msg, bcch_bch_payload->N_bytes, 0xffff, 0, tti);
}
} else {
logger.error("Couldn't read BCH payload from RRC");
}
}
// Schedule SIBs
for (auto& sib : bcch_dlsch_payload) {
if (sib.payload->N_bytes > 0) {
if (tti % (sib.periodicity * 10) == 0) {
logger.info("Adding SIB %d in TTI=%d", sib.index, tti);
tx_request.pdus[tx_request.nof_pdus].data[0] = sib.payload->msg;
tx_request.pdus[tx_request.nof_pdus].length = sib.payload->N_bytes;
tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus;
if (pcap) {
pcap->write_dl_si_rnti_nr(sib.payload->msg, sib.payload->N_bytes, 0xffff, 0, tti);
}
tx_request.nof_pdus++;
}
}
}
// Add MAC padding if TTI is empty
if (tx_request.nof_pdus == 0) {
uint32_t buffer_index = tti % SRSRAN_FDD_NOF_HARQ;
ue_tx_buffer.at(buffer_index)->clear();
ue_tx_pdu.init_tx(ue_tx_buffer.at(buffer_index).get(), args.tb_size);
// read RLC PDU
ue_rlc_buffer->clear();
int pdu_len = rlc_h->read_pdu(args.rnti, 4, ue_rlc_buffer->msg, args.tb_size - 2);
// Only create PDU if RLC has something to tx
if (pdu_len > 0) {
logger.info("Adding MAC PDU for RNTI=%d", args.rnti);
ue_rlc_buffer->N_bytes = pdu_len;
logger.info(ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes, "Read %d B from RLC", ue_rlc_buffer->N_bytes);
// add to MAC PDU and pack
ue_tx_pdu.add_sdu(4, ue_rlc_buffer->msg, ue_rlc_buffer->N_bytes);
ue_tx_pdu.pack();
logger.debug(ue_tx_buffer.at(buffer_index)->msg,
ue_tx_buffer.at(buffer_index)->N_bytes,
"Generated MAC PDU (%d B)",
ue_tx_buffer.at(buffer_index)->N_bytes);
tx_request.pdus[tx_request.nof_pdus].data[0] = ue_tx_buffer.at(buffer_index)->msg;
tx_request.pdus[tx_request.nof_pdus].length = ue_tx_buffer.at(buffer_index)->N_bytes;
tx_request.pdus[tx_request.nof_pdus].index = tx_request.nof_pdus;
if (pcap) {
pcap->write_dl_crnti_nr(tx_request.pdus[tx_request.nof_pdus].data[0],
tx_request.pdus[tx_request.nof_pdus].length,
args.rnti,
buffer_index,
tti);
}
tx_request.nof_pdus++;
}
}
config_request.tti = tti;
tx_request.tti = tti;
}
int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
{
phy_interface_stack_nr::dl_config_request_t config_request = {};
phy_interface_stack_nr::tx_request_t tx_request = {};
// step MAC TTI
logger.set_context(slot_cfg.idx);
get_dl_config(slot_cfg.idx, config_request, tx_request);
// send DL_CONFIG.request
phy_h->dl_config_request(config_request);
// send TX.request
phy_h->tx_request(tx_request);
return SRSRAN_SUCCESS;
}
int mac_nr::rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& rx_data) int mac_nr::rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& rx_data)
{ {
// push received PDU on queue // push received PDU on queue
@ -270,6 +162,11 @@ int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg)
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
int mac_nr::slot_indication(const srsran_slot_cfg_t& slot_cfg)
{
return 0;
}
int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) int mac_nr::get_dl_sched(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched)
{ {
return 0; return 0;
@ -286,5 +183,6 @@ int mac_nr::pusch_info(const srsran_slot_cfg_t& slot_cfg, const mac_interface_ph
{ {
return 0; return 0;
} }
void mac_nr::rach_detected(const mac_interface_phy_nr::rach_info_t& rach_info) {}
} // namespace srsenb } // namespace srsenb

@ -20,7 +20,7 @@
namespace srsue { namespace srsue {
namespace nr { namespace nr {
class worker_pool class worker_pool : public srsue::phy_interface_stack_nr
{ {
private: private:
srslog::basic_logger& logger; srslog::basic_logger& logger;
@ -37,12 +37,18 @@ public:
sf_worker* wait_worker(uint32_t tti); sf_worker* wait_worker(uint32_t tti);
void start_worker(sf_worker* w); void start_worker(sf_worker* w);
void stop(); void stop();
void send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power); void send_prach(const uint32_t prach_occasion,
int set_ul_grant(std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS> array, uint16_t rnti, srsran_rnti_type_t rnti_type); const int preamble_index,
bool set_config(const srsran::phy_cfg_nr_t& cfg); const float preamble_received_target_power,
bool has_valid_sr_resource(uint32_t sr_id); const float ta_base_sec = 0.0f) override;
void clear_pending_grants(); int set_ul_grant(std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS> array,
uint16_t rnti,
srsran_rnti_type_t rnti_type) override;
bool set_config(const srsran::phy_cfg_nr_t& cfg) override;
bool has_valid_sr_resource(uint32_t sr_id) override;
void clear_pending_grants() override;
void get_metrics(phy_metrics_t& m); void get_metrics(phy_metrics_t& m);
int tx_request(const tx_request_t& request) override;
}; };
} // namespace nr } // namespace nr

@ -90,6 +90,7 @@ void sf_worker::work_imp()
if (prach_ptr != nullptr) { if (prach_ptr != nullptr) {
// PRACH is available, set buffer, transmit and return // PRACH is available, set buffer, transmit and return
tx_buffer.set(0, prach_ptr); tx_buffer.set(0, prach_ptr);
tx_buffer.set_nof_samples(SRSRAN_SF_LEN_PRB_NR(phy_state.cfg.carrier.nof_prb));
// Notify MAC about PRACH transmission // Notify MAC about PRACH transmission
phy_state.stack->prach_sent(TTI_TX(tti_rx), phy_state.stack->prach_sent(TTI_TX(tti_rx),

@ -91,7 +91,10 @@ void worker_pool::stop()
pool.stop(); pool.stop();
} }
void worker_pool::send_prach(uint32_t prach_occasion, uint32_t preamble_index, int preamble_received_target_power) void worker_pool::send_prach(const uint32_t prach_occasion,
const int preamble_index,
const float preamble_received_target_power,
const float ta_base_sec)
{ {
prach_buffer->prepare_to_send(preamble_index); prach_buffer->prepare_to_send(preamble_index);
} }
@ -186,6 +189,10 @@ void worker_pool::get_metrics(phy_metrics_t& m)
{ {
phy_state.get_metrics(m); phy_state.get_metrics(m);
} }
int worker_pool::tx_request(const phy_interface_mac_nr::tx_request_t& request)
{
return 0;
}
} // namespace nr } // namespace nr
} // namespace srsue } // namespace srsue

@ -20,7 +20,11 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
${Boost_LIBRARIES} ${Boost_LIBRARIES}
${ATOMIC_LIBS}) ${ATOMIC_LIBS})
add_nr_test(nr_phy_test_10MHz_dl_only nr_phy_test --duration=100 --gnb.stack.pdsch.slots=\"0,1,2,3,4,5\" --gnb.stack.pusch.slots=\"\") add_nr_test(nr_phy_test_10MHz_dl_only nr_phy_test
--duration=100
--gnb.stack.pdsch.slots=\"0,1,2,3,4,5\"
--gnb.stack.pusch.slots=\"\")
add_nr_test(nr_phy_test_10MHz_ul_only nr_phy_test add_nr_test(nr_phy_test_10MHz_ul_only nr_phy_test
--duration=100 # 100 slots --duration=100 # 100 slots
--gnb.stack.pdsch.slots=6 # No PDSCH --gnb.stack.pdsch.slots=6 # No PDSCH
@ -29,6 +33,7 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
--gnb.stack.pusch.length=52 # Full 10 MHz BW --gnb.stack.pusch.length=52 # Full 10 MHz BW
--gnb.stack.pusch.mcs=28 # Maximum MCS --gnb.stack.pusch.mcs=28 # Maximum MCS
) )
add_nr_test(nr_phy_test_10MHz_bidir nr_phy_test add_nr_test(nr_phy_test_10MHz_bidir nr_phy_test
--duration=100 # 100 slots --duration=100 # 100 slots
--gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots --gnb.stack.pdsch.slots=0,1,2,3,4,5 # All possible DL slots
@ -40,4 +45,12 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
--gnb.stack.pusch.length=52 # Full 10 MHz BW --gnb.stack.pusch.length=52 # Full 10 MHz BW
--gnb.stack.pusch.mcs=28 # Maximum MCS --gnb.stack.pusch.mcs=28 # Maximum MCS
) )
add_nr_test(nr_phy_test_10MHz_prach nr_phy_test
--duration=1000 # 100 slots
--gnb.stack.pdsch.slots=6 # No PDSCH
--gnb.stack.pusch.slots=0 # No PUSCH
--ue.stack.prach.period=30 # Transmit PRACH every 30 radio frames
--ue.stack.prach.preamble=10 # Use preamble 10
)
endif () endif ()

@ -26,6 +26,17 @@
class gnb_dummy_stack : public srsenb::stack_interface_phy_nr class gnb_dummy_stack : public srsenb::stack_interface_phy_nr
{ {
public:
struct prach_metrics_t {
uint32_t count;
float avg_ta;
};
struct metrics_t {
std::map<uint32_t, prach_metrics_t> prach = {}; ///< PRACH metrics indexed with premable index
srsenb::mac_ue_metrics_t mac = {}; ///< MAC metrics
};
private: private:
srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK"); srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB STK");
const uint16_t rnti = 0x1234; const uint16_t rnti = 0x1234;
@ -41,8 +52,8 @@ private:
srsran::phy_cfg_nr_t phy_cfg = {}; srsran::phy_cfg_nr_t phy_cfg = {};
bool valid = false; bool valid = false;
std::mutex mac_metrics_mutex; std::mutex metrics_mutex;
srsenb::mac_ue_metrics_t mac_metrics = {}; metrics_t metrics = {};
// HARQ feedback // HARQ feedback
class pending_ack_t class pending_ack_t
@ -245,16 +256,16 @@ private:
bool handle_uci_data(const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value) bool handle_uci_data(const srsran_uci_cfg_nr_t& cfg, const srsran_uci_value_nr_t& value)
{ {
std::unique_lock<std::mutex> lock(mac_metrics_mutex); std::unique_lock<std::mutex> lock(metrics_mutex);
for (uint32_t i = 0; i < cfg.ack.count; i++) { for (uint32_t i = 0; i < cfg.ack.count; i++) {
const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i]; const srsran_harq_ack_bit_t* ack_bit = &cfg.ack.bits[i];
bool is_ok = (value.ack[i] == 1) and value.valid; bool is_ok = (value.ack[i] == 1) and value.valid;
uint32_t tb_count = (ack_bit->tb0 ? 1 : 0) + (ack_bit->tb1 ? 1 : 0); uint32_t tb_count = (ack_bit->tb0 ? 1 : 0) + (ack_bit->tb1 ? 1 : 0);
mac_metrics.tx_brate += tx_harq_proc[ack_bit->pid].get_tbs(); metrics.mac.tx_brate += tx_harq_proc[ack_bit->pid].get_tbs();
mac_metrics.tx_pkts += tb_count; metrics.mac.tx_pkts += tb_count;
if (not is_ok) { if (not is_ok) {
mac_metrics.tx_errors += tb_count; metrics.mac.tx_errors += tb_count;
logger.debug("NACK received!"); logger.debug("NACK received!");
} }
} }
@ -452,19 +463,27 @@ public:
} }
if (not pusch_info.pusch_data.tb[0].crc) { if (not pusch_info.pusch_data.tb[0].crc) {
mac_metrics.rx_errors++; metrics.mac.rx_errors++;
} }
mac_metrics.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs(); metrics.mac.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs();
mac_metrics.rx_pkts++; metrics.mac.rx_pkts++;
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;
} }
srsenb::mac_ue_metrics_t get_metrics() void rach_detected(const rach_info_t& rach_info) override
{
std::unique_lock<std::mutex> lock(metrics_mutex);
prach_metrics_t& prach_metrics = metrics.prach[rach_info.preamble];
prach_metrics.avg_ta = SRSRAN_VEC_SAFE_CMA((float)rach_info.time_adv, prach_metrics.avg_ta, prach_metrics.count);
prach_metrics.count++;
}
metrics_t get_metrics()
{ {
std::unique_lock<std::mutex> lock(mac_metrics_mutex); std::unique_lock<std::mutex> lock(metrics_mutex);
return mac_metrics; return metrics;
} }
}; };

@ -17,26 +17,60 @@
class ue_dummy_stack : public srsue::stack_interface_phy_nr class ue_dummy_stack : public srsue::stack_interface_phy_nr
{ {
public:
struct prach_metrics_t {
uint32_t count;
};
struct metrics_t {
std::map<uint32_t, prach_metrics_t> prach = {}; ///< PRACH metrics indexed with premable index
};
private: private:
srsran_random_t random_gen = srsran_random_init(0x4567); srsran_random_t random_gen = srsran_random_init(0x4567);
uint16_t rnti = 0; uint16_t rnti = 0;
bool valid = false; bool valid = false;
uint32_t sr_period = 0; uint32_t sr_period = 0;
uint32_t sr_count = 0; uint32_t sr_count = 0;
uint32_t prach_period = 0;
uint32_t prach_preamble = 0;
metrics_t metrics = {};
srsue::phy_interface_stack_nr& phy;
srsran::circular_array<dummy_tx_harq_proc, SRSRAN_MAX_HARQ_PROC_DL_NR> tx_harq_proc; srsran::circular_array<dummy_tx_harq_proc, SRSRAN_MAX_HARQ_PROC_DL_NR> tx_harq_proc;
srsran::circular_array<dummy_rx_harq_proc, SRSRAN_MAX_HARQ_PROC_DL_NR> rx_harq_proc; srsran::circular_array<dummy_rx_harq_proc, SRSRAN_MAX_HARQ_PROC_DL_NR> rx_harq_proc;
public: public:
struct args_t { struct args_t {
uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions
uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable. uint32_t sr_period = 0; ///< Indicates positive SR period in number of opportunities. Set to 0 to disable.
uint32_t prach_period = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable.
uint32_t prach_preamble = 0; ///< Requests PHY to transmit PRACH periodically in frames. Set to 0 to disable.
}; };
ue_dummy_stack(const args_t& args) : rnti(args.rnti), sr_period(args.sr_period) { valid = true; } ue_dummy_stack(const args_t& args, srsue::phy_interface_stack_nr& phy_) :
rnti(args.rnti),
sr_period(args.sr_period),
prach_period(args.prach_period),
prach_preamble(args.prach_preamble),
phy(phy_)
{
valid = true;
}
~ue_dummy_stack() { srsran_random_free(random_gen); } ~ue_dummy_stack() { srsran_random_free(random_gen); }
void in_sync() override {} void in_sync() override {}
void out_of_sync() override {} void out_of_sync() override {}
void run_tti(const uint32_t tti) override {} void run_tti(const uint32_t tti) override
{
// Run PRACH
if (prach_period != 0) {
uint32_t slot_idx = tti % SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz);
uint32_t sfn = tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz);
if (slot_idx == 0 and sfn % prach_period == 0) {
phy.send_prach(0, prach_preamble, 0.0f, 0.0f);
metrics.prach[prach_preamble].count++;
}
}
}
int sf_indication(const uint32_t tti) override { return 0; } int sf_indication(const uint32_t tti) override { return 0; }
sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { return {rnti, srsran_rnti_type_c}; } sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { return {rnti, srsran_rnti_type_c}; }
sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override { return {rnti, srsran_rnti_type_c}; } sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override { return {rnti, srsran_rnti_type_c}; }
@ -70,6 +104,8 @@ public:
return ret; return ret;
} }
bool is_valid() const { return valid; } bool is_valid() const { return valid; }
metrics_t get_metrics() { return metrics; }
}; };
#endif // SRSRAN_DUMMY_UE_STACK_H #endif // SRSRAN_DUMMY_UE_STACK_H

@ -72,7 +72,9 @@ test_bench::args_t::args_t(int argc, char** argv)
; ;
options_ue_stack.add_options() options_ue_stack.add_options()
("ue.stack.sr.period", bpo::value<uint32_t>(&ue_stack.sr_period)->default_value(ue_stack.sr_period), "SR period in number of opportunities. Set 0 to disable and 1 for all.") ("ue.stack.sr.period", bpo::value<uint32_t>(&ue_stack.sr_period)->default_value(ue_stack.sr_period), "SR period in number of opportunities. Set 0 to disable and 1 for all.")
("ue.stack.prach.period", bpo::value<uint32_t>(&ue_stack.prach_period)->default_value(ue_stack.prach_period), "PRACH period in SFN. Set 0 to disable and 1 for all.")
("ue.stack.prach.preamble", bpo::value<uint32_t>(&ue_stack.prach_preamble)->default_value(ue_stack.prach_preamble), "PRACH preamble. Set 0 to disable and 1 for all.")
; ;
options.add(options_gnb_stack).add(options_gnb_phy).add(options_ue_stack).add(options_ue_phy).add_options() options.add(options_gnb_stack).add(options_gnb_phy).add(options_ue_stack).add(options_ue_phy).add_options()
@ -153,18 +155,18 @@ int main(int argc, char** argv)
srslog::flush(); srslog::flush();
// Retrieve MAC metrics // Retrieve MAC metrics
srsenb::mac_ue_metrics_t mac_metrics = tb.get_gnb_metrics(); test_bench::metrics_t metrics = tb.get_gnb_metrics();
// Print PDSCH metrics if scheduled // Print PDSCH metrics if scheduled
if (mac_metrics.tx_pkts > 0) { if (metrics.gnb_stack.mac.tx_pkts > 0) {
float pdsch_bler = 0.0f; float pdsch_bler = 0.0f;
pdsch_bler = (float)mac_metrics.tx_errors / (float)mac_metrics.tx_pkts; pdsch_bler = (float)metrics.gnb_stack.mac.tx_errors / (float)metrics.gnb_stack.mac.tx_pkts;
float pdsch_shed_rate = 0.0f; float pdsch_shed_rate = 0.0f;
pdsch_shed_rate = (float)mac_metrics.tx_brate / (float)mac_metrics.tx_pkts / 1000.0f; pdsch_shed_rate = (float)metrics.gnb_stack.mac.tx_brate / (float)metrics.gnb_stack.mac.tx_pkts / 1000.0f;
srsran::console("PDSCH:\n"); srsran::console("PDSCH:\n");
srsran::console(" Count: %d\n", mac_metrics.tx_pkts); srsran::console(" Count: %d\n", metrics.gnb_stack.mac.tx_pkts);
srsran::console(" BLER: %f\n", pdsch_bler); srsran::console(" BLER: %f\n", pdsch_bler);
srsran::console(" Sched Rate: %f Mbps\n", pdsch_shed_rate); srsran::console(" Sched Rate: %f Mbps\n", pdsch_shed_rate);
srsran::console(" Net Rate: %f Mbps\n", (1.0f - pdsch_bler) * pdsch_shed_rate); srsran::console(" Net Rate: %f Mbps\n", (1.0f - pdsch_bler) * pdsch_shed_rate);
@ -173,19 +175,19 @@ int main(int argc, char** argv)
} }
// Print PUSCH metrics if scheduled // Print PUSCH metrics if scheduled
if (mac_metrics.rx_pkts > 0) { if (metrics.gnb_stack.mac.rx_pkts > 0) {
float pusch_bler = 0.0f; float pusch_bler = 0.0f;
if (mac_metrics.rx_pkts != 0) { if (metrics.gnb_stack.mac.rx_pkts != 0) {
pusch_bler = (float)mac_metrics.rx_errors / (float)mac_metrics.rx_pkts; pusch_bler = (float)metrics.gnb_stack.mac.rx_errors / (float)metrics.gnb_stack.mac.rx_pkts;
} }
float pusch_shed_rate = 0.0f; float pusch_shed_rate = 0.0f;
if (mac_metrics.rx_pkts != 0) { if (metrics.gnb_stack.mac.rx_pkts != 0) {
pusch_shed_rate = (float)mac_metrics.rx_brate / (float)mac_metrics.rx_pkts / 1000.0f; pusch_shed_rate = (float)metrics.gnb_stack.mac.rx_brate / (float)metrics.gnb_stack.mac.rx_pkts / 1000.0f;
} }
srsran::console("PUSCH:\n"); srsran::console("PUSCH:\n");
srsran::console(" Count: %d\n", mac_metrics.rx_pkts); srsran::console(" Count: %d\n", metrics.gnb_stack.mac.rx_pkts);
srsran::console(" BLER: %f\n", pusch_bler); srsran::console(" BLER: %f\n", pusch_bler);
srsran::console(" Sched Rate: %f Mbps\n", pusch_shed_rate); srsran::console(" Sched Rate: %f Mbps\n", pusch_shed_rate);
srsran::console(" Net Rate: %f Mbps\n", (1.0f - pusch_bler) * pusch_shed_rate); srsran::console(" Net Rate: %f Mbps\n", (1.0f - pusch_bler) * pusch_shed_rate);
@ -193,9 +195,43 @@ int main(int argc, char** argv)
srsran::console("\n"); srsran::console("\n");
} }
// Print PRACH
if (metrics.ue_stack.prach.size() > 0) {
srsran::console("PRACH:\n");
srsran::console(" UE transmitted:\n");
srsran::console(" +------------+------------+\n");
srsran::console(" | %10s | %10s |\n", "preamble", "count");
srsran::console(" +------------+------------+\n");
for (const auto& p : metrics.ue_stack.prach) {
srsran::console(" | %10d | %10d |\n", p.first, p.second.count);
// Ensure the detected count matches with transmission
TESTASSERT(metrics.gnb_stack.prach.count(p.first));
TESTASSERT(metrics.gnb_stack.prach[p.first].count == p.second.count);
}
srsran::console(" +------------+------------+\n\n");
srsran::console(" GNB detected:\n");
srsran::console(" +------------+------------+------------+\n");
srsran::console(" | %10s | %10s | %10s |\n", "preamble", "count", "avg TA");
srsran::console(" +------------+------------+------------+\n");
for (const auto& p : metrics.gnb_stack.prach) {
srsran::console(" | %10d | %10d | %10.1f |\n", p.first, p.second.count, p.second.avg_ta);
// Ensure all detected preambles were transmitted
TESTASSERT(metrics.ue_stack.prach.count(p.first) > 0);
}
srsran::console(" +------------+------------+------------+\n\n");
} else {
// In this case no PRACH should
TESTASSERT(metrics.gnb_stack.prach.empty());
}
// Assert metrics // Assert metrics
TESTASSERT(mac_metrics.tx_errors == 0); TESTASSERT(metrics.gnb_stack.mac.tx_errors == 0);
TESTASSERT(mac_metrics.rx_errors == 0); TESTASSERT(metrics.gnb_stack.mac.rx_errors == 0);
// If reached here, the test is successful // If reached here, the test is successful
return SRSRAN_SUCCESS; return SRSRAN_SUCCESS;

@ -24,7 +24,7 @@ private:
const std::string GNB_PHY_COM_LOG_NAME = "GNB/PHY/COM"; const std::string GNB_PHY_COM_LOG_NAME = "GNB/PHY/COM";
uint32_t slot_idx = 0; uint32_t slot_idx = 0;
uint64_t slot_count = 0; uint64_t slot_count = 0;
uint64_t duration_slots; uint64_t duration_slots = 0;
gnb_dummy_stack gnb_stack; gnb_dummy_stack gnb_stack;
srsenb::nr::worker_pool gnb_phy; srsenb::nr::worker_pool gnb_phy;
phy_common gnb_phy_com; phy_common gnb_phy_com;
@ -53,10 +53,15 @@ public:
args_t(int argc, char** argv); args_t(int argc, char** argv);
}; };
struct metrics_t {
gnb_dummy_stack::metrics_t gnb_stack = {};
ue_dummy_stack::metrics_t ue_stack = {};
};
test_bench(const args_t& args) : test_bench(const args_t& args) :
gnb_stack(args.gnb_stack), gnb_stack(args.gnb_stack),
gnb_phy(gnb_phy_com, gnb_stack, srslog::get_default_sink(), args.gnb_phy.nof_phy_threads), gnb_phy(gnb_phy_com, gnb_stack, srslog::get_default_sink(), args.gnb_phy.nof_phy_threads),
ue_stack(args.ue_stack), ue_stack(args.ue_stack, ue_phy),
ue_phy(args.ue_phy.nof_phy_threads), ue_phy(args.ue_phy.nof_phy_threads),
ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels),
@ -73,6 +78,15 @@ public:
return; return;
} }
srsenb::phy_interface_rrc_nr::common_cfg_t common_cfg = {};
common_cfg.carrier = args.phy_cfg.carrier;
common_cfg.pdcch = args.phy_cfg.pdcch;
common_cfg.prach = args.phy_cfg.prach;
if (gnb_phy.set_common_cfg(common_cfg) < SRSRAN_SUCCESS) {
return;
}
// Initialise UE PHY // Initialise UE PHY
if (not ue_phy.init(args.ue_phy, ue_phy_com, &ue_stack, 31)) { if (not ue_phy.init(args.ue_phy, ue_phy_com, &ue_stack, 31)) {
return; return;
@ -147,7 +161,10 @@ public:
ue_worker->set_tti(slot_idx); ue_worker->set_tti(slot_idx);
ue_worker->set_tx_time(ue_time); ue_worker->set_tx_time(ue_time);
// Start gNb work // Run UE stack
ue_stack.run_tti(slot_idx);
// Start UE work
ue_phy_com.push_semaphore(ue_worker); ue_phy_com.push_semaphore(ue_worker);
ue_phy.start_worker(ue_worker); ue_phy.start_worker(ue_worker);
@ -156,7 +173,13 @@ public:
return slot_count <= duration_slots; return slot_count <= duration_slots;
} }
srsenb::mac_ue_metrics_t get_gnb_metrics() { return gnb_stack.get_metrics(); } metrics_t get_gnb_metrics()
{
metrics_t metrics = {};
metrics.gnb_stack = gnb_stack.get_metrics();
metrics.ue_stack = ue_stack.get_metrics();
return metrics;
}
}; };
#endif // SRSRAN_TEST_BENCH_H #endif // SRSRAN_TEST_BENCH_H

Loading…
Cancel
Save