SRSENB: Refactor to accomodate 5G NR

master
Xavier Arteaga 3 years ago committed by Xavier Arteaga
parent e98a6f9eea
commit d49734b1bc

@ -224,15 +224,40 @@ public:
/**
* DL Scheduling result per cell/carrier
*/
typedef struct {
struct dl_sched_t {
dl_sched_grant_t pdsch[MAX_GRANTS]; //< DL Grants
uint32_t nof_grants; //< Number of DL grants
} dl_sched_t;
};
/**
* List of DL scheduling results, one entry per cell/carrier
*/
typedef std::vector<dl_sched_t> dl_sched_list_t;
/**
* UL grant structure per UE
*/
struct ul_sched_grant_t {
srsran_dci_ul_nr_t dci = {};
uint8_t* data = nullptr;
srsran_softbuffer_rx_t* softbuffer_rx = nullptr;
};
/**
* UL Scheduling result per cell/carrier
*/
struct ul_sched_t {
ul_sched_grant_t pusch[MAX_GRANTS]; //< UL Grants
uint32_t nof_grants; //< Number of UL grants
};
/**
* List of UL scheduling results, one entry per cell/carrier
*/
typedef std::vector<ul_sched_t> ul_sched_list_t;
virtual int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) = 0;
virtual int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) = 0;
};
class stack_interface_phy_nr : public mac_interface_phy_nr, public srsran::stack_interface_phy_nr

@ -13,6 +13,7 @@
#ifndef SRSENB_NR_CC_WORKER_H
#define SRSENB_NR_CC_WORKER_H
#include "phy_nr_state.h"
#include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsran/interfaces/gnb_interfaces.h"
#include "srsran/interfaces/rrc_nr_interface_types.h"
@ -25,56 +26,40 @@
namespace srsenb {
namespace nr {
struct phy_nr_args_t {
uint32_t nof_carriers = 1;
uint32_t nof_ports = 1;
srsran_enb_dl_nr_args_t dl = {};
};
class phy_nr_state
{
public:
phy_cell_cfg_list_nr_t cell_list = {};
phy_nr_args_t args;
srsran::phy_cfg_nr_t cfg;
phy_nr_state()
{
args.nof_carriers = 1;
args.dl.nof_max_prb = 100;
args.dl.nof_tx_antennas = 1;
args.dl.pdsch.measure_evm = true;
args.dl.pdsch.measure_time = true;
args.dl.pdsch.sch.disable_simd = false;
}
};
class cc_worker
{
public:
cc_worker(uint32_t cc_idx, srslog::basic_logger& logger, phy_nr_state& phy_state_);
struct args_t {
uint32_t cc_idx = 0;
srsran_carrier_nr_t carrier = {};
srsran_enb_dl_nr_args_t dl = {};
};
cc_worker(const args_t& args, srslog::basic_logger& logger, phy_nr_state& phy_state_);
~cc_worker();
bool set_carrier(const srsran_carrier_nr_t* carrier);
void set_tti(uint32_t tti);
cf_t* get_tx_buffer(uint32_t antenna_idx);
cf_t* get_rx_buffer(uint32_t antenna_idx);
uint32_t get_buffer_len();
bool work_dl(const srsran_slot_cfg_t& dl_slot_cfg, stack_interface_phy_nr::dl_sched_t& dl_grants);
bool work_dl(stack_interface_phy_nr::dl_sched_t& dl_grants, stack_interface_phy_nr::ul_sched_t& ul_grants);
bool work_ul();
private:
int encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants);
int encode_pdcch_dl(stack_interface_phy_nr::dl_sched_grant_t* grants, uint32_t nof_grants);
srsran_slot_cfg_t dl_slot_cfg = {};
uint32_t cc_idx = 0;
std::array<cf_t*, SRSRAN_MAX_PORTS> tx_buffer = {};
std::array<cf_t*, SRSRAN_MAX_PORTS> rx_buffer = {};
uint32_t buffer_sz = 0;
uint32_t nof_tx_antennas = 0;
srsran_slot_cfg_t dl_slot_cfg = {};
srsran_slot_cfg_t ul_slot_cfg = {};
uint32_t cc_idx = 0;
std::array<cf_t*, SRSRAN_MAX_PORTS> tx_buffer = {};
std::array<cf_t*, SRSRAN_MAX_PORTS> rx_buffer = {};
uint32_t buffer_sz = 0;
phy_nr_state& phy_state;
srsran_enb_dl_nr_t enb_dl = {};
srsran_enb_dl_nr_t gnb_dl = {};
srslog::basic_logger& logger;
};

@ -0,0 +1,101 @@
/**
*
* \section COPYRIGHT
*
* Copyright 2013-2020 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 SRSENB_PHY_NR_UE_DB_H_
#define SRSENB_PHY_NR_UE_DB_H_
#include <map>
#include <mutex>
#include <srsenb/hdr/phy/phy_interfaces.h>
#include <srsran/adt/circular_array.h>
#include <srsran/interfaces/gnb_interfaces.h>
#include <srsran/interfaces/rrc_nr_interface_types.h>
namespace srsenb {
namespace nr {
class phy_nr_state
{
private:
/**
* UE object stored in the PHY common database
*/
struct common_ue {
srsran::phy_cfg_nr_t cfg;
};
/**
* UE database indexed by RNTI
*/
std::map<uint16_t, common_ue> ue_db;
/**
* Concurrency protection mutex, allowed modifications from const methods.
*/
mutable std::mutex mutex;
/**
* Stack interface
*/
stack_interface_phy_nr& stack;
/**
* Cell list
*/
const phy_cell_cfg_list_nr_t& cell_cfg_list;
/**
* Internal RNTI addition, it is not thread safe protected
*
* @param rnti identifier of the UE
* @return SRSRAN_SUCCESS if the RNTI is not duplicated and is added successfully, SRSRAN_ERROR code if it exists
*/
inline int _add_rnti(uint16_t rnti);
/**
* Checks if a given RNTI exists in the database
* @param rnti provides UE identifier
* @return SRSRAN_SUCCESS if the indicated RNTI exists, otherwise it returns SRSRAN_ERROR
*/
inline int _assert_rnti(uint16_t rnti) const;
/**
* Internal eNb general configuration getter, returns default configuration if the UE does not exist in the given cell
*
* @param rnti provides UE identifier
* @param[out] phy_cfg The PHY configuration of the indicated UE for the indicated eNb carrier/call index.
* @return SRSRAN_SUCCESS if provided context is correct, SRSRAN_ERROR code otherwise
*/
inline int _get_rnti_config(uint16_t rnti, srsran::phy_cfg_nr_t& phy_cfg) const;
public:
phy_nr_state(const phy_cell_cfg_list_nr_t& cell_cfg_list_, stack_interface_phy_nr& stack_);
void addmod_rnti(uint16_t rnti, const srsran::phy_cfg_nr_t& phy_cfg);
/**
* Removes a whole UE entry from the UE database
*
* @param rnti identifier of the UE
* @return SRSRAN_SUCCESS if provided RNTI exists, SRSRAN_ERROR code otherwise
*/
int rem_rnti(uint16_t rnti);
const phy_cell_cfg_list_nr_t& get_carrier_list() const { return cell_cfg_list; }
stack_interface_phy_nr& get_stack() { return stack; }
};
} // namespace nr
} // namespace srsenb
#endif // SRSENB_PHY_NR_UE_DB_H_

@ -35,13 +35,11 @@ public:
sf_worker(srsran::phy_common_interface& common_, phy_nr_state& phy_state_, srslog::basic_logger& logger);
~sf_worker();
bool set_carrier_unlocked(uint32_t cc_idx, const srsran_carrier_nr_t* carrier_);
/* Functions used by main PHY thread */
cf_t* get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx);
cf_t* get_buffer_tx(uint32_t cc_idx, uint32_t antenna_idx);
uint32_t get_buffer_len();
void set_tti(uint32_t tti);
void set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp);
private:
/* Inherited from thread_pool::worker. Function called every subframe to run the DL/UL processing */
@ -52,6 +50,9 @@ private:
srsran::phy_common_interface& common;
phy_nr_state& phy_state;
srslog::basic_logger& logger;
srsran_slot_cfg_t dl_slot_cfg = {};
srsran_slot_cfg_t ul_slot_cfg = {};
srsran::rf_timestamp_t tx_time = {};
// Temporal attributes
srsran_softbuffer_tx_t softbuffer_tx = {};

