Created dummy radio and moved UE dummy PHY into real UE SA PHY

master
Xavier Arteaga 3 years ago committed by Xavier Arteaga
parent 3b396c8a9a
commit d6ee282796

@ -44,6 +44,19 @@ public:
* @param result Cell search result completion * @param result Cell search result completion
*/ */
virtual void cell_search_found_cell(const cell_search_result_t& result) = 0; virtual void cell_search_found_cell(const cell_search_result_t& result) = 0;
/**
* @brief Describes a cell select result
*/
struct cell_select_result_t {
bool successful = false; ///< Cell was found and physical layer is synchronised
};
/**
* @brief Informs RRC about cell select process completion
* @param result Cell select result completion
*/
virtual void cell_select_completed(const cell_select_result_t& result) = 0;
}; };
class mac_interface_phy_nr class mac_interface_phy_nr
@ -182,23 +195,25 @@ public:
}; };
struct phy_args_nr_t { struct phy_args_nr_t {
uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill
uint32_t nof_carriers = 1; uint32_t nof_carriers = 1;
uint32_t max_nof_prb = 106; uint32_t max_nof_prb = 106;
double srate_hz = 23.04e6; double srate_hz = 23.04e6;
uint32_t nof_phy_threads = 3; uint32_t nof_phy_threads = 3;
uint32_t worker_cpu_mask = 0; uint32_t worker_cpu_mask = 0;
srsran::phy_log_args_t log = {}; int slot_recv_thread_prio = 0; /// Specifies the slot receive thread priority, RT by default
srsran_ue_dl_nr_args_t dl = {}; int workers_thread_prio = 2; /// Specifies the workers thread priority, RT by default
srsran_ue_ul_nr_args_t ul = {}; srsran::phy_log_args_t log = {};
std::set<uint32_t> fixed_sr = {1}; srsran_ue_dl_nr_args_t dl = {};
uint32_t fix_wideband_cqi = 15; ///< Set to a non-zero value for fixing the wide-band CQI report srsran_ue_ul_nr_args_t ul = {};
bool store_pdsch_ko = false; std::set<uint32_t> fixed_sr = {1};
float trs_epre_ema_alpha = 0.1f; ///< EPRE measurement exponential average alpha uint32_t fix_wideband_cqi = 15; ///< Set to a non-zero value for fixing the wide-band CQI report
float trs_rsrp_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha bool store_pdsch_ko = false;
float trs_sinr_ema_alpha = 0.1f; ///< SINR measurement exponential average alpha float trs_epre_ema_alpha = 0.1f; ///< EPRE measurement exponential average alpha
float trs_cfo_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha float trs_rsrp_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha
bool enable_worker_cfo = true; ///< Enable/Disable open loop CFO correction at the workers float trs_sinr_ema_alpha = 0.1f; ///< SINR measurement exponential average alpha
float trs_cfo_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha
bool enable_worker_cfo = true; ///< Enable/Disable open loop CFO correction at the workers
phy_args_nr_t() phy_args_nr_t()
{ {

@ -0,0 +1,290 @@
/**
*
* \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 "channel_mapping.h"
#include "radio_metrics.h"
#include "rf_buffer.h"
#include "rf_timestamp.h"
#include "srsran/common/interfaces_common.h"
#include "srsran/interfaces/radio_interfaces.h"
#include "srsran/phy/resampling/resampler.h"
#include "srsran/phy/rf/rf.h"
#include "srsran/radio/radio_base.h"
#include "srsran/srslog/srslog.h"
#include "srsran/srsran.h"
#include <condition_variable>
#include <list>
#include <string>
#ifndef SRSRAN_RADIO_DUMMY_H
#define SRSRAN_RADIO_DUMMY_H
namespace srsran {
/**
* Implementation of the radio interface for the PHY
*
* It uses the rf C library object to access the underlying radio. This implementation uses a flat array to
* transmit/receive samples for all RF channels. The N carriers and P antennas are mapped into M=NP RF channels (M <=
* SRSRAN_MAX_CHANNELS). Note that all carriers must have the same number of antennas.
*
* The underlying radio receives and transmits M RF channels synchronously from possibly multiple radios using the same
* rf driver object. In the current implementation, the mapping between N carriers and P antennas is sequentially, eg:
* [carrier_0_port_0, carrier_0_port_1, carrier_1_port_0, carrier_1_port_1, ..., carrier_N_port_N]
*/
class radio_dummy : public srsran::radio_base, public srsran::radio_interface_phy
{
private:
static const uint32_t TEMP_BUFFER_SZ = SRSRAN_SF_LEN_MAX * SRSRAN_NOF_SF_X_FRAME;
srslog::basic_logger& logger;
std::vector<srsran_ringbuffer_t> rx_ring_buffers;
std::vector<srsran_ringbuffer_t> tx_ring_buffers;
std::mutex tx_mutex;
double srate_hz = 0.0f;
std::atomic<float> rx_gain = {1.0f};
std::atomic<float> tx_gain = {1.0f};
cf_t* temp_buffer = nullptr;
uint64_t rx_timestamp = 0;
uint64_t tx_timestamp = 0;
srsran_rf_info_t rf_info = {};
std::atomic<bool> is_initialised = {false};
std::atomic<bool> quit = {false};
void write_ring_buffers(std::vector<srsran_ringbuffer_t>& buffers, cf_t** buffer, uint32_t nsamples)
{
for (uint32_t i = 0; i < buffers.size(); i++) {
int ret = SRSRAN_SUCCESS;
do {
if (ret != SRSRAN_SUCCESS) {
logger.error("Ring buffer write failed (full). Trying again.");
}
ret = srsran_ringbuffer_write_timed(&buffers[i], buffer[i], (int)(sizeof(cf_t) * nsamples), 1000);
} while (ret == SRSRAN_ERROR_TIMEOUT and not quit);
}
}
void read_ring_buffers(std::vector<srsran_ringbuffer_t>& buffers, cf_t** buffer, uint32_t nsamples)
{
for (uint32_t i = 0; i < buffers.size(); i++) {
int ret = SRSRAN_SUCCESS;
do {
if (ret != SRSRAN_SUCCESS) {
logger.error("Ring buffer read failed. Trying again.");
}
ret = srsran_ringbuffer_read_timed(&buffers[i], buffer[i], (int)(sizeof(cf_t) * nsamples), 1000);
} while (ret == SRSRAN_ERROR_TIMEOUT and not quit);
}
}
void write_zeros_ring_buffers(std::vector<srsran_ringbuffer_t>& buffers, uint32_t nsamples)
{
uint32_t n = SRSRAN_MIN(nsamples, TEMP_BUFFER_SZ);
srsran_vec_cf_zero(temp_buffer, n);
std::array<cf_t*, SRSRAN_MAX_CHANNELS> zero_buffer_pointers = {};
for (cf_t*& ptr : zero_buffer_pointers) {
ptr = temp_buffer;
}
while (nsamples > 0) {
// Get new number of samples
n = SRSRAN_MIN(nsamples, TEMP_BUFFER_SZ);
// Write zeros in the buffers
write_ring_buffers(buffers, zero_buffer_pointers.data(), n);
nsamples -= n;
}
}
void advance_tx_timestamp(uint64_t ts)
{
std::lock_guard<std::mutex> lock(tx_mutex);
// Make sure new timestamp has not passed
if (ts < tx_timestamp) {
return;
}
// Calculate transmission gap in samples
uint32_t tx_gap = (uint32_t)(ts - tx_timestamp);
// Skip zeros if there is no gap
if (tx_gap == 0) {
return;
}
// Write zeros in tx ring buffer
write_zeros_ring_buffers(tx_ring_buffers, tx_gap);
// Update new transmit timestamp
tx_timestamp = ts;
}
public:
radio_dummy() : logger(srslog::fetch_basic_logger("RF", false)) {}
~radio_dummy()
{
for (auto& rb : rx_ring_buffers) {
srsran_ringbuffer_free(&rb);
}
if (temp_buffer) {
free(temp_buffer);
}
}
std::string get_type() override { return "dummy"; }
int init(const rf_args_t& args_, phy_interface_radio* phy_) override
{
// Set logger level
logger.set_level(srslog::str_to_basic_level(args_.log_level));
// Get base sampling rate and assert the value is valid
srate_hz = args_.srate_hz;
if (not std::isnormal(srate_hz)) {
logger.error("A valid sampling rate is missing");
return SRSRAN_ERROR;
}
// Create receiver ring buffers
rx_ring_buffers.resize(args_.nof_carriers * args_.nof_antennas);
for (auto& rb : rx_ring_buffers) {
if (srsran_ringbuffer_init(&rb, (int)sizeof(cf_t) * TEMP_BUFFER_SZ) != SRSRAN_SUCCESS) {
perror("init softbuffer");
}
}
// Create transmitter ring buffers
tx_ring_buffers.resize(args_.nof_carriers * args_.nof_antennas);
for (auto& rb : tx_ring_buffers) {
if (srsran_ringbuffer_init(&rb, (int)sizeof(cf_t) * TEMP_BUFFER_SZ) != SRSRAN_SUCCESS) {
perror("init softbuffer");
}
}
// Create temporal buffer
temp_buffer = srsran_vec_cf_malloc(TEMP_BUFFER_SZ);
if (!temp_buffer) {
perror("malloc");
}
// Set RF Info (in dB)
rf_info.min_rx_gain = 0.0f;
rf_info.max_rx_gain = 90.0f;
rf_info.min_tx_gain = 0.0f;
rf_info.max_tx_gain = 90.0f;
// Finally, the radio is initialised
is_initialised = true;
return SRSRAN_SUCCESS;
}
void stop() override { quit = true; }
bool get_metrics(rf_metrics_t* metrics) override { return false; }
void set_loglevel(std::string& str) { logger.set_level(srslog::str_to_basic_level(str)); }
void write_rx(cf_t** buffer, uint32_t nsamples) { write_ring_buffers(rx_ring_buffers, buffer, nsamples); }
void read_tx(cf_t** buffer, uint32_t nsamples) { read_ring_buffers(tx_ring_buffers, buffer, nsamples); }
bool tx(srsran::rf_buffer_interface& buffer, const srsran::rf_timestamp_interface& tx_time) override
{
bool ret = true;
// Convert timestamp to samples
uint64_t tx_time_n = srsran_timestamp_uint64(&tx_time.get(0), srate_hz);
// Check if the transmission is in the past
if (tx_time_n < tx_timestamp) {
logger.error("Error transmission in the past");
return false;
}
// Advance TX to timestamp
advance_tx_timestamp(tx_time_n);
// From now on, protect buffers
std::lock_guard<std::mutex> lock(tx_mutex);
// Write transmission buffers into the ring buffer
write_ring_buffers(tx_ring_buffers, buffer.to_cf_t(), buffer.get_nof_samples());
// Increment transmit timestamp
tx_timestamp += buffer.get_nof_samples();
return ret;
}
void release_freq(const uint32_t& carrier_idx) override{};
void tx_end() override {}
bool rx_now(srsran::rf_buffer_interface& buffer, srsran::rf_timestamp_interface& rxd_time) override
{
// Advance Tx buffer
advance_tx_timestamp(rx_timestamp + buffer.get_nof_samples());
// Read samples
read_ring_buffers(rx_ring_buffers, buffer.to_cf_t(), buffer.get_nof_samples());
// Apply Rx gain
for (uint32_t i = 0; i < rx_ring_buffers.size(); i++) {
cf_t* ptr = buffer.get(i);
srsran_vec_sc_prod_cfc(ptr, rx_gain, ptr, buffer.get_nof_samples());
}
// Set Rx timestamp
srsran_timestamp_init_uint64(rxd_time.get_ptr(0), rx_timestamp, (double)srate_hz);
// Advance timestamp
rx_timestamp += buffer.get_nof_samples();
return true;
}
void set_tx_freq(const uint32_t& channel_idx, const double& freq) override
{
logger.info("Set Tx freq to %+.0f MHz.", freq * 1.0e-6);
}
void set_rx_freq(const uint32_t& channel_idx, const double& freq) override
{
logger.info("Set Rx freq to %+.0f MHz.", freq * 1.0e-6);
}
void set_rx_gain_th(const float& gain) override
{
rx_gain = srsran_convert_dB_to_amplitude(gain);
logger.info("Set Rx gain-th to %+.1f dB (%.6f).", gain, rx_gain.load());
}
void set_tx_gain(const float& gain) override
{
tx_gain = srsran_convert_dB_to_amplitude(gain);
logger.info("Set Tx gain to %+.1f dB (%.6f).", gain, tx_gain.load());
}
void set_rx_gain(const float& gain) override
{
rx_gain = srsran_convert_dB_to_amplitude(gain);
logger.info("Set Rx gain to %+.1f dB (%.6f).", gain, rx_gain.load());
}
void set_tx_srate(const double& srate) override { logger.info("Set Tx sampling rate to %+.3f MHz.", srate * 1.0e-6); }
void set_rx_srate(const double& srate) override { logger.info("Set Rx sampling rate to %+.3f MHz.", srate * 1.0e-6); }
void set_channel_rx_offset(uint32_t ch, int32_t offset_samples) override{};
float get_rx_gain() override { return srsran_convert_amplitude_to_dB(rx_gain); }
double get_freq_offset() override { return 0; }
bool is_continuous_tx() override { return false; }
bool get_is_start_of_burst() override { return false; }
bool is_init() override { return is_initialised; }
void reset() override {}
srsran_rf_info_t* get_info() override { return &rf_info; }
};
} // namespace srsran
#endif // SRSRAN_RADIO_DUMMY_H

@ -727,18 +727,25 @@ ssb_measure(srsran_ssb_t* q, const cf_t ssb_grid[SRSRAN_SSB_NOF_RE], uint32_t N_
float rsrp_sss = SRSRAN_CSQABS(corr_sss); float rsrp_sss = SRSRAN_CSQABS(corr_sss);
float rsrp = (rsrp_pss + rsrp_sss) / 2.0f; float rsrp = (rsrp_pss + rsrp_sss) / 2.0f;
// avoid taking log of 0 (NaN) // Avoid taking log of 0 or another abnormal value
if (rsrp == 0.0) { if (!isnormal(rsrp)) {
rsrp = 1.0; rsrp = 1e-9f;
} }
// Compute Noise // Estimate Noise:
float n0_pss = 1e-9; // Almost 0 // - Infinite (1e9), if the EPRE or RSRP is zero
float n0_sss = 1e-9; // Almost 0 // - EPRE-RSRP if EPRE > RSRP
if (epre_pss > rsrp_pss) { // - zero (1e-9), otherwise
float n0_pss = 1e-9f;
if (!isnormal(epre_pss) || !isnormal(rsrp_pss)) {
n0_pss = 1e9f;
} else if (epre_pss > rsrp_pss) {
n0_pss = epre - rsrp_pss; n0_pss = epre - rsrp_pss;
} }
if (epre_sss > rsrp_sss) { float n0_sss = 1e-9f;
if (!isnormal(epre_sss) || !isnormal(rsrp_sss)) {
n0_sss = 1e9f;
} else if (epre_sss > rsrp_sss) {
n0_sss = epre - rsrp_sss; n0_sss = epre - rsrp_sss;
} }
float n0 = (n0_pss + n0_sss) / 2.0f; float n0 = (n0_pss + n0_sss) / 2.0f;
@ -1267,6 +1274,8 @@ static int ssb_pss_find(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, u
// Average power, skip window if value is invalid (0.0, nan or inf) // Average power, skip window if value is invalid (0.0, nan or inf)
float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz); float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz);
if (!isnormal(avg_pwr_corr)) { if (!isnormal(avg_pwr_corr)) {
// Advance time
t_offset += q->corr_window;
continue; continue;
} }

@ -167,7 +167,9 @@ static int ue_sync_nr_run_track(srsran_ue_sync_nr_t* q, cf_t* buffer)
// Check if the SSB selected candidate index shall be received in this subframe // Check if the SSB selected candidate index shall be received in this subframe
bool is_ssb_opportunity = (q->sf_idx == srsran_ssb_candidate_sf_idx(&q->ssb, q->ssb_idx, half_frame > 0)); bool is_ssb_opportunity = (q->sf_idx == srsran_ssb_candidate_sf_idx(&q->ssb, q->ssb_idx, half_frame > 0));
// If // TODO: it assumes SSB opportunity is every 10 ms, use real SSB SF candidate
is_ssb_opportunity = (q->sf_idx % SRSRAN_NOF_SF_X_FRAME == 0);
if (is_ssb_opportunity) { if (is_ssb_opportunity) {
// Measure PSS/SSS and decode PBCH // Measure PSS/SSS and decode PBCH
if (srsran_ssb_track(&q->ssb, buffer, q->N_id, q->ssb_idx, half_frame, &measurements, &pbch_msg) < SRSRAN_SUCCESS) { if (srsran_ssb_track(&q->ssb, buffer, q->N_id, q->ssb_idx, half_frame, &measurements, &pbch_msg) < SRSRAN_SUCCESS) {

@ -43,14 +43,14 @@ public:
int recv_callback(srsran::rf_buffer_t& rf_buffer, srsran_timestamp_t* timestamp); int recv_callback(srsran::rf_buffer_t& rf_buffer, srsran_timestamp_t* timestamp);
bool run_sfn_sync(); bool run_sfn_sync();
bool run_camping(srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& timestamp);
void run_stack_tti(); void run_stack_tti();
srsran_slot_cfg_t get_slot_cfg(); srsran_slot_cfg_t get_slot_cfg();
private: private:
const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe const static int MIN_TTI_JUMP = 1; ///< Time gap reported to stack after receiving subframe
const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata const static int MAX_TTI_JUMP = 1000; ///< Maximum time gap tolerance in RF stream metadata
enum { SEARCHING = 0, CAMPING } state = SEARCHING;
srslog::basic_logger& logger; srslog::basic_logger& logger;
stack_interface_phy_nr* stack = nullptr; stack_interface_phy_nr* stack = nullptr;
srsran::radio_interface_phy* radio = nullptr; srsran::radio_interface_phy* radio = nullptr;

@ -73,9 +73,9 @@ public:
sync_state::state_t get_state(); sync_state::state_t get_state();
// The following methods control the SYNC state machine // The following methods control the SYNC state machine
void cell_go_idle(); void cell_go_idle();
cell_search::ret_t cell_search_run(const cell_search::cfg_t& cfg); cell_search::ret_t cell_search_run(const cell_search::cfg_t& cfg);
bool cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req); rrc_interface_phy_nr::cell_select_result_t cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req);
void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override;
@ -92,6 +92,7 @@ private:
std::atomic<bool> running = {false}; std::atomic<bool> running = {false};
cf_t* rx_buffer = nullptr; cf_t* rx_buffer = nullptr;
double srate_hz = 0; ///< Sampling rate in Hz
uint32_t slot_sz = 0; ///< Subframe size (1-ms) uint32_t slot_sz = 0; ///< Subframe size (1-ms)
uint32_t tti = 0; uint32_t tti = 0;
srsran::tti_semaphore<void*> tti_semaphore; srsran::tti_semaphore<void*> tti_semaphore;

@ -41,7 +41,7 @@ public:
sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); }
worker_pool(srslog::basic_logger& logger_, uint32_t max_workers); worker_pool(srslog::basic_logger& logger_, uint32_t max_workers);
bool init(const phy_args_nr_t& args_, srsran::phy_common_interface& common, stack_interface_phy_nr* stack_, int prio); bool init(const phy_args_nr_t& args_, srsran::phy_common_interface& common, stack_interface_phy_nr* stack_);
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();

@ -134,14 +134,14 @@ public:
/********** NR INTERFACE ********************/ /********** NR INTERFACE ********************/
int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_);
bool set_config(const srsran::phy_cfg_nr_t& cfg) final; bool set_config(const srsran::phy_cfg_nr_t& cfg) final;
void send_prach(const uint32_t prach_occasion, void send_prach(const uint32_t prach_occasion,
const int preamble_index, const int preamble_index,
const float preamble_received_target_power, const float preamble_received_target_power,
const float ta_base_sec = 0.0f) final; const float ta_base_sec = 0.0f) final;
void set_earfcn(std::vector<uint32_t> earfcns); void set_earfcn(std::vector<uint32_t> earfcns);
bool has_valid_sr_resource(uint32_t sr_id) final; bool has_valid_sr_resource(uint32_t sr_id) final;
void clear_pending_grants() final; void clear_pending_grants() final;
int set_rar_grant(uint32_t rar_slot_idx, int set_rar_grant(uint32_t rar_slot_idx,
std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS> packed_ul_grant, std::array<uint8_t, SRSRAN_RAR_UL_GRANT_NBITS> packed_ul_grant,
uint16_t rnti, uint16_t rnti,

@ -59,15 +59,15 @@ public:
bool start_cell_search(const cell_search_args_t& req) final; bool start_cell_search(const cell_search_args_t& req) final;
bool start_cell_select(const cell_select_args_t& req) final; bool start_cell_select(const cell_select_args_t& req) final;
void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final{}; void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) final { return workers.get_metrics(*m); };
void srsran_phy_logger(phy_logger_level_t log_level, char* str); void srsran_phy_logger(phy_logger_level_t log_level, char* str);
private: private:
srslog::basic_logger& logger; srslog::basic_logger& logger;
srslog::basic_logger& logger_phy_lib; srslog::basic_logger& logger_phy_lib;
nr::worker_pool workers; nr::worker_pool workers;
nr::sync_sa sync; nr::sync_sa sync;
srsran::phy_cfg_nr_t config_nr = {}; srsran::phy_cfg_nr_t config_nr = {};
phy_args_nr_t args = {}; phy_args_nr_t args = {};
@ -76,7 +76,7 @@ private:
srsran::radio_interface_phy* radio = nullptr; srsran::radio_interface_phy* radio = nullptr;
stack_interface_phy_nr* stack = nullptr; stack_interface_phy_nr* stack = nullptr;
std::atomic<bool> is_configured = {false}; std::atomic<bool> is_configured = {false};
// Since cell_search/cell_select operations take a lot of time, we use another queue to process the other commands // Since cell_search/cell_select operations take a lot of time, we use another queue to process the other commands
// in parallel and avoid accumulating in the queue // in parallel and avoid accumulating in the queue
@ -86,10 +86,8 @@ private:
void init_background(); void init_background();
std::thread init_thread; std::thread init_thread;
const static int SF_RECV_THREAD_PRIO = 0; const static int MAX_WORKERS = 4;
const static int WORKERS_THREAD_PRIO = 2; const static int DEFAULT_WORKERS = 4;
const static int MAX_WORKERS = 4;
const static int DEFAULT_WORKERS = 4;
static void set_default_args(phy_args_nr_t& args); static void set_default_args(phy_args_nr_t& args);
bool check_args(const phy_args_nr_t& args); bool check_args(const phy_args_nr_t& args);

@ -45,6 +45,7 @@ cc_worker::cc_worker(uint32_t cc_idx_, srslog::basic_logger& log, state& phy_sta
srsran_ssb_args_t ssb_args = {}; srsran_ssb_args_t ssb_args = {};
ssb_args.enable_measure = true; ssb_args.enable_measure = true;
ssb_args.enable_decode = true; ssb_args.enable_decode = true;
ssb_args.enable_search = true;
if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) {
ERROR("Error initiating SSB"); ERROR("Error initiating SSB");
return; return;
@ -158,7 +159,7 @@ void cc_worker::decode_pdcch_dl()
if (logger.debug.enabled()) { if (logger.debug.enabled()) {
// log coreset info // log coreset info
srsran_coreset_t* coreset = &ue_dl.cfg.coreset[dci_rx[i].ctx.coreset_id]; srsran_coreset_t* coreset = &ue_dl.cfg.coreset[dci_rx[i].ctx.coreset_id];
std::array<char, 512> coreset_str; std::array<char, 512> coreset_str;
srsran_coreset_to_str(coreset, coreset_str.data(), coreset_str.size()); srsran_coreset_to_str(coreset, coreset_str.data(), coreset_str.size());
logger.info("PDCCH: coreset=%d, %s", cc_idx, coreset_str.data()); logger.info("PDCCH: coreset=%d, %s", cc_idx, coreset_str.data());
@ -349,6 +350,16 @@ bool cc_worker::decode_pdsch_dl()
bool cc_worker::measure_csi() bool cc_worker::measure_csi()
{ {
srsran_ssb_search_res_t search_res = {};
if (srsran_ssb_search(&ssb, rx_buffer[0], cfg.carrier.pci, &search_res) < SRSRAN_SUCCESS) {
logger.error("Error measuring SSB");
return false;
}
if (search_res.pbch_msg.crc) {
logger.error("Error measuring SSB");
}
// Measure SSB CSI // Measure SSB CSI
if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) { if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) {
srsran_csi_trs_measurements_t meas = {}; srsran_csi_trs_measurements_t meas = {};

@ -90,10 +90,8 @@ int slot_sync::recv_callback(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_t
stack_tti_ts_new = rf_timestamp.get(0); stack_tti_ts_new = rf_timestamp.get(0);
// Run stack if the sync state is not in camping // Run stack if the sync state is not in camping
if (state == SEARCHING) { logger.debug("run_stack_tti: from recv");
logger.debug("run_stack_tti: from recv"); run_stack_tti();
run_stack_tti();
}
logger.debug("SYNC: received %d samples from radio", data.get_nof_samples()); logger.debug("SYNC: received %d samples from radio", data.get_nof_samples());
@ -109,9 +107,27 @@ bool slot_sync::run_sfn_sync()
} }
if (outcome.in_sync) { if (outcome.in_sync) {
slot_cfg.idx = outcome.sfn * SRSRAN_NSLOTS_PER_SF_NR(srsran_subcarrier_spacing_15kHz) + outcome.sf_idx; slot_cfg.idx = outcome.sfn * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) + outcome.sf_idx;
}
return outcome.in_sync;
}
bool slot_sync::run_camping(srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& timestamp)
{
srsran_ue_sync_nr_outcome_t outcome = {};
if (srsran_ue_sync_nr_zerocopy(&ue_sync_nr, buffer.to_cf_t(), &outcome) < SRSRAN_SUCCESS) {
logger.error("SYNC: error in zerocopy");
return false;
}
if (outcome.in_sync) {
slot_cfg.idx = outcome.sfn * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz) + outcome.sf_idx;
} }
// Set RF timestamp
*timestamp.get_ptr(0) = outcome.timestamp;
return outcome.in_sync; return outcome.in_sync;
} }

@ -17,10 +17,7 @@ namespace nr {
worker_pool::worker_pool(srslog::basic_logger& logger_, uint32_t max_workers) : pool(max_workers), logger(logger_) {} worker_pool::worker_pool(srslog::basic_logger& logger_, uint32_t max_workers) : pool(max_workers), logger(logger_) {}
bool worker_pool::init(const phy_args_nr_t& args, bool worker_pool::init(const phy_args_nr_t& args, srsran::phy_common_interface& common, stack_interface_phy_nr* stack_)
srsran::phy_common_interface& common,
stack_interface_phy_nr* stack_,
int prio)
{ {
phy_state.stack = stack_; phy_state.stack = stack_;
phy_state.args = args; phy_state.args = args;
@ -59,7 +56,7 @@ bool worker_pool::init(const phy_args_nr_t& args,
std::lock_guard<std::mutex> lock(cfg_mutex); std::lock_guard<std::mutex> lock(cfg_mutex);
w = new sf_worker(common, phy_state, cfg, log); w = new sf_worker(common, phy_state, cfg, log);
} }
pool.init_worker(i, w, prio, args.worker_cpu_mask); pool.init_worker(i, w, args.workers_thread_prio, args.worker_cpu_mask);
workers.push_back(std::unique_ptr<sf_worker>(w)); workers.push_back(std::unique_ptr<sf_worker>(w));
} }

@ -323,7 +323,6 @@ bool phy::cell_select(phy_cell_t cell)
// Indicate workers that cell selection has finished // Indicate workers that cell selection has finished
common.cell_is_selecting = false; common.cell_is_selecting = false;
}); });
return true; return true;
} else { } else {
@ -620,7 +619,7 @@ void phy::set_mch_period_stop(uint32_t stop)
int phy::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) int phy::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_)
{ {
stack_nr = stack_; stack_nr = stack_;
if (!nr_workers.init(args_, common, stack_, WORKERS_THREAD_PRIO)) { if (!nr_workers.init(args_, common, stack_)) {
return SRSRAN_ERROR; return SRSRAN_ERROR;
} }

@ -96,11 +96,12 @@ void phy_nr_sa::init_background()
{ {
nr::sync_sa::args_t sync_args = {}; nr::sync_sa::args_t sync_args = {};
sync_args.srate_hz = args.srate_hz; sync_args.srate_hz = args.srate_hz;
sync_args.thread_priority = args.slot_recv_thread_prio;
if (not sync.init(sync_args, stack, radio)) { if (not sync.init(sync_args, stack, radio)) {
logger.error("Error initialising SYNC"); logger.error("Error initialising SYNC");
return; return;
} }
workers.init(args, sync, stack, WORKERS_THREAD_PRIO); workers.init(args, sync, stack);
is_configured = true; is_configured = true;
} }
@ -204,8 +205,8 @@ bool phy_nr_sa::start_cell_select(const cell_select_args_t& req)
selected_cell = req.carrier; selected_cell = req.carrier;
cmd_worker_cell.add_cmd([this, req]() { cmd_worker_cell.add_cmd([this, req]() {
// Request cell search to lower synchronization instance. // Request cell search to lower synchronization instance and push the result directly to the stack
sync.cell_select_run(req); stack->cell_select_completed(sync.cell_select_run(req));
}); });
return true; return true;

@ -23,9 +23,10 @@ sync_sa::~sync_sa() {}
bool sync_sa::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) bool sync_sa::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_)
{ {
stack = stack_; stack = stack_;
radio = radio_; radio = radio_;
slot_sz = (uint32_t)(args.srate_hz / 1000.0f); srate_hz = args.srate_hz;
slot_sz = (uint32_t)(args.srate_hz / 1000.0f);
// Initialise cell search internal object // Initialise cell search internal object
if (not searcher.init(args.get_cell_search())) { if (not searcher.init(args.get_cell_search())) {
@ -139,14 +140,14 @@ cell_search::ret_t sync_sa::cell_search_run(const cell_search::cfg_t& cfg)
return cs_ret; return cs_ret;
} }
bool sync_sa::cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req) rrc_interface_phy_nr::cell_select_result_t sync_sa::cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req)
{ {
std::unique_lock<std::mutex> ul(rrc_mutex); std::unique_lock<std::mutex> ul(rrc_mutex);
// Wait the FSM to transition to IDLE // Wait the FSM to transition to IDLE
if (!wait_idle()) { if (!wait_idle()) {
logger.error("Cell Search: SYNC thread didn't transition to IDLE after 100 ms\n"); logger.error("Cell Search: SYNC thread didn't transition to IDLE after 100 ms\n");
return false; return {};
} }
rrc_proc_state = PROC_SELECT_RUNNING; rrc_proc_state = PROC_SELECT_RUNNING;
@ -161,21 +162,26 @@ bool sync_sa::cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& re
srsran_ue_sync_nr_cfg_t cfg = {}; srsran_ue_sync_nr_cfg_t cfg = {};
cfg.N_id = req.carrier.pci; cfg.N_id = req.carrier.pci;
cfg.ssb = req.ssb_cfg; cfg.ssb = req.ssb_cfg;
cfg.ssb.srate_hz = srate_hz;
if (slot_synchronizer.set_sync_cfg(cfg)) { if (slot_synchronizer.set_sync_cfg(cfg)) {
logger.error("Cell Search: Failed setting slot synchronizer configuration"); logger.error("Cell Search: Failed setting slot synchronizer configuration");
return false; return {};
} }
// SFN synchronization // SFN synchronization
phy_state.run_sfn_sync(); phy_state.run_sfn_sync();
if (phy_state.is_camping()) {
// Determine if the procedure was successful if it is camping
rrc_interface_phy_nr::cell_select_result_t result = {};
result.successful = phy_state.is_camping();
if (result.successful) {
logger.info("Cell Select: SFN synchronized. CAMPING..."); logger.info("Cell Select: SFN synchronized. CAMPING...");
} else { } else {
logger.info("Cell Select: Could not synchronize SFN"); logger.info("Cell Select: Could not synchronize SFN");
} }
rrc_proc_state = PROC_IDLE; rrc_proc_state = PROC_IDLE;
return true; return result;
} }
sync_state::state_t sync_sa::get_state() sync_state::state_t sync_sa::get_state()
@ -234,7 +240,7 @@ void sync_sa::run_state_sfn_sync()
logger.info("SYNC: SFN synchronised successfully (SFN=%d). Transitioning to IDLE...", logger.info("SYNC: SFN synchronised successfully (SFN=%d). Transitioning to IDLE...",
tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz)); tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz));
phy_state.state_exit(); phy_state.state_exit(true);
return; return;
} }
@ -262,8 +268,8 @@ void sync_sa::run_state_cell_camping()
srsran::rf_buffer_t rf_buffer = {}; srsran::rf_buffer_t rf_buffer = {};
rf_buffer.set_nof_samples(slot_sz); rf_buffer.set_nof_samples(slot_sz);
rf_buffer.set(0, nr_worker->get_buffer(0, 0)); rf_buffer.set(0, nr_worker->get_buffer(0, 0));
if (not slot_synchronizer.recv_callback(rf_buffer, last_rx_time.get_ptr(0))) { if (not slot_synchronizer.run_camping(rf_buffer, last_rx_time)) {
logger.error("SYNC: receiving from radio\n"); logger.error("SYNC: detected out-of-sync... unhandled outcome...");
} }
srsran::phy_common_interface::worker_context_t context; srsran::phy_common_interface::worker_context_t context;
@ -303,12 +309,6 @@ void sync_sa::run_thread()
run_state_cell_camping(); run_state_cell_camping();
break; break;
} }
// Advance stack TTI
#ifdef useradio
slot_synchronizer.run_stack_tti();
#else
stack->run_tti(tti, 1);
#endif
} }
} }
void sync_sa::worker_end(const srsran::phy_common_interface::worker_context_t& w_ctx, void sync_sa::worker_end(const srsran::phy_common_interface::worker_context_t& w_ctx,

@ -20,51 +20,6 @@
#include <boost/program_options/parsers.hpp> #include <boost/program_options/parsers.hpp>
#include <iostream> #include <iostream>
class throttled_dummy_stack : public ue_dummy_stack
{
private:
bool pending_tti = false;
std::mutex pending_tti_mutex;
std::condition_variable pending_tti_cvar;
std::atomic<bool> running = {true};
public:
throttled_dummy_stack(const ue_dummy_stack::args_t& args, srsue::phy_interface_stack_nr& phy) :
ue_dummy_stack(args, phy)
{}
void wait_tti() override
{
// Wait for tick
std::unique_lock<std::mutex> lock(pending_tti_mutex);
while (not pending_tti and running) {
pending_tti_cvar.wait_for(lock, std::chrono::milliseconds(1));
}
// Let the tick proceed
pending_tti = false;
pending_tti_cvar.notify_all();
}
void tick()
{
// Wait for TTI to get processed
std::unique_lock<std::mutex> lock(pending_tti_mutex);
while (pending_tti and running) {
pending_tti_cvar.wait_for(lock, std::chrono::milliseconds(1));
}
// Let the TTI proceed
pending_tti = true;
pending_tti_cvar.notify_all();
}
void stop()
{
running = false;
pending_tti_cvar.notify_all();
}
};
struct args_t { struct args_t {
// Generic parameters // Generic parameters
double srate_hz = 11.52e6; double srate_hz = 11.52e6;
@ -211,8 +166,8 @@ int parse_args(int argc, char** argv, args_t& args)
class dummy_ue class dummy_ue
{ {
private: private:
throttled_dummy_stack stack; ue_dummy_stack stack;
srsue::phy_nr_sa phy; srsue::phy_nr_sa phy;
public: public:
struct args_t { struct args_t {

@ -87,7 +87,9 @@ private:
srsenb::rlc_dummy rlc_obj; srsenb::rlc_dummy rlc_obj;
std::unique_ptr<srsenb::mac_nr> mac; std::unique_ptr<srsenb::mac_nr> mac;
srslog::basic_logger& sched_logger; srslog::basic_logger& sched_logger;
bool autofill_sch_bsr = false; bool autofill_sch_bsr = false;
bool wait_preamble = false;
bool enable_user_sched = false;
std::mutex metrics_mutex; std::mutex metrics_mutex;
metrics_t metrics = {}; metrics_t metrics = {};
@ -166,7 +168,8 @@ private:
bool schedule_pdsch(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) bool schedule_pdsch(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched)
{ {
if (dl.slots.count(SRSRAN_SLOT_NR_MOD(srsran_subcarrier_spacing_15kHz, slot_cfg.idx)) == 0) { if (dl.slots.count(SRSRAN_SLOT_NR_MOD(srsran_subcarrier_spacing_15kHz, slot_cfg.idx)) == 0 or
not enable_user_sched) {
return true; return true;
} }
@ -241,7 +244,8 @@ private:
bool schedule_pusch(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched) bool schedule_pusch(const srsran_slot_cfg_t& slot_cfg, dl_sched_t& dl_sched)
{ {
if (ul.slots.count(SRSRAN_SLOT_NR_MOD(srsran_subcarrier_spacing_15kHz, slot_cfg.idx + 4)) == 0) { if (ul.slots.count(SRSRAN_SLOT_NR_MOD(srsran_subcarrier_spacing_15kHz, slot_cfg.idx + 4)) == 0 or
not enable_user_sched) {
return true; return true;
} }
@ -353,7 +357,8 @@ public:
phy_cfg(args.phy_cfg), phy_cfg(args.phy_cfg),
ss_id(args.ss_id), ss_id(args.ss_id),
use_dummy_mac(args.use_dummy_mac == "dummymac"), use_dummy_mac(args.use_dummy_mac == "dummymac"),
sched_logger(srslog::fetch_basic_logger("MAC")) sched_logger(srslog::fetch_basic_logger("MAC")),
wait_preamble(args.wait_preamble)
{ {
logger.set_level(srslog::str_to_basic_level(args.log_level)); logger.set_level(srslog::str_to_basic_level(args.log_level));
sched_logger.set_level(srslog::str_to_basic_level(args.log_level)); sched_logger.set_level(srslog::str_to_basic_level(args.log_level));
@ -372,14 +377,6 @@ public:
std::vector<srsenb::sched_nr_interface::cell_cfg_t> cells_cfg = srsenb::get_default_cells_cfg(1, phy_cfg); std::vector<srsenb::sched_nr_interface::cell_cfg_t> cells_cfg = srsenb::get_default_cells_cfg(1, phy_cfg);
mac->cell_cfg(cells_cfg); mac->cell_cfg(cells_cfg);
// add UE to scheduler
if (not use_dummy_mac and not args.wait_preamble) {
srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1, phy_cfg);
ue_cfg.ue_bearers[4].direction = srsenb::mac_lc_ch_cfg_t::BOTH;
mac->reserve_rnti(0, ue_cfg);
}
dl.mcs = args.pdsch.mcs; dl.mcs = args.pdsch.mcs;
ul.mcs = args.pusch.mcs; ul.mcs = args.pusch.mcs;
@ -463,6 +460,19 @@ public:
bool is_valid() const { return valid; } bool is_valid() const { return valid; }
void start_scheduling()
{
// add UE to scheduler
if (not use_dummy_mac and not wait_preamble) {
srsenb::sched_nr_interface::ue_cfg_t ue_cfg = srsenb::get_default_ue_cfg(1, phy_cfg);
ue_cfg.ue_bearers[4].direction = srsenb::mac_lc_ch_cfg_t::BOTH;
mac->reserve_rnti(0, ue_cfg);
}
enable_user_sched = true;
}
int slot_indication(const srsran_slot_cfg_t& slot_cfg) override { return 0; } int slot_indication(const srsran_slot_cfg_t& slot_cfg) override { return 0; }
dl_sched_t* get_dl_sched(const srsran_slot_cfg_t& slot_cfg) override dl_sched_t* get_dl_sched(const srsran_slot_cfg_t& slot_cfg) override
@ -595,7 +605,7 @@ public:
} }
ul_sched.pusch.push_back(pusch); ul_sched.pusch.push_back(pusch);
} else if (uci_cfg.ack.count > 0 || uci_cfg.nof_csi > 0 || uci_cfg.o_sr > 0) { } else if ((uci_cfg.ack.count > 0 || uci_cfg.nof_csi > 0 || uci_cfg.o_sr > 0) and enable_user_sched) {
// If any UCI information is triggered, schedule PUCCH // If any UCI information is triggered, schedule PUCCH
ul_sched.pucch.emplace_back(); ul_sched.pucch.emplace_back();

@ -146,9 +146,7 @@ public:
uint32_t nof_channels = 1; uint32_t nof_channels = 1;
args_t(double srate_hz_, uint32_t buffer_sz_ms_, uint32_t nof_channels_) : args_t(double srate_hz_, uint32_t buffer_sz_ms_, uint32_t nof_channels_) :
srate_hz(srate_hz_), srate_hz(srate_hz_), buffer_sz_ms(buffer_sz_ms_), nof_channels(nof_channels_)
buffer_sz_ms(buffer_sz_ms_),
nof_channels(nof_channels_)
{} {}
}; };

@ -66,10 +66,18 @@ private:
metrics_t metrics = {}; metrics_t metrics = {};
srsue::phy_interface_stack_nr& phy; srsue::phy_interface_stack_nr& phy;
// Attributes for throttling PHY and avoiding PHY free-running
bool pending_tti = false;
std::mutex pending_tti_mutex;
std::condition_variable pending_tti_cvar;
std::atomic<bool> running = {true};
dummy_tx_harq_entity tx_harq_proc; dummy_tx_harq_entity tx_harq_proc;
dummy_rx_harq_entity rx_harq_proc; dummy_rx_harq_entity rx_harq_proc;
std::atomic<bool> cell_search_finished = {false}; std::atomic<bool> cell_search_finished = {false};
std::atomic<bool> cell_select_finished = {false};
cell_select_result_t cell_select_result = {};
public: public:
struct args_t { struct args_t {
@ -86,15 +94,19 @@ public:
} }
~ue_dummy_stack() { srsran_random_free(random_gen); } ~ue_dummy_stack() { srsran_random_free(random_gen); }
virtual void wait_tti()
{
// Do nothing
}
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, const uint32_t tti_jump) override void run_tti(const uint32_t tti, const uint32_t tti_jump) override
{ {
wait_tti(); // Wait for tick from test bench
std::unique_lock<std::mutex> lock(pending_tti_mutex);
while (not pending_tti and running) {
pending_tti_cvar.wait_for(lock, std::chrono::milliseconds(1));
}
// Let the tick proceed
pending_tti = false;
pending_tti_cvar.notify_all();
// Run PRACH // Run PRACH
if (prach_period != 0) { if (prach_period != 0) {
@ -107,6 +119,24 @@ public:
} }
} }
} }
void tick()
{
// Wait for TTI to get processed
std::unique_lock<std::mutex> lock(pending_tti_mutex);
while (pending_tti and running) {
pending_tti_cvar.wait_for(lock, std::chrono::milliseconds(1));
}
// Let the TTI proceed
pending_tti = true;
pending_tti_cvar.notify_all();
}
void stop()
{
running = false;
pending_tti_cvar.notify_all();
}
sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override
{ {
std::unique_lock<std::mutex> lock(rnti_mutex); std::unique_lock<std::mutex> lock(rnti_mutex);
@ -234,6 +264,23 @@ public:
// Flag as cell search is done // Flag as cell search is done
cell_search_finished = true; cell_search_finished = true;
} }
bool get_cell_select_finished()
{
bool ret = cell_select_finished;
cell_select_finished = false;
return ret;
}
cell_select_result_t get_cell_select_result() { return cell_select_result; }
void cell_select_completed(const cell_select_result_t& result) override
{
cell_select_result = result;
cell_select_finished = true;
}
}; };
#endif // SRSRAN_DUMMY_UE_STACK_H #endif // SRSRAN_DUMMY_UE_STACK_H

@ -39,7 +39,8 @@ test_bench::args_t::args_t(int argc, char** argv)
bpo::options_description options_gnb_stack("gNb stack and scheduling related options"); bpo::options_description options_gnb_stack("gNb stack and scheduling related options");
bpo::options_description options_gnb_phy("gNb PHY related options"); bpo::options_description options_gnb_phy("gNb PHY related options");
bpo::options_description options_ue_stack("UE stack options"); bpo::options_description options_ue_stack("UE stack options");
bpo::options_description options_ue_phy("UE stack options"); bpo::options_description options_ue_phy("UE PHY options");
bpo::options_description options_ue_rf("UE RF options");
bpo::options_description options_assertion("Test assertions"); bpo::options_description options_assertion("Test assertions");
bpo::options_description options_conf_file("Configuration file"); bpo::options_description options_conf_file("Configuration file");
@ -94,6 +95,10 @@ test_bench::args_t::args_t(int argc, char** argv)
("ue.phy.log.id_preamble", bpo::value<std::string>(&ue_phy.log.id_preamble)->default_value(" UE/"), "UE PHY log ID preamble") ("ue.phy.log.id_preamble", bpo::value<std::string>(&ue_phy.log.id_preamble)->default_value(" UE/"), "UE PHY log ID preamble")
; ;
options_ue_rf.add_options()
("ue.rf.log.level", bpo::value<std::string>(&ue_radio_log_level)->default_value("warning"), "UE RF log level")
;
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.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.")
@ -115,7 +120,8 @@ test_bench::args_t::args_t(int argc, char** argv)
bpo::positional_options_description p; bpo::positional_options_description p;
p.add("config_file", -1); p.add("config_file", -1);
options.add(options_tb).add(options_assertion).add(options_gnb_stack).add(options_gnb_phy).add(options_ue_stack).add(options_ue_phy).add(options_conf_file).add_options() options.add(options_tb).add(options_assertion).add(options_gnb_stack).add(options_gnb_phy).add(options_ue_stack)
.add(options_ue_phy).add(options_ue_rf).add(options_conf_file).add_options()
("help", "Show this message") ("help", "Show this message")
; ;
// clang-format on // clang-format on
@ -173,6 +179,7 @@ test_bench::args_t::args_t(int argc, char** argv)
// Calculate sampling rate in Hz // Calculate sampling rate in Hz
srate_hz = (double)(srsran_min_symbol_sz_rb(phy_cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs)); srate_hz = (double)(srsran_min_symbol_sz_rb(phy_cfg.carrier.nof_prb) * SRSRAN_SUBC_SPACING_NR(phy_cfg.carrier.scs));
ue_phy.srate_hz = srate_hz;
cell_list.resize(1); cell_list.resize(1);
cell_list[0].carrier = phy_cfg.carrier; cell_list[0].carrier = phy_cfg.carrier;
@ -196,6 +203,10 @@ test_bench::args_t::args_t(int argc, char** argv)
gnb_stack.pdsch.rb_start = 0; gnb_stack.pdsch.rb_start = 0;
} }
// Disable RT priority in the UE PHY
ue_phy.workers_thread_prio = -1;
ue_phy.slot_recv_thread_prio = -1;
// Flag configuration as valid // Flag configuration as valid
valid = true; valid = true;
} }
@ -216,6 +227,11 @@ int main(int argc, char** argv)
// Assert bench is initialised correctly // Assert bench is initialised correctly
TESTASSERT(tb.is_initialised()); TESTASSERT(tb.is_initialised());
// Start cell selection procedure
srsue::rrc_interface_phy_nr::cell_select_result_t cs_res =
tb.run_cell_select(args.phy_cfg.carrier, args.phy_cfg.get_ssb_cfg());
srsran_assert(cs_res.successful, "Failed to perform cell selection");
// Run per TTI basis // Run per TTI basis
while (tb.run_tti()) { while (tb.run_tti()) {
; // Do nothing ; // Do nothing

@ -14,9 +14,10 @@
#define SRSRAN_TEST_BENCH_H #define SRSRAN_TEST_BENCH_H
#include "dummy_phy_common.h" #include "dummy_phy_common.h"
#include "dummy_ue_phy.h"
#include "srsenb/hdr/phy/nr/worker_pool.h" #include "srsenb/hdr/phy/nr/worker_pool.h"
#include "srsran/radio/radio_dummy.h"
#include "srsue/hdr/phy/nr/worker_pool.h" #include "srsue/hdr/phy/nr/worker_pool.h"
#include "srsue/hdr/phy/phy_nr_sa.h"
class test_bench class test_bench
{ {
@ -31,10 +32,13 @@ private:
srsenb::nr::worker_pool gnb_phy; srsenb::nr::worker_pool gnb_phy;
phy_common gnb_phy_com; phy_common gnb_phy_com;
ue_dummy_stack ue_stack; ue_dummy_stack ue_stack;
ue_dummy_phy ue_phy; srsue::phy_nr_sa ue_phy;
phy_common ue_phy_com; srsran::radio_dummy ue_radio;
bool initialised = false; srsran::rf_timestamp_t gnb_rx_time = {};
uint32_t sf_sz = 0;
bool initialised = false;
uint32_t sf_sz = 0;
srsran::rf_buffer_t rf_buffer;
// Channel simulator // Channel simulator
srsran::channel dl_channel; srsran::channel dl_channel;
srsran::channel ul_channel; srsran::channel ul_channel;
@ -51,9 +55,10 @@ public:
gnb_dummy_stack::args_t gnb_stack; gnb_dummy_stack::args_t gnb_stack;
srsue::phy_args_nr_t ue_phy; srsue::phy_args_nr_t ue_phy;
ue_dummy_stack::args_t ue_stack; ue_dummy_stack::args_t ue_stack;
std::string phy_com_log_level = "info"; std::string gnb_phy_com_log_level = "info";
std::string phy_lib_log_level = "none"; std::string ue_radio_log_level = "info";
uint64_t durations_slots = 100; std::string phy_lib_log_level = "none";
uint64_t durations_slots = 100;
// channel simulator args // channel simulator args
srsran::channel::args_t dl_channel; srsran::channel::args_t dl_channel;
@ -71,19 +76,18 @@ public:
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_phy), ue_stack(args.ue_stack, ue_phy),
ue_phy("PHY", args.ue_phy.nof_phy_threads), ue_phy("PHY"),
ue_radio(),
ue_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels),
srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)),
gnb_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels), gnb_phy_com(phy_common::args_t(args.srate_hz, args.buffer_sz_ms, args.nof_channels),
srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)), srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME, srslog::get_default_sink(), false)),
sf_sz((uint32_t)std::round(args.srate_hz * 1e-3)), sf_sz((uint32_t)std::round(args.srate_hz * 1e-3)),
duration_slots(args.durations_slots), duration_slots(args.durations_slots),
dl_channel(args.dl_channel, 1, srslog::fetch_basic_logger(CHANNEL_LOG_NAME, srslog::get_default_sink(), false)), dl_channel(args.dl_channel, 1, srslog::fetch_basic_logger(CHANNEL_LOG_NAME, srslog::get_default_sink(), false)),
ul_channel(args.ul_channel, 1, srslog::fetch_basic_logger(CHANNEL_LOG_NAME, srslog::get_default_sink(), false)) ul_channel(args.ul_channel, 1, srslog::fetch_basic_logger(CHANNEL_LOG_NAME, srslog::get_default_sink(), false)),
rf_buffer(1)
{ {
srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level)); srslog::fetch_basic_logger(UE_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.gnb_phy_com_log_level));
srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.phy_com_log_level)); srslog::fetch_basic_logger(GNB_PHY_COM_LOG_NAME).set_level(srslog::str_to_basic_level(args.gnb_phy_com_log_level));
srslog::fetch_basic_logger(CHANNEL_LOG_NAME).set_level(srslog::basic_levels::error); srslog::fetch_basic_logger(CHANNEL_LOG_NAME).set_level(srslog::basic_levels::error);
if (not gnb_phy.init(args.gnb_phy, args.cell_list)) { if (not gnb_phy.init(args.gnb_phy, args.cell_list)) {
@ -101,11 +105,24 @@ public:
return; return;
} }
// Initialise radio
srsran::rf_args_t rf_args = {};
rf_args.nof_antennas = 1;
rf_args.nof_carriers = 1;
rf_args.srate_hz = args.srate_hz;
rf_args.log_level = args.ue_radio_log_level;
if (ue_radio.init(rf_args, &ue_phy) != SRSRAN_SUCCESS) {
return;
}
// Initialise UE PHY // Initialise UE PHY
if (not ue_phy.init(args.ue_phy, ue_phy_com, &ue_stack, 31)) { if (ue_phy.init(args.ue_phy, &ue_stack, &ue_radio) != SRSRAN_SUCCESS) {
return; return;
} }
// Wait for PHY to initialise
ue_phy.wait_initialize();
// Set UE configuration // Set UE configuration
if (not ue_phy.set_config(args.phy_cfg)) { if (not ue_phy.set_config(args.phy_cfg)) {
return; return;
@ -127,9 +144,41 @@ public:
initialised = true; initialised = true;
} }
srsue::rrc_interface_phy_nr::cell_select_result_t run_cell_select(const srsran_carrier_nr_t& carrier,
const srsran_ssb_cfg_t& ssb_cfg)
{
// Prepare return value
srsue::rrc_interface_phy_nr::cell_select_result_t ret = {};
// Prepare cell selection arguments
srsue::phy_interface_rrc_nr::cell_select_args_t cs_args = {};
cs_args.carrier = carrier;
cs_args.ssb_cfg = ssb_cfg;
// Start cell selection procedure
if (not ue_phy.start_cell_select(cs_args)) {
// Return unsuccessful cell select result
return {};
}
// Run test bench until the cell selection is completed
while (not ue_stack.get_cell_select_finished()) {
run_tti();
}
// It is now the right time to start scheduling
gnb_stack.start_scheduling();
// Reset slot counting
slot_count = 0;
return ue_stack.get_cell_select_result();
}
void stop() void stop()
{ {
ue_phy_com.stop(); ue_stack.stop();
ue_radio.stop();
gnb_phy_com.stop(); gnb_phy_com.stop();
gnb_phy.stop(); gnb_phy.stop();
ue_phy.stop(); ue_phy.stop();
@ -138,7 +187,11 @@ public:
~test_bench() = default; ~test_bench() = default;
bool is_initialised() const { return ue_stack.is_valid() and gnb_stack.is_valid() and initialised; } bool is_initialised()
{
return ue_stack.is_valid() and ue_radio.is_init() and ue_phy.is_initialized() and gnb_stack.is_valid() and
initialised;
}
bool run_tti() bool run_tti()
{ {
@ -149,16 +202,19 @@ public:
} }
// Feed gNb the UE transmitted signal // Feed gNb the UE transmitted signal
srsran::rf_timestamp_t gnb_time = {}; std::vector<cf_t*> gnb_rx_buffers(1);
std::vector<cf_t*> gnb_rx_buffers(1);
gnb_rx_buffers[0] = gnb_worker->get_buffer_rx(0); gnb_rx_buffers[0] = gnb_worker->get_buffer_rx(0);
ue_phy_com.read(gnb_rx_buffers, sf_sz, gnb_time); ue_radio.read_tx(gnb_rx_buffers.data(), sf_sz);
// Run the UL channel simulator
ul_channel.run(gnb_rx_buffers.data(), gnb_rx_buffers.data(), (uint32_t)sf_sz, gnb_rx_time.get(0));
// Set gNb time // Set gNb TX time
srsran::rf_timestamp_t gnb_time = gnb_rx_time;
gnb_time.add(TX_ENB_DELAY * 1e-3); gnb_time.add(TX_ENB_DELAY * 1e-3);
// Run the UL channel simulator // Advance gNb Rx time
ul_channel.run(gnb_rx_buffers.data(), gnb_rx_buffers.data(), (uint32_t)sf_sz, gnb_time.get(0)); gnb_rx_time.add(1e-3);
// Set gNb context // Set gNb context
srsran::phy_common_interface::worker_context_t gnb_context; srsran::phy_common_interface::worker_context_t gnb_context;
@ -172,41 +228,26 @@ public:
gnb_phy_com.push_semaphore(gnb_worker); gnb_phy_com.push_semaphore(gnb_worker);
gnb_phy.start_worker(gnb_worker); gnb_phy.start_worker(gnb_worker);
// Get UE worker
srsue::nr::sf_worker* ue_worker = ue_phy.wait_worker(slot_idx);
if (ue_worker == nullptr) {
return false;
}
// Feed UE the gNb transmitted signal // Feed UE the gNb transmitted signal
srsran::rf_timestamp_t ue_time = {}; srsran::rf_timestamp_t ue_time = {};
std::vector<cf_t*> ue_rx_buffers(1); std::vector<cf_t*> ue_rx_buffers(1);
ue_rx_buffers[0] = ue_worker->get_buffer(0, 0); ue_rx_buffers[0] = rf_buffer.get(0);
gnb_phy_com.read(ue_rx_buffers, sf_sz, ue_time); gnb_phy_com.read(ue_rx_buffers, sf_sz, ue_time);
// Set UE time
ue_time.add(TX_ENB_DELAY * 1e-3);
// Run the DL channel simulator // Run the DL channel simulator
dl_channel.run(ue_rx_buffers.data(), ue_rx_buffers.data(), (uint32_t)sf_sz, ue_time.get(0)); dl_channel.run(ue_rx_buffers.data(), ue_rx_buffers.data(), (uint32_t)sf_sz, ue_time.get(0));
// Set UE context // Write signal in UE radio buffer, this triggers UE to work
srsran::phy_common_interface::worker_context_t ue_context; ue_radio.write_rx(ue_rx_buffers.data(), sf_sz);
ue_context.sf_idx = slot_idx;
ue_context.worker_ptr = ue_worker;
ue_context.last = true; // Set last if standalone
ue_context.tx_time.copy(gnb_time);
ue_worker->set_context(ue_context);
// Run UE stack // Throttle UE PHY by running stack tick
ue_stack.run_tti(slot_idx, 1); ue_stack.tick();
// Start UE work // Increment slot index, the slot index shall be continuous
ue_phy_com.push_semaphore(ue_worker); slot_idx = (slot_idx + 1) % (1024 * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz));
ue_phy.start_worker(ue_worker);
// Increment slot counter and determine end of execution
slot_count++; slot_count++;
slot_idx = slot_count % (1024 * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz));
return slot_count <= duration_slots; return slot_count <= duration_slots;
} }
@ -215,7 +256,7 @@ public:
metrics_t metrics = {}; metrics_t metrics = {};
metrics.gnb_stack = gnb_stack.get_metrics(); metrics.gnb_stack = gnb_stack.get_metrics();
metrics.ue_stack = ue_stack.get_metrics(); metrics.ue_stack = ue_stack.get_metrics();
ue_phy.get_metrics(metrics.ue_phy); // get the metrics from the ue_phy ue_phy.get_metrics(srsran::srsran_rat_t::nr, &metrics.ue_phy); // get the metrics from the ue_phy
return metrics; return metrics;
} }
}; };

Loading…
Cancel
Save