mirror of https://github.com/PentHertz/srsLTE.git
Created dummy radio and moved UE dummy PHY into real UE SA PHY
This commit is contained in:
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,12 +107,30 @@ 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;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
void slot_sync::run_stack_tti()
|
void slot_sync::run_stack_tti()
|
||||||
{ // check timestamp reset
|
{ // check timestamp reset
|
||||||
if (forced_rx_time_init || srsran_timestamp_iszero(&stack_tti_ts) ||
|
if (forced_rx_time_init || srsran_timestamp_iszero(&stack_tti_ts) ||
|
||||||
|
|
|
@ -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 UE PHY
|
// Initialise radio
|
||||||
if (not ue_phy.init(args.ue_phy, ue_phy_com, &ue_stack, 31)) {
|
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;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialise UE PHY
|
||||||
|
if (ue_phy.init(args.ue_phy, &ue_stack, &ue_radio) != SRSRAN_SUCCESS) {
|
||||||
|
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);
|
||||||
|
|
||||||
// Set gNb time
|
|
||||||
gnb_time.add(TX_ENB_DELAY * 1e-3);
|
|
||||||
|
|
||||||
// Run the UL channel simulator
|
// Run the UL channel simulator
|
||||||
ul_channel.run(gnb_rx_buffers.data(), gnb_rx_buffers.data(), (uint32_t)sf_sz, gnb_time.get(0));
|
ul_channel.run(gnb_rx_buffers.data(), gnb_rx_buffers.data(), (uint32_t)sf_sz, gnb_rx_time.get(0));
|
||||||
|
|
||||||
|
// Set gNb TX time
|
||||||
|
srsran::rf_timestamp_t gnb_time = gnb_rx_time;
|
||||||
|
gnb_time.add(TX_ENB_DELAY * 1e-3);
|
||||||
|
|
||||||
|
// Advance gNb Rx time
|
||||||
|
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…
Reference in New Issue