@ -13,6 +13,7 @@
#ifndef SRSENB_NR_WORKER_POOL_H
#define SRSENB_NR_WORKER_POOL_H
#include "phy_nr_state.h"
#include "sf_worker.h"
#include "srsenb/hdr/phy/phy_interfaces.h"
#include "srsran/common/thread_pool.h"
@ -27,14 +28,19 @@ class worker_pool
phy_nr_state phy_state;
public:
struct args_t {
uint32_t nof_workers = 3;
uint32_t prio = 52;
std::string log_level = "info";
uint32_t log_hex_limit = 64;
};
sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); }
worker_pool(uint32_t max_workers);
bool init(const phy_cell_cfg_list_nr_t& cell_list,
const phy_args_t& args,
srsran::phy_common_interface& common,
srslog::sink& log_sink,
int prio);
worker_pool(const phy_cell_cfg_list_nr_t& cell_list,
const args_t& args,
srsran::phy_common_interface& common,
stack_interface_phy_nr& stack,
srslog::sink& log_sink);
sf_worker* wait_worker(uint32_t tti);
sf_worker* wait_worker_id(uint32_t id);
void start_worker(sf_worker* w);

@ -77,11 +77,11 @@ private:
srslog::basic_logger& phy_log;
srslog::basic_logger& phy_lib_log;
lte::worker_pool lte_workers;
nr::worker_pool nr_workers;
phy_common workers_common;
prach_worker_pool prach;
txrx tx_rx;
lte::worker_pool lte_workers;
std::unique_ptr<nr::worker_pool> nr_workers;
phy_common workers_common;
prach_worker_pool prach;
txrx tx_rx;
bool initialized = false;

@ -72,6 +72,8 @@ public:
void process_pdus() final;
void toggle_padding() { srsran::console("padding not available for NR\n"); }
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override;
int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override;
private:
void run_thread() final;

