diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index 4c7502e9e..45603cb62 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -44,6 +44,19 @@ public: * @param result Cell search result completion */ 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 @@ -182,23 +195,25 @@ public: }; struct phy_args_nr_t { - uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill - uint32_t nof_carriers = 1; - uint32_t max_nof_prb = 106; - double srate_hz = 23.04e6; - uint32_t nof_phy_threads = 3; - uint32_t worker_cpu_mask = 0; - srsran::phy_log_args_t log = {}; - srsran_ue_dl_nr_args_t dl = {}; - srsran_ue_ul_nr_args_t ul = {}; - std::set fixed_sr = {1}; - uint32_t fix_wideband_cqi = 15; ///< Set to a non-zero value for fixing the wide-band CQI report - bool store_pdsch_ko = false; - float trs_epre_ema_alpha = 0.1f; ///< EPRE measurement exponential average alpha - float trs_rsrp_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha - 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 + uint32_t rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill + uint32_t nof_carriers = 1; + uint32_t max_nof_prb = 106; + double srate_hz = 23.04e6; + uint32_t nof_phy_threads = 3; + uint32_t worker_cpu_mask = 0; + int slot_recv_thread_prio = 0; /// Specifies the slot receive thread priority, RT by default + int workers_thread_prio = 2; /// Specifies the workers thread priority, RT by default + srsran::phy_log_args_t log = {}; + srsran_ue_dl_nr_args_t dl = {}; + srsran_ue_ul_nr_args_t ul = {}; + std::set fixed_sr = {1}; + uint32_t fix_wideband_cqi = 15; ///< Set to a non-zero value for fixing the wide-band CQI report + bool store_pdsch_ko = false; + float trs_epre_ema_alpha = 0.1f; ///< EPRE measurement exponential average alpha + float trs_rsrp_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha + 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() { diff --git a/lib/include/srsran/radio/radio_dummy.h b/lib/include/srsran/radio/radio_dummy.h new file mode 100644 index 000000000..e5a9decb6 --- /dev/null +++ b/lib/include/srsran/radio/radio_dummy.h @@ -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 +#include +#include + +#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 rx_ring_buffers; + std::vector tx_ring_buffers; + std::mutex tx_mutex; + double srate_hz = 0.0f; + std::atomic rx_gain = {1.0f}; + std::atomic 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 is_initialised = {false}; + std::atomic quit = {false}; + + void write_ring_buffers(std::vector& 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& 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& buffers, uint32_t nsamples) + { + uint32_t n = SRSRAN_MIN(nsamples, TEMP_BUFFER_SZ); + srsran_vec_cf_zero(temp_buffer, n); + + std::array 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 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 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 diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index d96beacbd..495d5fdb9 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -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 = (rsrp_pss + rsrp_sss) / 2.0f; - // avoid taking log of 0 (NaN) - if (rsrp == 0.0) { - rsrp = 1.0; - } - - // Compute Noise - float n0_pss = 1e-9; // Almost 0 - float n0_sss = 1e-9; // Almost 0 - if (epre_pss > rsrp_pss) { + // Avoid taking log of 0 or another abnormal value + if (!isnormal(rsrp)) { + rsrp = 1e-9f; + } + + // Estimate Noise: + // - Infinite (1e9), if the EPRE or RSRP is zero + // - EPRE-RSRP if EPRE > RSRP + // - 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; } - 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; } 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) float avg_pwr_corr = srsran_vec_avg_power_cf(&q->tmp_time[peak_idx], q->symbol_sz); if (!isnormal(avg_pwr_corr)) { + // Advance time + t_offset += q->corr_window; continue; } diff --git a/lib/src/phy/ue/ue_sync_nr.c b/lib/src/phy/ue/ue_sync_nr.c index 5972ab5c0..22520e55a 100644 --- a/lib/src/phy/ue/ue_sync_nr.c +++ b/lib/src/phy/ue/ue_sync_nr.c @@ -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 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) { // 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) { diff --git a/srsue/hdr/phy/nr/slot_sync.h b/srsue/hdr/phy/nr/slot_sync.h index b3b519ffb..f97cced37 100644 --- a/srsue/hdr/phy/nr/slot_sync.h +++ b/srsue/hdr/phy/nr/slot_sync.h @@ -43,14 +43,14 @@ public: int recv_callback(srsran::rf_buffer_t& rf_buffer, srsran_timestamp_t* timestamp); bool run_sfn_sync(); + bool run_camping(srsran::rf_buffer_t& buffer, srsran::rf_timestamp_t& timestamp); void run_stack_tti(); srsran_slot_cfg_t get_slot_cfg(); private: - 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 - enum { SEARCHING = 0, CAMPING } state = SEARCHING; + 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 srslog::basic_logger& logger; stack_interface_phy_nr* stack = nullptr; srsran::radio_interface_phy* radio = nullptr; diff --git a/srsue/hdr/phy/nr/sync_sa.h b/srsue/hdr/phy/nr/sync_sa.h index eacbcaaaf..a6303791b 100644 --- a/srsue/hdr/phy/nr/sync_sa.h +++ b/srsue/hdr/phy/nr/sync_sa.h @@ -73,9 +73,9 @@ public: sync_state::state_t get_state(); // The following methods control the SYNC state machine - void cell_go_idle(); - 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); + void cell_go_idle(); + cell_search::ret_t cell_search_run(const cell_search::cfg_t& cfg); + 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; @@ -92,6 +92,7 @@ private: std::atomic running = {false}; cf_t* rx_buffer = nullptr; + double srate_hz = 0; ///< Sampling rate in Hz uint32_t slot_sz = 0; ///< Subframe size (1-ms) uint32_t tti = 0; srsran::tti_semaphore tti_semaphore; diff --git a/srsue/hdr/phy/nr/worker_pool.h b/srsue/hdr/phy/nr/worker_pool.h index 1d54cf1f8..d1d673545 100644 --- a/srsue/hdr/phy/nr/worker_pool.h +++ b/srsue/hdr/phy/nr/worker_pool.h @@ -41,7 +41,7 @@ public: sf_worker* operator[](std::size_t pos) { return workers.at(pos).get(); } 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); void start_worker(sf_worker* w); void stop(); diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index 391e3d5b0..25f3e6ca9 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -134,14 +134,14 @@ public: /********** NR INTERFACE ********************/ 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; - void send_prach(const uint32_t prach_occasion, - const int preamble_index, - const float preamble_received_target_power, - const float ta_base_sec = 0.0f) final; + bool set_config(const srsran::phy_cfg_nr_t& cfg) final; + void send_prach(const uint32_t prach_occasion, + const int preamble_index, + const float preamble_received_target_power, + const float ta_base_sec = 0.0f) final; void set_earfcn(std::vector earfcns); - bool has_valid_sr_resource(uint32_t sr_id) final; - void clear_pending_grants() final; + bool has_valid_sr_resource(uint32_t sr_id) final; + void clear_pending_grants() final; int set_rar_grant(uint32_t rar_slot_idx, std::array packed_ul_grant, uint16_t rnti, diff --git a/srsue/hdr/phy/phy_nr_sa.h b/srsue/hdr/phy/phy_nr_sa.h index 6f5532968..dc66ca806 100644 --- a/srsue/hdr/phy/phy_nr_sa.h +++ b/srsue/hdr/phy/phy_nr_sa.h @@ -59,15 +59,15 @@ public: bool start_cell_search(const cell_search_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); private: srslog::basic_logger& logger; srslog::basic_logger& logger_phy_lib; - nr::worker_pool workers; - nr::sync_sa sync; + nr::worker_pool workers; + nr::sync_sa sync; srsran::phy_cfg_nr_t config_nr = {}; phy_args_nr_t args = {}; @@ -76,7 +76,7 @@ private: srsran::radio_interface_phy* radio = nullptr; stack_interface_phy_nr* stack = nullptr; - std::atomic is_configured = {false}; + std::atomic is_configured = {false}; // 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 @@ -86,10 +86,8 @@ private: void init_background(); std::thread init_thread; - const static int SF_RECV_THREAD_PRIO = 0; - const static int WORKERS_THREAD_PRIO = 2; - const static int MAX_WORKERS = 4; - 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); bool check_args(const phy_args_nr_t& args); diff --git a/srsue/src/phy/nr/cc_worker.cc b/srsue/src/phy/nr/cc_worker.cc index 7b1f81663..ca2e585f5 100644 --- a/srsue/src/phy/nr/cc_worker.cc +++ b/srsue/src/phy/nr/cc_worker.cc @@ -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 = {}; ssb_args.enable_measure = true; ssb_args.enable_decode = true; + ssb_args.enable_search = true; if (srsran_ssb_init(&ssb, &ssb_args) < SRSRAN_SUCCESS) { ERROR("Error initiating SSB"); return; @@ -158,7 +159,7 @@ void cc_worker::decode_pdcch_dl() if (logger.debug.enabled()) { // 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 coreset_str; srsran_coreset_to_str(coreset, coreset_str.data(), coreset_str.size()); 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() { + 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 if (srsran_ssb_send(&ssb, dl_slot_cfg.idx)) { srsran_csi_trs_measurements_t meas = {}; diff --git a/srsue/src/phy/nr/slot_sync.cc b/srsue/src/phy/nr/slot_sync.cc index dbfcd2074..35fc2cc02 100644 --- a/srsue/src/phy/nr/slot_sync.cc +++ b/srsue/src/phy/nr/slot_sync.cc @@ -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); // Run stack if the sync state is not in camping - if (state == SEARCHING) { - logger.debug("run_stack_tti: from recv"); - run_stack_tti(); - } + logger.debug("run_stack_tti: from recv"); + run_stack_tti(); 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) { - 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; } diff --git a/srsue/src/phy/nr/worker_pool.cc b/srsue/src/phy/nr/worker_pool.cc index eded6c277..61c179b92 100644 --- a/srsue/src/phy/nr/worker_pool.cc +++ b/srsue/src/phy/nr/worker_pool.cc @@ -17,10 +17,7 @@ namespace nr { 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, - srsran::phy_common_interface& common, - stack_interface_phy_nr* stack_, - int prio) +bool worker_pool::init(const phy_args_nr_t& args, srsran::phy_common_interface& common, stack_interface_phy_nr* stack_) { phy_state.stack = stack_; phy_state.args = args; @@ -59,7 +56,7 @@ bool worker_pool::init(const phy_args_nr_t& args, std::lock_guard lock(cfg_mutex); 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(w)); } diff --git a/srsue/src/phy/phy.cc b/srsue/src/phy/phy.cc index a71c39aca..50b30990f 100644 --- a/srsue/src/phy/phy.cc +++ b/srsue/src/phy/phy.cc @@ -323,7 +323,6 @@ bool phy::cell_select(phy_cell_t cell) // Indicate workers that cell selection has finished common.cell_is_selecting = false; - }); return true; } 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_) { stack_nr = stack_; - if (!nr_workers.init(args_, common, stack_, WORKERS_THREAD_PRIO)) { + if (!nr_workers.init(args_, common, stack_)) { return SRSRAN_ERROR; } diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc index 65738635c..c1e686b1c 100644 --- a/srsue/src/phy/phy_nr_sa.cc +++ b/srsue/src/phy/phy_nr_sa.cc @@ -96,11 +96,12 @@ void phy_nr_sa::init_background() { nr::sync_sa::args_t sync_args = {}; sync_args.srate_hz = args.srate_hz; + sync_args.thread_priority = args.slot_recv_thread_prio; if (not sync.init(sync_args, stack, radio)) { logger.error("Error initialising SYNC"); return; } - workers.init(args, sync, stack, WORKERS_THREAD_PRIO); + workers.init(args, sync, stack); is_configured = true; } @@ -204,8 +205,8 @@ bool phy_nr_sa::start_cell_select(const cell_select_args_t& req) selected_cell = req.carrier; cmd_worker_cell.add_cmd([this, req]() { - // Request cell search to lower synchronization instance. - sync.cell_select_run(req); + // Request cell search to lower synchronization instance and push the result directly to the stack + stack->cell_select_completed(sync.cell_select_run(req)); }); return true; diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc index 8e3063798..17f45104a 100644 --- a/srsue/src/phy/sync_sa.cc +++ b/srsue/src/phy/sync_sa.cc @@ -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_) { - stack = stack_; - radio = radio_; - slot_sz = (uint32_t)(args.srate_hz / 1000.0f); + stack = stack_; + radio = radio_; + srate_hz = args.srate_hz; + slot_sz = (uint32_t)(args.srate_hz / 1000.0f); // Initialise cell search internal object 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; } -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 ul(rrc_mutex); // Wait the FSM to transition to IDLE if (!wait_idle()) { logger.error("Cell Search: SYNC thread didn't transition to IDLE after 100 ms\n"); - return false; + return {}; } 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 = {}; cfg.N_id = req.carrier.pci; cfg.ssb = req.ssb_cfg; + cfg.ssb.srate_hz = srate_hz; if (slot_synchronizer.set_sync_cfg(cfg)) { logger.error("Cell Search: Failed setting slot synchronizer configuration"); - return false; + return {}; } // SFN synchronization 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..."); } else { logger.info("Cell Select: Could not synchronize SFN"); } rrc_proc_state = PROC_IDLE; - return true; + return result; } 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...", tti / SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz)); - phy_state.state_exit(); + phy_state.state_exit(true); return; } @@ -262,8 +268,8 @@ void sync_sa::run_state_cell_camping() srsran::rf_buffer_t rf_buffer = {}; rf_buffer.set_nof_samples(slot_sz); rf_buffer.set(0, nr_worker->get_buffer(0, 0)); - if (not slot_synchronizer.recv_callback(rf_buffer, last_rx_time.get_ptr(0))) { - logger.error("SYNC: receiving from radio\n"); + if (not slot_synchronizer.run_camping(rf_buffer, last_rx_time)) { + logger.error("SYNC: detected out-of-sync... unhandled outcome..."); } srsran::phy_common_interface::worker_context_t context; @@ -303,12 +309,6 @@ void sync_sa::run_thread() run_state_cell_camping(); 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, diff --git a/srsue/src/phy/test/nr_sa_cell_search_test.cc b/srsue/src/phy/test/nr_sa_cell_search_test.cc index b9e698ff0..d80c526d0 100644 --- a/srsue/src/phy/test/nr_sa_cell_search_test.cc +++ b/srsue/src/phy/test/nr_sa_cell_search_test.cc @@ -20,51 +20,6 @@ #include #include -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 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 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 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 { // Generic parameters double srate_hz = 11.52e6; @@ -211,8 +166,8 @@ int parse_args(int argc, char** argv, args_t& args) class dummy_ue { private: - throttled_dummy_stack stack; - srsue::phy_nr_sa phy; + ue_dummy_stack stack; + srsue::phy_nr_sa phy; public: struct args_t { diff --git a/test/phy/dummy_gnb_stack.h b/test/phy/dummy_gnb_stack.h index d363ea9cd..f31637f6b 100644 --- a/test/phy/dummy_gnb_stack.h +++ b/test/phy/dummy_gnb_stack.h @@ -87,7 +87,9 @@ private: srsenb::rlc_dummy rlc_obj; std::unique_ptr mac; 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; metrics_t metrics = {}; @@ -166,7 +168,8 @@ private: 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; } @@ -241,7 +244,8 @@ private: 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; } @@ -353,7 +357,8 @@ public: phy_cfg(args.phy_cfg), ss_id(args.ss_id), 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)); sched_logger.set_level(srslog::str_to_basic_level(args.log_level)); @@ -372,14 +377,6 @@ public: std::vector cells_cfg = srsenb::get_default_cells_cfg(1, phy_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; ul.mcs = args.pusch.mcs; @@ -463,6 +460,19 @@ public: 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; } 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); - } 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 ul_sched.pucch.emplace_back(); diff --git a/test/phy/dummy_phy_common.h b/test/phy/dummy_phy_common.h index ca88931bf..09fe79cdd 100644 --- a/test/phy/dummy_phy_common.h +++ b/test/phy/dummy_phy_common.h @@ -146,9 +146,7 @@ public: uint32_t nof_channels = 1; args_t(double srate_hz_, uint32_t buffer_sz_ms_, uint32_t nof_channels_) : - srate_hz(srate_hz_), - buffer_sz_ms(buffer_sz_ms_), - nof_channels(nof_channels_) + srate_hz(srate_hz_), buffer_sz_ms(buffer_sz_ms_), nof_channels(nof_channels_) {} }; diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h index 6518e9e6c..8dd740dd9 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -66,10 +66,18 @@ private: metrics_t metrics = {}; 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 running = {true}; + dummy_tx_harq_entity tx_harq_proc; dummy_rx_harq_entity rx_harq_proc; - std::atomic cell_search_finished = {false}; + std::atomic cell_search_finished = {false}; + std::atomic cell_select_finished = {false}; + cell_select_result_t cell_select_result = {}; public: struct args_t { @@ -86,15 +94,19 @@ public: } ~ue_dummy_stack() { srsran_random_free(random_gen); } - virtual void wait_tti() - { - // Do nothing - } void in_sync() override {} void out_of_sync() 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 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 if (prach_period != 0) { @@ -107,6 +119,24 @@ public: } } } + void tick() + { + // Wait for TTI to get processed + std::unique_lock 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 { std::unique_lock lock(rnti_mutex); @@ -234,6 +264,23 @@ public: // Flag as cell search is done 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 diff --git a/test/phy/nr_phy_test.cc b/test/phy/nr_phy_test.cc index fe99edc1f..43b603911 100644 --- a/test/phy/nr_phy_test.cc +++ b/test/phy/nr_phy_test.cc @@ -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_phy("gNb PHY related 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_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(&ue_phy.log.id_preamble)->default_value(" UE/"), "UE PHY log ID preamble") ; + options_ue_rf.add_options() + ("ue.rf.log.level", bpo::value(&ue_radio_log_level)->default_value("warning"), "UE RF log level") + ; + options_ue_stack.add_options() ("ue.stack.sr.period", bpo::value(&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(&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; 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") ; // clang-format on @@ -173,6 +179,7 @@ test_bench::args_t::args_t(int argc, char** argv) // 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)); + ue_phy.srate_hz = srate_hz; cell_list.resize(1); 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; } + // Disable RT priority in the UE PHY + ue_phy.workers_thread_prio = -1; + ue_phy.slot_recv_thread_prio = -1; + // Flag configuration as valid valid = true; } @@ -216,6 +227,11 @@ int main(int argc, char** argv) // Assert bench is initialised correctly 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 while (tb.run_tti()) { ; // Do nothing diff --git a/test/phy/test_bench.h b/test/phy/test_bench.h index 71c18ef08..4097a1eae 100644 --- a/test/phy/test_bench.h +++ b/test/phy/test_bench.h @@ -14,9 +14,10 @@ #define SRSRAN_TEST_BENCH_H #include "dummy_phy_common.h" -#include "dummy_ue_phy.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/phy_nr_sa.h" class test_bench { @@ -31,10 +32,13 @@ private: srsenb::nr::worker_pool gnb_phy; phy_common gnb_phy_com; ue_dummy_stack ue_stack; - ue_dummy_phy ue_phy; - phy_common ue_phy_com; - bool initialised = false; - uint32_t sf_sz = 0; + srsue::phy_nr_sa ue_phy; + srsran::radio_dummy ue_radio; + srsran::rf_timestamp_t gnb_rx_time = {}; + + bool initialised = false; + uint32_t sf_sz = 0; + srsran::rf_buffer_t rf_buffer; // Channel simulator srsran::channel dl_channel; srsran::channel ul_channel; @@ -51,9 +55,10 @@ public: gnb_dummy_stack::args_t gnb_stack; srsue::phy_args_nr_t ue_phy; ue_dummy_stack::args_t ue_stack; - std::string phy_com_log_level = "info"; - std::string phy_lib_log_level = "none"; - uint64_t durations_slots = 100; + std::string gnb_phy_com_log_level = "info"; + std::string ue_radio_log_level = "info"; + std::string phy_lib_log_level = "none"; + uint64_t durations_slots = 100; // channel simulator args srsran::channel::args_t dl_channel; @@ -71,19 +76,18 @@ public: gnb_stack(args.gnb_stack), 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_phy("PHY", args.ue_phy.nof_phy_threads), - - 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)), + ue_phy("PHY"), + ue_radio(), 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)), sf_sz((uint32_t)std::round(args.srate_hz * 1e-3)), duration_slots(args.durations_slots), 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(GNB_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.gnb_phy_com_log_level)); srslog::fetch_basic_logger(CHANNEL_LOG_NAME).set_level(srslog::basic_levels::error); if (not gnb_phy.init(args.gnb_phy, args.cell_list)) { @@ -101,11 +105,24 @@ public: 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 - 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; } + // Wait for PHY to initialise + ue_phy.wait_initialize(); + // Set UE configuration if (not ue_phy.set_config(args.phy_cfg)) { return; @@ -127,9 +144,41 @@ public: 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() { - ue_phy_com.stop(); + ue_stack.stop(); + ue_radio.stop(); gnb_phy_com.stop(); gnb_phy.stop(); ue_phy.stop(); @@ -138,7 +187,11 @@ public: ~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() { @@ -149,16 +202,19 @@ public: } // Feed gNb the UE transmitted signal - srsran::rf_timestamp_t gnb_time = {}; - std::vector gnb_rx_buffers(1); + std::vector gnb_rx_buffers(1); 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); - // Run the UL channel simulator - ul_channel.run(gnb_rx_buffers.data(), gnb_rx_buffers.data(), (uint32_t)sf_sz, gnb_time.get(0)); + // Advance gNb Rx time + gnb_rx_time.add(1e-3); // Set 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.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 srsran::rf_timestamp_t ue_time = {}; std::vector 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); - // Set UE time - ue_time.add(TX_ENB_DELAY * 1e-3); - // Run the DL channel simulator dl_channel.run(ue_rx_buffers.data(), ue_rx_buffers.data(), (uint32_t)sf_sz, ue_time.get(0)); - // Set UE context - srsran::phy_common_interface::worker_context_t ue_context; - 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); + // Write signal in UE radio buffer, this triggers UE to work + ue_radio.write_rx(ue_rx_buffers.data(), sf_sz); - // Run UE stack - ue_stack.run_tti(slot_idx, 1); + // Throttle UE PHY by running stack tick + ue_stack.tick(); - // Start UE work - ue_phy_com.push_semaphore(ue_worker); - ue_phy.start_worker(ue_worker); + // Increment slot index, the slot index shall be continuous + slot_idx = (slot_idx + 1) % (1024 * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz)); + // Increment slot counter and determine end of execution slot_count++; - slot_idx = slot_count % (1024 * SRSRAN_NSLOTS_PER_FRAME_NR(srsran_subcarrier_spacing_15kHz)); return slot_count <= duration_slots; } @@ -215,7 +256,7 @@ public: metrics_t metrics = {}; metrics.gnb_stack = gnb_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; } };