@ -53,18 +53,20 @@ public:
void get_metrics(srsenb::mac_metrics_t& metrics);
// MAC interface for RRC
int cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg);
int cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg) override;
int read_pdu_bcch_bch(uint8_t* payload);
// MAC interface for RLC
// TODO:
int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) { return 0; }
int rlc_buffer_state(uint16_t rnti, uint32_t lc_id, uint32_t tx_queue, uint32_t retx_queue) override { return 0; }
// Interface for PHY
int sf_indication(const uint32_t tti);
int rx_data_indication(stack_interface_phy_nr::rx_data_ind_t& grant);
void process_pdus();
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override;
int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override;
private:
void get_dl_config(const uint32_t tti,

@ -13,6 +13,7 @@ set(SOURCES
nr/cc_worker.cc
nr/sf_worker.cc
nr/worker_pool.cc
nr/phy_nr_state.cc
phy.cc
phy_common.cc
phy_ue_db.cc
@ -22,6 +23,6 @@ add_library(srsenb_phy STATIC ${SOURCES})
add_library(srsgnb_phy STATIC vnf_phy_nr.cc)
if(ENABLE_GUI AND SRSGUI_FOUND)
if (ENABLE_GUI AND SRSGUI_FOUND)
target_link_libraries(srsenb_phy ${SRSGUI_LIBRARIES})
endif()
endif ()

@ -15,28 +15,32 @@
namespace srsenb {
namespace nr {
cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, phy_nr_state& phy_state_) :
cc_idx(cc_idx_), phy_state(phy_state_), logger(log)
cc_worker::cc_worker(const args_t& args, srslog::basic_logger& log, phy_nr_state& phy_state_) :
cc_idx(args.cc_idx), phy_state(phy_state_), logger(log), nof_tx_antennas(args.dl.nof_tx_antennas)
{
cf_t* buffer_c[SRSRAN_MAX_PORTS] = {};
// Allocate buffers
buffer_sz = SRSRAN_SF_LEN_PRB(phy_state.args.dl.nof_max_prb);
for (uint32_t i = 0; i < phy_state.args.dl.nof_tx_antennas; i++) {
buffer_sz = SRSRAN_SF_LEN_PRB(args.dl.nof_max_prb);
for (uint32_t i = 0; i < args.dl.nof_tx_antennas; i++) {
tx_buffer[i] = srsran_vec_cf_malloc(buffer_sz);
rx_buffer[i] = srsran_vec_cf_malloc(buffer_sz);
buffer_c[i] = tx_buffer[i];
}
if (srsran_enb_dl_nr_init(&enb_dl, buffer_c, &phy_state.args.dl)) {
ERROR("Error initiating UE DL NR");
if (srsran_enb_dl_nr_init(&gnb_dl, buffer_c, &args.dl)) {
ERROR("Error initiating GNB DL NR");
return;
}
if (srsran_enb_dl_nr_set_carrier(&gnb_dl, &args.carrier) < SRSRAN_SUCCESS) {
ERROR("Error setting carrier");
}
}
cc_worker::~cc_worker()
{
srsran_enb_dl_nr_free(&enb_dl);
srsran_enb_dl_nr_free(&gnb_dl);
for (cf_t* p : rx_buffer) {
if (p != nullptr) {
free(p);
@ -49,34 +53,15 @@ cc_worker::~cc_worker()
}
}
bool cc_worker::set_carrier(const srsran_carrier_nr_t* carrier)
{
if (srsran_enb_dl_nr_set_carrier(&enb_dl, carrier) < SRSRAN_SUCCESS) {
ERROR("Error setting carrier");
return false;
}
srsran_coreset_t coreset = {};
coreset.freq_resources[0] = true; // Enable the bottom 6 PRB for PDCCH
coreset.duration = 2;
srsran_dci_cfg_nr_t dci_cfg = phy_state.cfg.get_dci_cfg();
if (srsran_enb_dl_nr_set_pdcch_config(&enb_dl, &phy_state.cfg.pdcch, &dci_cfg) < SRSRAN_SUCCESS) {
ERROR("Error setting coreset");
return false;
}
return true;
}
void cc_worker::set_tti(uint32_t tti)
{
ul_slot_cfg.idx = tti;
dl_slot_cfg.idx = TTI_ADD(tti, FDD_HARQ_DELAY_UL_MS);
}
cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx)
{
if (antenna_idx >= phy_state.args.dl.nof_tx_antennas) {
if (antenna_idx >= nof_tx_antennas) {
return nullptr;
}
@ -85,7 +70,7 @@ cf_t* cc_worker::get_tx_buffer(uint32_t antenna_idx)
cf_t* cc_worker::get_rx_buffer(uint32_t antenna_idx)
{
if (antenna_idx >= phy_state.args.dl.nof_tx_antennas) {
if (antenna_idx >= nof_tx_antennas) {
return nullptr;
}
@ -104,7 +89,7 @@ int cc_worker::encode_pdcch_dl(stack_interface_phy_nr::dl_sched_grant_t* grants,
// ...
// Put actual DCI
if (srsran_enb_dl_nr_pdcch_put(&enb_dl, &dl_slot_cfg, &grants[i].dci) < SRSRAN_SUCCESS) {
if (srsran_enb_dl_nr_pdcch_put(&gnb_dl, &dl_slot_cfg, &grants[i].dci) < SRSRAN_SUCCESS) {
ERROR("Error putting PDCCH");
return SRSRAN_ERROR;
}
@ -127,10 +112,10 @@ int cc_worker::encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, ui
// Compute DL grant
if (srsran_ra_dl_dci_to_grant_nr(
&enb_dl.carrier, &dl_slot_cfg, &pdsch_hl_cfg, &grants[i].dci, &pdsch_cfg, &pdsch_cfg.grant) <
&gnb_dl.carrier, &dl_slot_cfg, &pdsch_hl_cfg, &grants[i].dci, &pdsch_cfg, &pdsch_cfg.grant) <
SRSRAN_SUCCESS) {
ERROR("Computing DL grant");
return false;
return SRSRAN_ERROR;
}
// Set soft buffer
@ -138,15 +123,15 @@ int cc_worker::encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, ui
pdsch_cfg.grant.tb[j].softbuffer.tx = grants[i].softbuffer_tx[j];
}
if (srsran_enb_dl_nr_pdsch_put(&enb_dl, &dl_slot_cfg, &pdsch_cfg, grants[i].data) < SRSRAN_SUCCESS) {
if (srsran_enb_dl_nr_pdsch_put(&gnb_dl, &dl_slot_cfg, &pdsch_cfg, grants[i].data) < SRSRAN_SUCCESS) {
ERROR("Error putting PDSCH");
return false;
return SRSRAN_ERROR;
}
// Logging
if (logger.info.enabled()) {
char str[512];
srsran_enb_dl_nr_pdsch_info(&enb_dl, &pdsch_cfg, str, sizeof(str));
srsran_enb_dl_nr_pdsch_info(&gnb_dl, &pdsch_cfg, str, sizeof(str));
logger.info("PDSCH: cc=%d, %s", cc_idx, str);
}
}
@ -154,10 +139,15 @@ int cc_worker::encode_pdsch(stack_interface_phy_nr::dl_sched_grant_t* grants, ui
return SRSRAN_SUCCESS;
}
bool cc_worker::work_dl(const srsran_slot_cfg_t& dl_sf_cfg, stack_interface_phy_nr::dl_sched_t& dl_grants)
bool cc_worker::work_ul()
{
return true;
}
bool cc_worker::work_dl(stack_interface_phy_nr::dl_sched_t& dl_grants, stack_interface_phy_nr::ul_sched_t& ul_grants)
{
// Reset resource grid
if (srsran_enb_dl_nr_base_zero(&enb_dl) < SRSRAN_SUCCESS) {
if (srsran_enb_dl_nr_base_zero(&gnb_dl) < SRSRAN_SUCCESS) {
ERROR("Error setting base to zero");
return SRSRAN_ERROR;
}
@ -167,7 +157,7 @@ bool cc_worker::work_dl(const srsran_slot_cfg_t& dl_sf_cfg, stack_interface_phy_
encode_pdsch(dl_grants.pdsch, dl_grants.nof_grants);
// Generate signal
srsran_enb_dl_nr_gen_signal(&enb_dl);
srsran_enb_dl_nr_gen_signal(&gnb_dl);
return true;
}

@ -0,0 +1,79 @@
/**
*
* \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 "srsenb/hdr/phy/nr/phy_nr_state.h"
namespace srsenb {
namespace nr {
int phy_nr_state::_add_rnti(uint16_t rnti)
{
if (ue_db.count(rnti) > 0) {
return SRSRAN_ERROR;
}
// Access UE to create
// Set defaults
ue_db[rnti] = {};
return SRSRAN_SUCCESS;
}
int phy_nr_state::_assert_rnti(uint16_t rnti) const
{
return ue_db.count(rnti) > 0 ? SRSRAN_SUCCESS : SRSRAN_ERROR;
}
int phy_nr_state::_get_rnti_config(uint16_t rnti, srsran::phy_cfg_nr_t& phy_cfg) const
{
std::lock_guard<std::mutex> lock(mutex);
if (_assert_rnti(rnti) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
phy_cfg = ue_db.at(rnti).cfg;
return SRSRAN_SUCCESS;
}
phy_nr_state::phy_nr_state(const phy_cell_cfg_list_nr_t& cell_cfg_list_, stack_interface_phy_nr& stack_) :
cell_cfg_list(cell_cfg_list_), stack(stack_)
{}
void phy_nr_state::addmod_rnti(uint16_t rnti, const srsran::phy_cfg_nr_t& phy_cfg)
{
std::lock_guard<std::mutex> lock(mutex);
// Create UE if it does not exist
if (_assert_rnti(rnti) < SRSRAN_SUCCESS) {
_add_rnti(rnti);
}
// Set UE configuration
ue_db[rnti].cfg = phy_cfg;
}
int phy_nr_state::rem_rnti(uint16_t rnti)
{
std::lock_guard<std::mutex> lock(mutex);
if (_assert_rnti(rnti) < SRSRAN_SUCCESS) {
return SRSRAN_ERROR;
}
ue_db.erase(rnti);
return SRSRAN_SUCCESS;
}
} // namespace nr
} // namespace srsenb

@ -17,8 +17,15 @@ namespace nr {
sf_worker::sf_worker(srsran::phy_common_interface& common_, phy_nr_state& phy_state_, srslog::basic_logger& logger) :
common(common_), phy_state(phy_state_), logger(logger)
{
for (uint32_t i = 0; i < phy_state.args.nof_carriers; i++) {
cc_worker* w = new cc_worker(i, logger, phy_state);
const phy_cell_cfg_list_nr_t& carrier_list = phy_state.get_carrier_list();
for (uint32_t i = 0; i < (uint32_t)carrier_list.size(); i++) {
cc_worker::args_t cc_args = {};
cc_args.cc_idx = i;
cc_args.carrier = carrier_list[i].carrier;
cc_args.dl.nof_tx_antennas = 1;
cc_args.dl.nof_max_prb = cc_args.carrier.nof_prb;
cc_worker* w = new cc_worker(cc_args, logger, phy_state);
cc_workers.push_back(std::unique_ptr<cc_worker>(w));
}
@ -37,15 +44,6 @@ sf_worker::~sf_worker()
srsran_softbuffer_tx_free(&softbuffer_tx);
}
bool sf_worker::set_carrier_unlocked(uint32_t cc_idx, const srsran_carrier_nr_t* carrier_)
{
if (cc_idx >= cc_workers.size()) {
return false;
}
return cc_workers.at(cc_idx)->set_carrier(carrier_);
}
cf_t* sf_worker::get_buffer_rx(uint32_t cc_idx, uint32_t antenna_idx)
{
if (cc_idx >= cc_workers.size()) {
@ -69,12 +67,15 @@ uint32_t sf_worker::get_buffer_len()
return cc_workers.at(0)->get_buffer_len();
}
void sf_worker::set_tti(uint32_t tti)
void sf_worker::set_time(const uint32_t& tti, const srsran::rf_timestamp_t& timestamp)
{
logger.set_context(tti);
for (auto& w : cc_workers) {
w->set_tti(tti);
}
ul_slot_cfg.idx = tti;
dl_slot_cfg.idx = TTI_ADD(tti, FDD_HARQ_DELAY_UL_MS);
tx_time.copy(timestamp);
}
void sf_worker::work_imp()
@ -82,38 +83,30 @@ void sf_worker::work_imp()
// Get Transmission buffers
srsran::rf_buffer_t tx_buffer = {};
srsran::rf_timestamp_t dummy_ts = {};
for (uint32_t cc = 0; cc < phy_state.args.nof_carriers; cc++) {
for (uint32_t ant = 0; ant < phy_state.args.nof_ports; ant++) {
tx_buffer.set(cc, ant, phy_state.args.nof_ports, cc_workers[cc]->get_tx_buffer(ant));
}
for (uint32_t cc = 0; cc < (uint32_t)phy_state.get_carrier_list().size(); cc++) {
tx_buffer.set(cc, 0, 1, cc_workers[cc]->get_tx_buffer(0));
}
// Configure user
phy_state.cfg.pdsch.rbg_size_cfg_1 = false;
// Fill grant (this comes from the scheduler)
srsran_slot_cfg_t dl_cfg = {};
stack_interface_phy_nr::dl_sched_t grants = {};
grants.nof_grants = 1;
grants.pdsch[0].data[0] = data.data();
grants.pdsch[0].softbuffer_tx[0] = &softbuffer_tx;
srsran_softbuffer_tx_reset(&softbuffer_tx);
grants.pdsch[0].dci.ctx.rnti = 0x1234;
grants.pdsch[0].dci.ctx.format = srsran_dci_format_nr_1_0;
grants.pdsch[0].dci.freq_domain_assigment = 0x1FFF;
grants.pdsch[0].dci.time_domain_assigment = 0;
grants.pdsch[0].dci.mcs = 27;
// Get UL Scheduling
mac_interface_phy_nr::ul_sched_list_t ul_sched_list = {};
ul_sched_list.resize(1);
if (phy_state.get_stack().get_ul_sched(ul_slot_cfg.idx, ul_sched_list) < SRSRAN_SUCCESS) {
logger.error("DL Scheduling error");
common.worker_end(this, true, tx_buffer, dummy_ts, true);
return;
}
grants.pdsch[0].dci.ctx.ss_type = srsran_search_space_type_ue;
grants.pdsch[0].dci.ctx.coreset_id = 1;
grants.pdsch[0].dci.ctx.location.L = 0;
grants.pdsch[0].dci.ctx.location.ncce = 0;
// Get DL scheduling
mac_interface_phy_nr::dl_sched_list_t dl_sched_list = {};
dl_sched_list.resize(1);
if (phy_state.get_stack().get_dl_sched(ul_slot_cfg.idx, dl_sched_list) < SRSRAN_SUCCESS) {
logger.error("DL Scheduling error");
common.worker_end(this, true, tx_buffer, dummy_ts, true);
return;
}
for (auto& w : cc_workers) {
w->work_dl(dl_cfg, grants);
w->work_dl(dl_sched_list[0], ul_sched_list[0]);
}
common.worker_end(this, true, tx_buffer, dummy_ts, true);

@ -14,33 +14,24 @@
namespace srsenb {
namespace nr {
worker_pool::worker_pool(uint32_t max_workers) : pool(max_workers) {}
bool worker_pool::init(const phy_cell_cfg_list_nr_t& cell_list,
const phy_args_t& args,
srsran::phy_common_interface& common,
srslog::sink& log_sink,
int prio)
worker_pool::worker_pool(const phy_cell_cfg_list_nr_t& cell_list,
const args_t& args,
srsran::phy_common_interface& common,
stack_interface_phy_nr& stack,
srslog::sink& log_sink) :
pool(args.nof_workers), phy_state(cell_list, stack)
{
// Save cell list
phy_state.cell_list = cell_list;
// Add workers to workers pool and start threads
srslog::basic_levels log_level = srslog::str_to_basic_level(args.log.phy_level);
for (uint32_t i = 0; i < args.nof_phy_threads; i++) {
srslog::basic_levels log_level = srslog::str_to_basic_level(args.log_level);
for (uint32_t i = 0; i < args.nof_workers; i++) {
auto& log = srslog::fetch_basic_logger(fmt::format("PHY{}-NR", i), log_sink);
log.set_level(log_level);
log.set_hex_dump_max_size(args.log.phy_hex_limit);
log.set_hex_dump_max_size(args.log_hex_limit);
auto w = new sf_worker(common, phy_state, log);
pool.init_worker(i, w, prio);
pool.init_worker(i, w, args.prio);
workers.push_back(std::unique_ptr<sf_worker>(w));
srsran_carrier_nr_t c = phy_state.cell_list[0].carrier;
w->set_carrier_unlocked(0, &c);
}
return true;
}
void worker_pool::start_worker(sf_worker* w)

@ -66,7 +66,6 @@ phy::phy(srslog::sink& log_sink) :
phy_log(srslog::fetch_basic_logger("PHY", log_sink)),
phy_lib_log(srslog::fetch_basic_logger("PHY_LIB", log_sink)),
lte_workers(MAX_WORKERS),
nr_workers(MAX_WORKERS),
workers_common(),
nof_workers(0),
tx_rx(phy_log)
@ -132,7 +131,10 @@ int phy::init(const phy_args_t& args,
lte_workers.init(args, &workers_common, log_sink, WORKERS_THREAD_PRIO);
}
if (not cfg.phy_cell_cfg_nr.empty()) {
nr_workers.init(cfg.phy_cell_cfg_nr, args, workers_common, log_sink, WORKERS_THREAD_PRIO);
// Not implemented
// nr_workers = std::unique_ptr<nr::worker_pool>(
// new nr::worker_pool(cfg.phy_cell_cfg_nr, workers_common, stack_, log_sink, MAX_WORKERS,
// WORKERS_THREAD_PRIO));
}
// For each carrier, initialise PRACH worker
@ -144,7 +146,7 @@ int phy::init(const phy_args_t& args,
prach.set_max_prach_offset_us(args.max_prach_offset_us);
// Warning this must be initialized after all workers have been added to the pool
tx_rx.init(stack_, radio, &lte_workers, &nr_workers, &workers_common, &prach, SF_RECV_THREAD_PRIO);
tx_rx.init(stack_, radio, &lte_workers, nr_workers.get(), &workers_common, &prach, SF_RECV_THREAD_PRIO);
initialized = true;
@ -157,7 +159,7 @@ void phy::stop()
tx_rx.stop();
workers_common.stop();
lte_workers.stop();
nr_workers.stop();
nr_workers->stop();
prach.stop();
initialized = false;

@ -47,13 +47,13 @@ bool txrx::init(stack_interface_phy_lte* stack_,
prach_worker_pool* prach_,
uint32_t prio_)
{
stack = stack_;
radio_h = radio_h_;
lte_workers = lte_workers_;
nr_workers = nr_workers_;
worker_com = worker_com_;
prach = prach_;
running = true;
stack = stack_;
radio_h = radio_h_;
lte_workers = lte_workers_;
nr_workers = nr_workers_;
worker_com = worker_com_;
prach = prach_;
running = true;
// Instantiate UL channel emulator
if (worker_com->params.ul_channel_args.enable) {
@ -179,7 +179,7 @@ void txrx::run_thread()
// Launch NR worker only if available
if (nr_worker != nullptr) {
nr_worker->set_tti(tti);
nr_worker->set_time(tti, timestamp);
worker_com->semaphore.push(nr_worker);
nr_workers->start_worker(nr_worker);
}

@ -182,5 +182,13 @@ bool gnb_stack_nr::has_active_radio_bearer(uint32_t eps_bearer_id)
{
return (eps_bearer_id == args.coreless.drb_lcid);
}
int gnb_stack_nr::get_dl_sched(uint32_t tti, mac_interface_phy_nr::dl_sched_list_t& dl_sched_res)
{
return m_mac->get_dl_sched(tti, dl_sched_res);
}
int gnb_stack_nr::get_ul_sched(uint32_t tti, mac_interface_phy_nr::ul_sched_list_t& ul_sched_res)
{
return m_mac->get_ul_sched(tti, ul_sched_res);
}
} // namespace srsenb

@ -267,5 +267,13 @@ int mac_nr::cell_cfg(srsenb::sched_interface::cell_cfg_t* cell_cfg)
return SRSRAN_SUCCESS;
}
int mac_nr::get_dl_sched(uint32_t tti, mac_interface_phy_nr::dl_sched_list_t& dl_sched_res)
{
return 0;
}
int mac_nr::get_ul_sched(uint32_t tti, mac_interface_phy_nr::ul_sched_list_t& ul_sched_res)
{
return 0;
}
} // namespace srsenb

@ -40,6 +40,7 @@ public:
cf_t* get_buffer(uint32_t cc_idx, uint32_t antenna_idx);
uint32_t get_buffer_len();
void set_tti(uint32_t tti);
void set_tx_time(const srsran::rf_timestamp_t& tx_time_);
int read_pdsch_d(cf_t* pdsch_d);
void start_plot();
@ -54,10 +55,10 @@ private:
srsran::phy_common_interface& common;
state& phy_state;
srslog::basic_logger& logger;
uint32_t tti_rx = 0;
cf_t* prach_ptr = nullptr;
float prach_power = 0;
srsran::rf_timestamp_t tx_time = {};
uint32_t tti_rx = 0;
cf_t* prach_ptr = nullptr;
float prach_power = 0;
};
} // namespace nr

@ -68,10 +68,14 @@ void sf_worker::set_tti(uint32_t tti)
}
}
void sf_worker::set_tx_time(const srsran::rf_timestamp_t& tx_time_)
{
tx_time.copy(tx_time_);
}
void sf_worker::work_imp()
{
srsran::rf_buffer_t tx_buffer = {};
srsran::rf_timestamp_t dummy_ts = {};
srsran::rf_buffer_t tx_buffer = {};
// Perform DL processing
for (auto& w : cc_workers) {
@ -95,7 +99,7 @@ void sf_worker::work_imp()
0);
// Transmit NR PRACH
common.worker_end(this, true, tx_buffer, dummy_ts, true);
common.worker_end(this, true, tx_buffer, tx_time, true);
// Reset PRACH pointer
prach_ptr = nullptr;
@ -114,7 +118,7 @@ void sf_worker::work_imp()
}
// Always call worker_end before returning
common.worker_end(this, true, tx_buffer, dummy_ts, true);
common.worker_end(this, true, tx_buffer, tx_time, true);
// Tell the plotting thread to draw the plots
#ifdef ENABLE_GUI

@ -7,7 +7,7 @@
#
if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
add_executable(nr_phy_test nr_phy_test.cc)
add_executable(nr_phy_test nr_dl_flood.cc)
target_link_libraries(nr_phy_test
srsue_phy_nr
srsue_phy
@ -22,4 +22,4 @@ if (RF_FOUND AND ENABLE_SRSUE AND ENABLE_SRSENB)
${ATOMIC_LIBS})
add_nr_test(nr_phy_test nr_phy_test)
endif ()
endif ()

@ -0,0 +1,233 @@
/**
*
* \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_DUMMY_PHY_COMMON_H
#define SRSRAN_DUMMY_PHY_COMMON_H
#include <srsran/common/tti_sempahore.h>
#include <srsran/interfaces/phy_common_interface.h>
#include <srsran/srslog/srslog.h>
#include <srsran/srsran.h>
#include <vector>
class phy_common : public srsran::phy_common_interface
{
private:
const uint32_t RINGBUFFER_TIMEOUT_MS = 10;
bool quit = false;
srslog::basic_logger& logger;
double srate_hz;
uint64_t write_ts = 0;
uint64_t read_ts = 0;
std::vector<cf_t> zero_buffer; ///< Zero buffer for Tx
std::vector<cf_t> sink_buffer; ///< Dummy buffer for Rx
std::vector<srsran_ringbuffer_t> ringbuffers;
srsran::tti_semaphore<void*> semaphore;
void write_zero_padding(uint32_t nof_zeros)
{
// Skip if no pading is required
if (nof_zeros == 0) {
return;
}
logger.debug("Padding %d zeros", nof_zeros);
// For each ringbuffer, padd zero
int nof_bytes = (int)(nof_zeros * sizeof(cf_t));
for (srsran_ringbuffer_t& rb : ringbuffers) {
// If quit is flagged, return instantly
if (quit) {
return;
}
// Actual write
int err = SRSRAN_SUCCESS;
do {
err = srsran_ringbuffer_write_timed(&rb, zero_buffer.data(), nof_bytes, RINGBUFFER_TIMEOUT_MS);
if (err < SRSRAN_SUCCESS and err != SRSRAN_ERROR_TIMEOUT) {
logger.error("Error writing zeros in ringbuffer");
}
} while (err < SRSRAN_SUCCESS and not quit);
}
// Increment write timestamp
write_ts += nof_zeros;
}
void write_baseband(srsran::rf_buffer_t& buffer)
{
// skip if baseband is not available
if (buffer.get_nof_samples() == 0) {
return;
}
// For each ringbuffer, write
int nof_bytes = (int)(buffer.get_nof_samples() * sizeof(cf_t));
uint32_t channel_idx = 0;
for (srsran_ringbuffer_t& rb : ringbuffers) {
// If quit is flagged, return instantly
if (quit) {
return;
}
// Extract channel buffer pointer
cf_t* channel_buffer = buffer.get(channel_idx);
// If the pointer is not set, use the zero buffer
if (channel_buffer == nullptr) {
channel_buffer = zero_buffer.data();
}
// Actual write
int err = SRSRAN_SUCCESS;
do {
err = srsran_ringbuffer_write_timed(&rb, channel_buffer, nof_bytes, RINGBUFFER_TIMEOUT_MS);
if (err < SRSRAN_SUCCESS and err != SRSRAN_ERROR_TIMEOUT) {
logger.error("Error writing zeros in ringbuffer");
}
} while (err < SRSRAN_SUCCESS and not quit);
// Increment channel counter
channel_idx++;
}
// Increment write timestamp
write_ts += buffer.get_nof_samples();
}
void read_baseband(std::vector<cf_t*>& buffers, uint32_t nof_samples)
{
// For each ringbuffer, read
int nof_bytes = (int)(nof_samples * sizeof(cf_t));
uint32_t channel_idx = 0;
for (srsran_ringbuffer_t& rb : ringbuffers) {
// If quit is flagged, return instantly
if (quit) {
return;
}
// Extract channel buffer pointer
cf_t* channel_buffer = buffers[channel_idx];
// If the pointer is not set, use the zero buffer
if (channel_buffer == nullptr) {
channel_buffer = sink_buffer.data();
}
// Actual write
int err = SRSRAN_SUCCESS;
do {
err = srsran_ringbuffer_read_timed(&rb, channel_buffer, nof_bytes, RINGBUFFER_TIMEOUT_MS);
if (err < SRSRAN_SUCCESS and err != SRSRAN_ERROR_TIMEOUT) {
logger.error("Error reading zeros in ringbuffer");
}
} while (err < SRSRAN_SUCCESS and not quit);
// Increment channel counter
channel_idx++;
}
}
public:
struct args_t {
double srate_hz = 11.52e6;
uint32_t buffer_sz_ms = 10; ///< Buffer size in milliseconds
uint32_t nof_channels = 1;
args_t(double srate_hz_, uint32_t buffer_sz_ms_, uint32_t nof_channels_) :
srate_hz(srate_hz_), buffer_sz_ms(buffer_sz_ms_), nof_channels(nof_channels_)
{}
};
phy_common(const args_t& args, srslog::basic_logger& logger_) : srate_hz(args.srate_hz), logger(logger_)
{
uint32_t buffer_sz = std::ceil((double)args.buffer_sz_ms * srate_hz * 1e-3);
uint32_t buffer_sz_bytes = sizeof(cf_t) * buffer_sz;
// Allocate data buffer
zero_buffer.resize(buffer_sz);
sink_buffer.resize(buffer_sz);
// Allocate ring buffers
ringbuffers.resize(args.nof_channels);
// Initialise buffers
for (srsran_ringbuffer_t& rb : ringbuffers) {
if (srsran_ringbuffer_init(&rb, buffer_sz_bytes) < SRSRAN_SUCCESS) {
logger.error("Error ringbuffer init");
}
}
}
~phy_common()
{
for (srsran_ringbuffer_t& rb : ringbuffers) {
srsran_ringbuffer_free(&rb);
}
}
void push_semaphore(void* worker_ptr) { semaphore.push(worker_ptr); }
void
worker_end(void* h, bool tx_enable, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr) override
{
// Synchronize worker
semaphore.wait(h);
uint64_t tx_ts = srsran_timestamp_uint64(&tx_time.get(0), srate_hz);
// Check transmit timestamp is not in the past
if (tx_ts < write_ts) {
logger.error("Tx time is %d samples in the past", (uint32_t)(write_ts - tx_ts));
semaphore.release();
return;
}
// Write zero padding if necessary
write_zero_padding((uint32_t)(tx_ts - write_ts));
// Write baseband
if (tx_enable) {
write_baseband(buffer);
} else {
write_zero_padding(buffer.get_nof_samples());
}
// Release semaphore, so next worker can be used
semaphore.release();
}
void read(std::vector<cf_t*>& buffers, uint32_t nof_samples, srsran::rf_timestamp_t& timestamp)
{
// Detect if zero padding is necessary
if (read_ts + nof_samples > write_ts) {
uint32_t nof_zero_pading = (uint32_t)((read_ts + nof_samples) - write_ts);
write_zero_padding(nof_zero_pading);
}
// Actual baseband read
read_baseband(buffers, nof_samples);
// Write timestamp
srsran_timestamp_init_uint64(timestamp.get_ptr(0), read_ts, srate_hz);
// Increment Rx timestamp
read_ts += nof_samples;
}
void stop() {
quit = true;
}
};
#endif // SRSRAN_DUMMY_PHY_COMMON_H

@ -0,0 +1,563 @@
/**
*
* \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 "test_bench.h"
test_bench::args_t::args_t(int argc, char** argv)
{
// Flag configuration as valid
valid = true;
cell_list.resize(1);
cell_list[0].carrier.nof_prb = 52;
phy_cfg.carrier = cell_list[0].carrier;
phy_cfg.carrier.pci = 500;
phy_cfg.carrier.absolute_frequency_point_a = 633928;
phy_cfg.carrier.absolute_frequency_ssb = 634176;
phy_cfg.carrier.offset_to_carrier = 0;
phy_cfg.carrier.scs = srsran_subcarrier_spacing_15kHz;
phy_cfg.carrier.nof_prb = 52;
phy_cfg.ssb.periodicity_ms = 5;
phy_cfg.ssb.position_in_burst[0] = true;
phy_cfg.ssb.scs = srsran_subcarrier_spacing_30kHz;
phy_cfg.pdcch.coreset_present[1] = true;
phy_cfg.pdcch.coreset[1].id = 1;
phy_cfg.pdcch.coreset[1].freq_resources[0] = true;
phy_cfg.pdcch.coreset[1].freq_resources[1] = true;
phy_cfg.pdcch.coreset[1].freq_resources[2] = true;
phy_cfg.pdcch.coreset[1].freq_resources[3] = true;
phy_cfg.pdcch.coreset[1].freq_resources[4] = true;
phy_cfg.pdcch.coreset[1].freq_resources[5] = true;
phy_cfg.pdcch.coreset[1].freq_resources[6] = true;
phy_cfg.pdcch.coreset[1].freq_resources[7] = true;
phy_cfg.pdcch.coreset[1].duration = 1;
phy_cfg.pdcch.coreset[1].mapping_type = srsran_coreset_mapping_type_non_interleaved;
phy_cfg.pdcch.coreset[1].precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
phy_cfg.pdcch.coreset_present[2] = true;
phy_cfg.pdcch.coreset[2].id = 2;
phy_cfg.pdcch.coreset[2].freq_resources[0] = true;
phy_cfg.pdcch.coreset[2].freq_resources[1] = true;
phy_cfg.pdcch.coreset[2].freq_resources[2] = true;
phy_cfg.pdcch.coreset[2].freq_resources[3] = true;
phy_cfg.pdcch.coreset[2].freq_resources[4] = true;
phy_cfg.pdcch.coreset[2].freq_resources[5] = true;
phy_cfg.pdcch.coreset[2].freq_resources[6] = true;
phy_cfg.pdcch.coreset[2].freq_resources[7] = true;
phy_cfg.pdcch.coreset[2].duration = 1;
phy_cfg.pdcch.coreset[2].mapping_type = srsran_coreset_mapping_type_non_interleaved;
phy_cfg.pdcch.coreset[2].precoder_granularity = srsran_coreset_precoder_granularity_reg_bundle;
phy_cfg.pdcch.search_space_present[1] = true;
phy_cfg.pdcch.search_space[1].id = 1;
phy_cfg.pdcch.search_space[1].coreset_id = 1;
phy_cfg.pdcch.search_space[1].duration = 1;
phy_cfg.pdcch.search_space[1].nof_candidates[0] = 1;
phy_cfg.pdcch.search_space[1].nof_candidates[1] = 1;
phy_cfg.pdcch.search_space[1].nof_candidates[2] = 1;
phy_cfg.pdcch.search_space[1].nof_candidates[3] = 0;
phy_cfg.pdcch.search_space[1].nof_candidates[4] = 0;
phy_cfg.pdcch.search_space[1].formats[0] = srsran_dci_format_nr_0_0;
phy_cfg.pdcch.search_space[1].formats[1] = srsran_dci_format_nr_1_0;
phy_cfg.pdcch.search_space[1].nof_formats = 2;
phy_cfg.pdcch.search_space[1].type = srsran_search_space_type_common_3;
phy_cfg.pdcch.search_space_present[2] = true;
phy_cfg.pdcch.search_space[2].id = 2;
phy_cfg.pdcch.search_space[2].coreset_id = 2;
phy_cfg.pdcch.search_space[2].duration = 1;
phy_cfg.pdcch.search_space[2].nof_candidates[0] = 0;
phy_cfg.pdcch.search_space[2].nof_candidates[1] = 2;
phy_cfg.pdcch.search_space[2].nof_candidates[2] = 1;
phy_cfg.pdcch.search_space[2].nof_candidates[3] = 0;
phy_cfg.pdcch.search_space[2].nof_candidates[4] = 0;
phy_cfg.pdcch.search_space[2].formats[0] = srsran_dci_format_nr_0_0;
phy_cfg.pdcch.search_space[2].formats[1] = srsran_dci_format_nr_1_0;
phy_cfg.pdcch.search_space[2].nof_formats = 2;
phy_cfg.pdcch.search_space[2].type = srsran_search_space_type_ue;
phy_cfg.pdcch.ra_search_space_present = true;
phy_cfg.pdcch.ra_search_space = phy_cfg.pdcch.search_space[1];
phy_cfg.pdcch.ra_search_space.type = srsran_search_space_type_common_1;
phy_cfg.pdsch.common_time_ra[0].mapping_type = srsran_sch_mapping_type_A;
phy_cfg.pdsch.common_time_ra[0].sliv = 40;
phy_cfg.pdsch.common_time_ra[1].mapping_type = srsran_sch_mapping_type_A;
phy_cfg.pdsch.common_time_ra[1].sliv = 57;
phy_cfg.pdsch.nof_common_time_ra = 2;
phy_cfg.pusch.common_time_ra[0].k = 4;
phy_cfg.pusch.common_time_ra[0].mapping_type = srsran_sch_mapping_type_A;
phy_cfg.pusch.common_time_ra[0].sliv = 27;
phy_cfg.pusch.common_time_ra[1].k = 5;
phy_cfg.pusch.common_time_ra[1].mapping_type = srsran_sch_mapping_type_A;
phy_cfg.pusch.common_time_ra[1].sliv = 27;
phy_cfg.pusch.nof_common_time_ra = 2;
phy_cfg.pdsch.typeA_pos = srsran_dmrs_sch_typeA_pos_2;
phy_cfg.pusch.typeA_pos = srsran_dmrs_sch_typeA_pos_2;
phy_cfg.tdd.pattern1.period_ms = 10;
phy_cfg.tdd.pattern1.nof_dl_slots = 7;
phy_cfg.tdd.pattern1.nof_dl_symbols = 6;
phy_cfg.tdd.pattern1.nof_ul_slots = 4;
phy_cfg.tdd.pattern1.nof_ul_symbols = 4;
phy_cfg.pdsch.dmrs_typeA.additional_pos = srsran_dmrs_sch_add_pos_1;
phy_cfg.pdsch.dmrs_typeA.present = true;
phy_cfg.pdsch.alloc = srsran_resource_alloc_type1;
phy_cfg.pucch.enabled = true;
srsran_pucch_nr_resource_t& pucch0 = phy_cfg.pucch.sets[0].resources[0];
srsran_pucch_nr_resource_t& pucch1 = phy_cfg.pucch.sets[0].resources[1];
srsran_pucch_nr_resource_t& pucch2 = phy_cfg.pucch.sets[0].resources[2];
srsran_pucch_nr_resource_t& pucch3 = phy_cfg.pucch.sets[0].resources[3];
srsran_pucch_nr_resource_t& pucch4 = phy_cfg.pucch.sets[0].resources[4];
srsran_pucch_nr_resource_t& pucch5 = phy_cfg.pucch.sets[0].resources[5];
srsran_pucch_nr_resource_t& pucch6 = phy_cfg.pucch.sets[0].resources[6];
srsran_pucch_nr_resource_t& pucch7 = phy_cfg.pucch.sets[0].resources[7];
phy_cfg.pucch.sets[0].nof_resources = 8;
srsran_pucch_nr_resource_t& pucch8 = phy_cfg.pucch.sets[1].resources[0];
srsran_pucch_nr_resource_t& pucch9 = phy_cfg.pucch.sets[1].resources[1];
srsran_pucch_nr_resource_t& pucch10 = phy_cfg.pucch.sets[1].resources[2];
srsran_pucch_nr_resource_t& pucch11 = phy_cfg.pucch.sets[1].resources[3];
srsran_pucch_nr_resource_t& pucch12 = phy_cfg.pucch.sets[1].resources[4];
srsran_pucch_nr_resource_t& pucch13 = phy_cfg.pucch.sets[1].resources[5];
srsran_pucch_nr_resource_t& pucch14 = phy_cfg.pucch.sets[1].resources[6];
srsran_pucch_nr_resource_t& pucch15 = phy_cfg.pucch.sets[1].resources[7];
phy_cfg.pucch.sets[1].nof_resources = 8;
pucch0.starting_prb = 0;
pucch0.format = SRSRAN_PUCCH_NR_FORMAT_1;
pucch0.initial_cyclic_shift = 0;
pucch0.nof_symbols = 14;
pucch0.start_symbol_idx = 0;
pucch0.time_domain_occ = 0;
pucch1 = pucch0;
pucch1.initial_cyclic_shift = 4;
pucch1.time_domain_occ = 0;
pucch2 = pucch0;
pucch2.initial_cyclic_shift = 8;
pucch2.time_domain_occ = 0;
pucch3 = pucch0;
pucch3.initial_cyclic_shift = 0;
pucch3.time_domain_occ = 1;
pucch4 = pucch0;
pucch4.initial_cyclic_shift = 0;
pucch4.time_domain_occ = 1;
pucch5 = pucch0;
pucch5.initial_cyclic_shift = 4;
pucch5.time_domain_occ = 1;
pucch6 = pucch0;
pucch6.initial_cyclic_shift = 0;
pucch6.time_domain_occ = 2;
pucch7 = pucch0;
pucch7.initial_cyclic_shift = 4;
pucch7.time_domain_occ = 2;
pucch8.starting_prb = 51;
pucch8.format = SRSRAN_PUCCH_NR_FORMAT_2;
pucch8.nof_prb = 1;
pucch8.nof_symbols = 2;
pucch8.start_symbol_idx = 0;
pucch9 = pucch8;
pucch9.start_symbol_idx = 2;
pucch10 = pucch8;
pucch10.start_symbol_idx = 4;
pucch11 = pucch8;
pucch11.start_symbol_idx = 6;
pucch12 = pucch8;
pucch12.start_symbol_idx = 8;
pucch13 = pucch8;
pucch13.start_symbol_idx = 10;
pucch14 = pucch8;
pucch14.start_symbol_idx = 12;
pucch15 = pucch8;
pucch15.starting_prb = 1;
pucch15.start_symbol_idx = 0;
srsran_pucch_nr_resource_t& pucch16 = phy_cfg.pucch.sr_resources[1].resource;
pucch16.starting_prb = 0;
pucch16.format = SRSRAN_PUCCH_NR_FORMAT_1;
pucch16.initial_cyclic_shift = 8;
pucch16.nof_symbols = 14;
pucch16.start_symbol_idx = 0;
pucch16.time_domain_occ = 2;
phy_cfg.pucch.sr_resources[1].configured = true;
phy_cfg.pucch.sr_resources[1].sr_id = 0;
phy_cfg.pucch.sr_resources[1].period = 40;
phy_cfg.pucch.sr_resources[1].offset = 8;
phy_cfg.pucch.sr_resources[1].resource = pucch16;
phy_cfg.harq_ack.dl_data_to_ul_ack[0] = 8;
phy_cfg.harq_ack.dl_data_to_ul_ack[1] = 7;
phy_cfg.harq_ack.dl_data_to_ul_ack[2] = 6;
phy_cfg.harq_ack.dl_data_to_ul_ack[3] = 5;
phy_cfg.harq_ack.dl_data_to_ul_ack[4] = 4;
phy_cfg.harq_ack.dl_data_to_ul_ack[5] = 12;
phy_cfg.harq_ack.dl_data_to_ul_ack[6] = 11;
phy_cfg.prach.freq_offset = 2;
// pusch-Config: setup (1)
// setup
// dmrs-UplinkForPUSCH-MappingTypeA: setup (1)
// setup
// dmrs-AdditionalPosition: pos1 (1)
// transformPrecodingDisabled
// pusch-PowerControl
// msg3-Alpha: alpha1 (7)
// p0-NominalWithoutGrant: -90dBm
// p0-AlphaSets: 1 item
// Item 0
// P0-PUSCH-AlphaSet
// p0-PUSCH-AlphaSetId: 0
// p0: 0dB
// alpha: alpha1 (7)
// pathlossReferenceRSToAddModList: 1 item
// Item 0
// PUSCH-PathlossReferenceRS
// pusch-PathlossReferenceRS-Id: 0
// referenceSignal: ssb-Index (0)
// ssb-Index: 0
// sri-PUSCH-MappingToAddModList: 1 item
// Item 0
// SRI-PUSCH-PowerControl
// sri-PUSCH-PowerControlId: 0
// sri-PUSCH-PathlossReferenceRS-Id: 0
// sri-P0-PUSCH-AlphaSetId: 0
// sri-PUSCH-ClosedLoopIndex: i0 (0)
// resourceAllocation: resourceAllocationType1 (1)
// uci-OnPUSCH: setup (1)
// setup
// betaOffsets: semiStatic (1)
// semiStatic
// betaOffsetACK-Index1: 9
// betaOffsetACK-Index2: 9
// betaOffsetACK-Index3: 9
// betaOffsetCSI-Part1-Index1: 6
// betaOffsetCSI-Part1-Index2: 6
// betaOffsetCSI-Part2-Index1: 6
// betaOffsetCSI-Part2-Index2: 6
// scaling: f1 (3)
// srs-Config: setup (1)
// setup
// srs-ResourceSetToAddModList: 1 item
// Item 0
// SRS-ResourceSet
// srs-ResourceSetId: 0
// srs-ResourceIdList: 1 item
// Item 0
// SRS-ResourceId: 0
// resourceType: aperiodic (0)
// aperiodic
// aperiodicSRS-ResourceTrigger: 1
// slotOffset: 7
// usage: codebook (1)
// p0: -90dBm
// pathlossReferenceRS: ssb-Index (0)
// ssb-Index: 0
// srs-ResourceToAddModList: 1 item
// Item 0
// SRS-Resource
// srs-ResourceId: 0
// nrofSRS-Ports: port1 (0)
// transmissionComb: n2 (0)
// n2
// combOffset-n2: 0
// cyclicShift-n2: 0
// resourceMapping
// startPosition: 0
// nrofSymbols: n1 (0)
// repetitionFactor: n1 (0)
// freqDomainPosition: 0
// freqDomainShift: 6
// freqHopping
// c-SRS: 11
// b-SRS: 3
// b-hop: 0
// groupOrSequenceHopping: neither (0)
// resourceType: aperiodic (0)
// aperiodic
// sequenceId: 500
// firstActiveUplinkBWP-Id: 0
// pusch-ServingCellConfig: setup (1)
// setup
// pdcch-ServingCellConfig: setup (1)
// setup
// pdsch-ServingCellConfig: setup (1)
// setup
// nrofHARQ-ProcessesForPDSCH: n16 (5)
// csi-MeasConfig: setup (1)
// setup
// nzp-CSI-RS-ResourceToAddModList: 5 items
// Item 0
// NZP-CSI-RS-Resource
// nzp-CSI-RS-ResourceId: 0
// resourceMapping
// frequencyDomainAllocation: row2 (1)
// row2: 8000 [bit length 12, 4 LSB pad bits, 1000 0000 0000
// .... decimal value 2048]
// nrofPorts: p1 (0)
// firstOFDMSymbolInTimeDomain: 4
// cdm-Type: noCDM (0)
// density: one (1)
// one: NULL
// freqBand
// startingRB: 0
// nrofRBs: 52
// powerControlOffset: 0dB
// powerControlOffsetSS: db0 (1)
// scramblingID: 0
// periodicityAndOffset: slots80 (9)
// slots80: 1
// qcl-InfoPeriodicCSI-RS: 0
// Item 1
// NZP-CSI-RS-Resource
// nzp-CSI-RS-ResourceId: 1
// resourceMapping
// frequencyDomainAllocation: row1 (0)
// row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal
// value 1]
// nrofPorts: p1 (0)
// firstOFDMSymbolInTimeDomain: 4
// cdm-Type: noCDM (0)
// density: three (2)
// three: NULL
// freqBand
// startingRB: 0
// nrofRBs: 52
// powerControlOffset: 0dB
// powerControlOffsetSS: db0 (1)
// scramblingID: 0
// periodicityAndOffset: slots40 (7)
// slots40: 11
// qcl-InfoPeriodicCSI-RS: 0
// Item 2
// NZP-CSI-RS-Resource
// nzp-CSI-RS-ResourceId: 2
// resourceMapping
// frequencyDomainAllocation: row1 (0)
// row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal
// value 1]
// nrofPorts: p1 (0)
// firstOFDMSymbolInTimeDomain: 8
// cdm-Type: noCDM (0)
// density: three (2)
// three: NULL
// freqBand
// startingRB: 0
// nrofRBs: 52
// powerControlOffset: 0dB
// powerControlOffsetSS: db0 (1)
// scramblingID: 0
// periodicityAndOffset: slots40 (7)
// slots40: 11
// qcl-InfoPeriodicCSI-RS: 0
// Item 3
// NZP-CSI-RS-Resource
// nzp-CSI-RS-ResourceId: 3
// resourceMapping
// frequencyDomainAllocation: row1 (0)
// row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal
// value 1]
// nrofPorts: p1 (0)
// firstOFDMSymbolInTimeDomain: 4
// cdm-Type: noCDM (0)
// density: three (2)
// three: NULL
// freqBand
// startingRB: 0
// nrofRBs: 52
// powerControlOffset: 0dB
// powerControlOffsetSS: db0 (1)
// scramblingID: 0
// periodicityAndOffset: slots40 (7)
// slots40: 12
// qcl-InfoPeriodicCSI-RS: 0
// Item 4
// NZP-CSI-RS-Resource
// nzp-CSI-RS-ResourceId: 4
// resourceMapping
// frequencyDomainAllocation: row1 (0)
// row1: 10 [bit length 4, 4 LSB pad bits, 0001 .... decimal
// value 1]
// nrofPorts: p1 (0)
// firstOFDMSymbolInTimeDomain: 8
// cdm-Type: noCDM (0)
// density: three (2)
// three: NULL
// freqBand
// startingRB: 0
// nrofRBs: 52
// powerControlOffset: 0dB
// powerControlOffsetSS: db0 (1)
// scramblingID: 0
// periodicityAndOffset: slots40 (7)
// slots40: 12
// qcl-InfoPeriodicCSI-RS: 0
// nzp-CSI-RS-ResourceSetToAddModList: 2 items
// Item 0
// NZP-CSI-RS-ResourceSet
// nzp-CSI-ResourceSetId: 0
// nzp-CSI-RS-Resources: 1 item
// Item 0
// NZP-CSI-RS-ResourceId: 0
// Item 1
// NZP-CSI-RS-ResourceSet
// nzp-CSI-ResourceSetId: 1
// nzp-CSI-RS-Resources: 4 items
// Item 0
// NZP-CSI-RS-ResourceId: 1
// Item 1
// NZP-CSI-RS-ResourceId: 2
// Item 2
// NZP-CSI-RS-ResourceId: 3
// Item 3
// NZP-CSI-RS-ResourceId: 4
// trs-Info: true (0)
// csi-IM-ResourceToAddModList: 1 item
// Item 0
// CSI-IM-Resource
// csi-IM-ResourceId: 0
// csi-IM-ResourceElementPattern: pattern1 (1)
// pattern1
// subcarrierLocation-p1: s8 (2)
// symbolLocation-p1: 8
// freqBand
// startingRB: 0
// nrofRBs: 52
// periodicityAndOffset: slots80 (9)
// slots80: 1
// csi-IM-ResourceSetToAddModList: 1 item
// Item 0
// CSI-IM-ResourceSet
// csi-IM-ResourceSetId: 0
// csi-IM-Resources: 1 item
// Item 0
// CSI-IM-ResourceId: 0
// csi-ResourceConfigToAddModList: 3 items
// Item 0
// CSI-ResourceConfig
// csi-ResourceConfigId: 0
// csi-RS-ResourceSetList: nzp-CSI-RS-SSB (0)
// nzp-CSI-RS-SSB
// nzp-CSI-RS-ResourceSetList: 1 item
// Item 0
// NZP-CSI-RS-ResourceSetId: 0
// bwp-Id: 0
// resourceType: periodic (2)
// Item 1
// CSI-ResourceConfig
// csi-ResourceConfigId: 1
// csi-RS-ResourceSetList: csi-IM-ResourceSetList (1)
// csi-IM-ResourceSetList: 1 item
// Item 0
// CSI-IM-ResourceSetId: 0
// bwp-Id: 0
// resourceType: periodic (2)
// Item 2
// CSI-ResourceConfig
// csi-ResourceConfigId: 2
// csi-RS-ResourceSetList: nzp-CSI-RS-SSB (0)
// nzp-CSI-RS-SSB
// nzp-CSI-RS-ResourceSetList: 1 item
// Item 0
// NZP-CSI-RS-ResourceSetId: 1
// bwp-Id: 0
// resourceType: periodic (2)
// csi-ReportConfigToAddModList: 1 item
// Item 0
// CSI-ReportConfig
// reportConfigId: 0
// resourcesForChannelMeasurement: 0
// csi-IM-ResourcesForInterference: 1
// reportConfigType: periodic (0)
// periodic
// reportSlotConfig: slots80 (7)
// slots80: 9
// pucch-CSI-ResourceList: 1 item
// Item 0
// PUCCH-CSI-Resource
// uplinkBandwidthPartId: 0
// pucch-Resource: 17
// reportQuantity: cri-RI-PMI-CQI (1)
// cri-RI-PMI-CQI: NULL
// reportFreqConfiguration
// cqi-FormatIndicator: widebandCQI (0)
// timeRestrictionForChannelMeasurements: notConfigured (1)
// timeRestrictionForInterferenceMeasurements: notConfigured (1)
// groupBasedBeamReporting: disabled (1)
// disabled
// cqi-Table: table2 (1)
// subbandSize: value1 (0)
// tag-Id: 0
}
class ue_dummy_stack : public srsue::stack_interface_phy_nr
{
public:
void in_sync() override {}
void out_of_sync() override {}
void run_tti(const uint32_t tti) override {}
int sf_indication(const uint32_t tti) override { return 0; }
sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { return sched_rnti_t(); }
sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override { return sched_rnti_t(); }
void new_grant_dl(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_t* action) override {}
void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) override {}
void new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) override {}
void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) override {}
bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) override { return false; }
};
class gnb_dummy_stack : public srsenb::stack_interface_phy_nr
{
public:
int sf_indication(const uint32_t tti) override { return 0; }
int rx_data_indication(rx_data_ind_t& grant) override { return 0; }
int get_dl_sched(uint32_t tti, dl_sched_list_t& dl_sched_res) override
{
dl_sched_res[0].nof_grants = 0;
return SRSRAN_SUCCESS;
}
int get_ul_sched(uint32_t tti, ul_sched_list_t& ul_sched_res) override { return 0; }
};
int main(int argc, char** argv)
{
test_bench::args_t args(argc, argv);
// Parse arguments
TESTASSERT(args.valid);
ue_dummy_stack ue_stack;
gnb_dummy_stack gnb_stack;
// Create test bench
test_bench tb(args, gnb_stack, ue_stack);
// Assert bench is initialised correctly
TESTASSERT(tb.is_initialised());
for (uint32_t i = 0; i < 1000; i++) {
TESTASSERT(tb.run_tti());
}
// If reached here, the test is successful
return SRSRAN_SUCCESS;
}

@ -1,113 +0,0 @@
/**
*
* \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 "srsenb/hdr/phy/nr/worker_pool.h"
#include "srsran/common/test_common.h"
#include "srsue/hdr/phy/nr/worker_pool.h"
class phy_common : public srsran::phy_common_interface
{
public:
void
worker_end(void* h, bool tx_enable, srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& tx_time, bool is_nr) override
{}
};
class ue_dummy_stack : public srsue::stack_interface_phy_nr
{
public:
void in_sync() override {}
void out_of_sync() override {}
void run_tti(const uint32_t tti) override {}
int sf_indication(const uint32_t tti) override { return 0; }
sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { return sched_rnti_t(); }
sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override { return sched_rnti_t(); }
void new_grant_dl(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_t* action) override {}
void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) override {}
void new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) override {}
void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) override {}
bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) override { return false; }
};
class test_bench
{
private:
srsenb::nr::worker_pool gnb_phy;
phy_common gnb_phy_com;
srsue::nr::worker_pool ue_phy;
phy_common ue_phy_com;
ue_dummy_stack ue_stack;
bool initialised = false;
public:
struct args_t {
uint32_t nof_threads = 6;
uint32_t nof_prb = 52;
bool parse(int argc, char** argv);
};
test_bench(const args_t& args) : ue_phy(args.nof_threads), gnb_phy(args.nof_threads)
{
// Prepare cell list
srsenb::phy_cell_cfg_list_nr_t cell_list(1);
cell_list[0].carrier.nof_prb = args.nof_prb;
// Prepare gNb PHY arguments
srsenb::phy_args_t gnb_phy_args = {};
// Initialise gnb
if (not gnb_phy.init(cell_list, gnb_phy_args, gnb_phy_com, srslog::get_default_sink(), 31)) {
return;
}
// Prepare PHY
srsue::phy_args_nr_t ue_phy_args = {};
// Initialise UE PHY
if (not ue_phy.init(ue_phy_args, ue_phy_com, &ue_stack, 31)) {
return;
}
initialised = true;
}
~test_bench()
{
gnb_phy.stop();
ue_phy.stop();
}
bool is_initialised() const { return initialised; }
};
bool test_bench::args_t::parse(int argc, char** argv)
{
return true;
}
int main(int argc, char** argv)
{
test_bench::args_t args = {};
// Parse arguments
TESTASSERT(args.parse(argc, argv));
// Create test bench
test_bench tb(args);
// Assert bench is initialised correctly
TESTASSERT(tb.is_initialised());
// If reached here, the test is successful
return SRSRAN_SUCCESS;
}

@ -0,0 +1,131 @@
/**
*
* \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_TEST_BENCH_H
#define SRSRAN_TEST_BENCH_H
#include "dummy_phy_common.h"
#include "srsenb/hdr/phy/nr/worker_pool.h"
#include "srsue/hdr/phy/nr/worker_pool.h"
class test_bench
{
private:
uint32_t tti = 0;
srsenb::nr::worker_pool gnb_phy;
phy_common gnb_phy_com;
srsue::nr::worker_pool ue_phy;
phy_common ue_phy_com;
bool initialised = false;
uint32_t sf_sz = 0;
public:
struct args_t {
double srate_hz = 11.52e6;
uint32_t nof_threads = 6;
uint32_t nof_channels = 1;
uint32_t buffer_sz_ms = 10;
bool valid = false;
srsran::phy_cfg_nr_t phy_cfg = {};
srsenb::phy_cell_cfg_list_nr_t cell_list = {};
srsenb::nr::worker_pool::args_t gnb_args;
uint16_t rnti = 0x1234;
args_t(int argc, char** argv);
};
test_bench(const args_t& args, srsenb::stack_interface_phy_nr& gnb_stack, srsue::stack_interface_phy_nr& ue_stack) :
ue_phy(args.nof_threads),
gnb_phy(args.cell_list, args.gnb_args, gnb_phy_com, gnb_stack, srslog::get_default_sink()),
ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels),
srslog::fetch_basic_logger("UE /PHY/COM")),
gnb_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels),
srslog::fetch_basic_logger("GNB/PHY/COM"))
{
// Calculate subframe length
sf_sz = (uint32_t)std::round(args.srate_hz * 1e-3);
// Prepare PHY
srsue::phy_args_nr_t ue_phy_args = {};
// Initialise UE PHY
if (not ue_phy.init(ue_phy_args, ue_phy_com, &ue_stack, 31)) {
return;
}
// Set UE configuration
if (not ue_phy.set_config(args.phy_cfg)) {
return;
}
initialised = true;
}
~test_bench()
{
ue_phy_com.stop();
gnb_phy_com.stop();
gnb_phy.stop();
ue_phy.stop();
}
bool is_initialised() const { return initialised; }
bool run_tti()
{
// Get gNb worker
srsenb::nr::sf_worker* gnb_worker = gnb_phy.wait_worker(tti);
if (gnb_worker == nullptr) {
return false;
}
// Feed gNb the UE transmitted signal
srsran::rf_timestamp_t gnb_time = {};
std::vector<cf_t*> gnb_rx_buffers(1);
gnb_rx_buffers[0] = gnb_worker->get_buffer_rx(0, 0);
ue_phy_com.read(gnb_rx_buffers, sf_sz, gnb_time);
// Set gNb time
gnb_time.add(TX_ENB_DELAY * 1e-3);
gnb_worker->set_time(tti, gnb_time);
// Start gNb work
gnb_phy_com.push_semaphore(gnb_worker);
gnb_phy.start_worker(gnb_worker);
// Get UE worker
srsue::nr::sf_worker* ue_worker = ue_phy.wait_worker(tti);
if (ue_worker == nullptr) {
return false;
}
// Feed UE the gNb transmitted signal
srsran::rf_timestamp_t ue_time = {};
std::vector<cf_t*> ue_rx_buffers(1);
ue_rx_buffers[0] = ue_worker->get_buffer(0, 0);
gnb_phy_com.read(ue_rx_buffers, sf_sz, ue_time);
// Set UE time
ue_time.add(TX_ENB_DELAY * 1e-3);
ue_worker->set_tti(tti);
ue_worker->set_tx_time(ue_time);
// Start gNb work
ue_phy_com.push_semaphore(ue_worker);
ue_phy.start_worker(ue_worker);
tti++;
return true;
}
};
#endif // SRSRAN_TEST_BENCH_H
Loading…
Cancel
Save