From fb17e3326f03b538e82fb821dedf0723cbe5c444 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 15 Oct 2021 18:18:01 +0200 Subject: [PATCH] More development in UE NR SA cell search --- .../srsran/interfaces/ue_nr_interfaces.h | 6 +- srsue/hdr/phy/nr/slot_sync.h | 2 +- srsue/hdr/phy/nr/sync_sa.h | 5 +- srsue/hdr/phy/phy_nr_sa.h | 17 +- srsue/src/phy/phy_nr_sa.cc | 43 +++- srsue/src/phy/sync_sa.cc | 17 +- srsue/src/phy/test/nr_sa_cell_search_test.cc | 231 ++++++++++++++++-- 7 files changed, 284 insertions(+), 37 deletions(-) diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index 72fc30f36..c8bc7eafb 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -296,7 +296,11 @@ public: * @brief Describes cell search arguments */ struct cell_search_args_t { - // TBD + double center_freq_hz; + double ssb_freq_hz; + srsran_subcarrier_spacing_t ssb_scs; + srsran_ssb_patern_t ssb_pattern; + srsran_duplex_mode_t duplex_mode; }; /** diff --git a/srsue/hdr/phy/nr/slot_sync.h b/srsue/hdr/phy/nr/slot_sync.h index 72baad84a..631ccb84e 100644 --- a/srsue/hdr/phy/nr/slot_sync.h +++ b/srsue/hdr/phy/nr/slot_sync.h @@ -49,8 +49,8 @@ private: srslog::basic_logger& logger; stack_interface_phy_sa_nr& stack; srsran::radio_interface_phy& radio; - srsran_ue_sync_nr_t ue_sync_nr = {}; srsran::rf_timestamp_t last_rx_time; + srsran_ue_sync_nr_t ue_sync_nr = {}; srsran_timestamp_t stack_tti_ts_new = {}; srsran_timestamp_t stack_tti_ts = {}; bool forced_rx_time_init = true; // Rx time sync after first receive from radio diff --git a/srsue/hdr/phy/nr/sync_sa.h b/srsue/hdr/phy/nr/sync_sa.h index de11bbee2..53ee88113 100644 --- a/srsue/hdr/phy/nr/sync_sa.h +++ b/srsue/hdr/phy/nr/sync_sa.h @@ -81,6 +81,8 @@ public: void stop(); + state_t get_state() const; + void worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; private: @@ -91,11 +93,12 @@ private: state_t state = STATE_IDLE; state_t next_state = STATE_IDLE; - std::mutex state_mutex; + mutable std::mutex state_mutex; std::condition_variable state_cvar; std::atomic running = {false}; uint32_t sf_sz = 0; ///< Subframe size (1-ms) srsran::tti_semaphore tti_semaphore; + srsran_slot_cfg_t slot_cfg = {}; cell_search searcher; slot_sync slot_synchronizer; diff --git a/srsue/hdr/phy/phy_nr_sa.h b/srsue/hdr/phy/phy_nr_sa.h index 4b45f919e..8eb6e10e0 100644 --- a/srsue/hdr/phy/phy_nr_sa.h +++ b/srsue/hdr/phy/phy_nr_sa.h @@ -27,17 +27,11 @@ public: struct args_t { std::string log_level = "info"; ///< General PHY logging level double srate_hz = 61.44e6; ///< Sampling rate in Hz - - // Frequency allocation parameters - uint32_t pointA_arfcn = 0; ///< Resource grid PointA ARFCN - float pointA_Hz = 0; ///< Resource grid PointA frequency in Hz. Overrides pointA_arfcn if valid - uint32_t ssb_arfcn = 0; ///< SS/PBCH block center point ARFCN - float ssb_Hz = 0; ///< SS/PBCH block center point ARFCN. Overrides ssb_arfcn if valid }; phy_nr_sa(stack_interface_phy_sa_nr& stack_, srsran::radio_interface_phy& radio_); - bool init(const args_t& args); + bool init(const args_t& args_); int set_ul_grant(uint32_t rar_slot_idx, std::array packed_ul_grant, @@ -56,15 +50,18 @@ public: void clear_pending_grants() override {} bool set_config(const srsran::phy_cfg_nr_t& cfg) override { return false; } - phy_nr_sa_state_t get_state() const override { return PHY_NR_SA_STATE_CELL_SELECT; } - void reset() override {} - bool start_cell_search(const cell_search_args_t& req) override { return false; } + phy_nr_sa_state_t get_state() const override; + void reset() override; + bool start_cell_search(const cell_search_args_t& req) override; bool start_cell_select(const cell_search_args_t& req) override { return false; } + void stop() { sync.stop(); } + private: nr::worker_pool workers; nr::sync_sa sync; srslog::basic_logger& logger; + args_t args; }; } // namespace srsue diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc index 085bd0185..0bf6f6f8a 100644 --- a/srsue/src/phy/phy_nr_sa.cc +++ b/srsue/src/phy/phy_nr_sa.cc @@ -14,13 +14,13 @@ namespace srsue { phy_nr_sa::phy_nr_sa(stack_interface_phy_sa_nr& stack_, srsran::radio_interface_phy& radio_) : - logger(srslog::fetch_basic_logger("PHY-NR")), - sync(stack_, radio_, workers), - workers(4) + logger(srslog::fetch_basic_logger("PHY-NR")), sync(stack_, radio_, workers), workers(4) {} -bool phy_nr_sa::init(const args_t& args) +bool phy_nr_sa::init(const args_t& args_) { + args = args_; + nr::sync_sa::args_t sync_args = {}; sync_args.srate_hz = args.srate_hz; if (not sync.init(sync_args)) { @@ -31,4 +31,39 @@ bool phy_nr_sa::init(const args_t& args) return true; } +phy_interface_rrc_sa_nr::phy_nr_sa_state_t phy_nr_sa::get_state() const +{ + { + switch (sync.get_state()) { + case nr::sync_sa::STATE_IDLE: + break; + case nr::sync_sa::STATE_CELL_SEARCH: + return phy_interface_rrc_sa_nr::PHY_NR_SA_STATE_CELL_SEARCH; + case nr::sync_sa::STATE_CELL_SELECT: + return phy_interface_rrc_sa_nr::PHY_NR_SA_STATE_CELL_SELECT; + } + } + return phy_interface_rrc_sa_nr::PHY_NR_SA_STATE_IDLE; +} + +void phy_nr_sa::reset() +{ + sync.go_idle(); +} + +bool phy_nr_sa::start_cell_search(const cell_search_args_t& req) +{ + // Prepare cell search configuration from the request + nr::cell_search::cfg_t cfg = {}; + cfg.srate_hz = args.srate_hz; + cfg.center_freq_hz = req.center_freq_hz; + cfg.ssb_freq_hz = req.ssb_freq_hz; + cfg.ssb_scs = req.ssb_scs; + cfg.ssb_pattern = req.ssb_pattern; + cfg.duplex_mode = req.duplex_mode; + + // Request cell search to lower synchronization instance + return sync.start_cell_search(cfg); +} + } // namespace srsue \ No newline at end of file diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc index 06fc532b3..78ee1f21e 100644 --- a/srsue/src/phy/sync_sa.cc +++ b/srsue/src/phy/sync_sa.cc @@ -59,7 +59,7 @@ bool sync_sa::start_cell_search(const cell_search::cfg_t& cfg) return false; } - // Configure searcher without locking state + // Configure searcher without locking state for avoiding stalling the Rx stream lock.unlock(); if (not searcher.start(cfg)) { logger.error("Sync: failed to start cell search"); @@ -70,11 +70,6 @@ bool sync_sa::start_cell_search(const cell_search::cfg_t& cfg) // Transition to search next_state = STATE_CELL_SEARCH; - // Wait for state transition - while (state != STATE_CELL_SEARCH) { - state_cvar.wait(lock); - } - return true; } @@ -102,6 +97,13 @@ bool sync_sa::go_idle() void sync_sa::stop() { running = false; + wait_thread_finish(); +} + +sync_sa::state_t sync_sa::get_state() const +{ + std::unique_lock lock(state_mutex); + return state; } void sync_sa::run_state_idle() @@ -111,7 +113,10 @@ void sync_sa::run_state_idle() srsran::rf_timestamp_t ts = {}; + // Receives from radio 1 slot radio.rx_now(rf_buffer, ts); + + stack.run_tti(slot_cfg.idx); } void sync_sa::run_state_cell_search() 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 ef7f7759b..02f10cb18 100644 --- a/srsue/src/phy/test/nr_sa_cell_search_test.cc +++ b/srsue/src/phy/test/nr_sa_cell_search_test.cc @@ -10,6 +10,7 @@ * */ +#include "srsran/common/band_helper.h" #include "srsue/hdr/phy/phy_nr_sa.h" struct test_args_t {}; @@ -23,20 +24,57 @@ private: srsran_ssb_t ssb = {}; srsran_ringbuffer_t ringbuffer = {}; cf_t* buffer = nullptr; + uint32_t slot_idx = 0; + srsran_carrier_nr_t carrier = {}; + std::atomic running = {true}; srslog::basic_logger& logger; + void run_async_slot() + { + // Early return if not running + if (not running) { + return; + } + + // Zero slot buffer + srsran_vec_cf_zero(buffer, sf_len); + + if (srsran_ssb_send(&ssb, slot_idx)) { + // Create MIB for this slot + srsran_mib_nr_t mib = {}; + + // Create PBCH message from packed MIB + srsran_pbch_msg_nr_t pbch_msg = {}; + srsran_assert(srsran_pbch_msg_nr_mib_pack(&mib, &pbch_msg) >= SRSRAN_SUCCESS, + "Error packing MIB into PBCH message"); + + // Encode SSB signal and add it to the baseband + srsran_assert(srsran_ssb_add(&ssb, carrier.pci, &pbch_msg, buffer, buffer) >= SRSRAN_SUCCESS, + "Error adding SSB signal"); + } + slot_idx++; + + // Write slot samples in ringbuffer + srsran_assert(srsran_ringbuffer_write(&ringbuffer, buffer, (int)sizeof(cf_t) * sf_len) > SRSRAN_SUCCESS, + "Error writing in ringbuffer"); + } + public: struct args_t { double srate_hz; srsran_carrier_nr_t carrier; srsran_subcarrier_spacing_t ssb_scs; srsran_ssb_patern_t ssb_pattern; - srsran_ssb_patern_t ssb_periodicity_ms; - srsran_duplex_config_nr_t duplex; + uint32_t ssb_periodicity_ms; + srsran_duplex_mode_t duplex_mode; }; gnb_emulator(const args_t& args) : logger(srslog::fetch_basic_logger(LOGNAME)) { - sf_len = args.srate_hz / 1000; + srsran_assert( + std::isnormal(args.srate_hz) and args.srate_hz > 0, "Invalid sampling rate (%.2f MHz)", args.srate_hz); + + sf_len = args.srate_hz / 1000; + carrier = args.carrier; srsran_ssb_args_t ssb_args = {}; ssb_args.enable_encode = true; @@ -48,33 +86,50 @@ public: ssb_cfg.ssb_freq_hz = args.carrier.ssb_center_freq_hz; ssb_cfg.scs = args.ssb_scs; ssb_cfg.pattern = args.ssb_pattern; - ssb_cfg.duplex_mode = args.duplex.mode; + ssb_cfg.duplex_mode = args.duplex_mode; ssb_cfg.periodicity_ms = args.ssb_periodicity_ms; srsran_assert(srsran_ssb_set_cfg(&ssb, &ssb_cfg) == SRSRAN_SUCCESS, "SSB set config failed"); - srsran_assert(srsran_ringbuffer_init(&ringbuffer, sizeof(cf_t) * BUFFER_SIZE_SF * sf_len), + srsran_assert(srsran_ringbuffer_init(&ringbuffer, sizeof(cf_t) * BUFFER_SIZE_SF * sf_len) >= SRSRAN_SUCCESS, "Ringbuffer initialisation failed"); - buffer = srsran_vec_cf_malloc(sf_len); + buffer = srsran_vec_cf_malloc(BUFFER_SIZE_SF * sf_len); } ~gnb_emulator() { srsran_ssb_free(&ssb); srsran_ringbuffer_free(&ringbuffer); + if (buffer != nullptr) { + free(buffer); + } } void tx_end() override {} - bool tx(srsran::rf_buffer_interface& buffer, const srsran::rf_timestamp_interface& tx_time) override { return false; } - bool rx_now(srsran::rf_buffer_interface& buffer, srsran::rf_timestamp_interface& rxd_time) override + bool tx(srsran::rf_buffer_interface& tx_buffer, const srsran::rf_timestamp_interface& tx_time) override { - uint32_t nbytes = sizeof(cf_t) * buffer.get_nof_samples(); + return false; + } + bool rx_now(srsran::rf_buffer_interface& rx_buffer, srsran::rf_timestamp_interface& rxd_time) override + { + int nbytes = (int)(sizeof(cf_t) * rx_buffer.get_nof_samples()); + cf_t* temp_buffer = rx_buffer.get(0); + + // If the buffer is invalid, use internal temporal buffer + if (temp_buffer == nullptr) { + temp_buffer = buffer; + } // As long as there are not enough samples - while (srsran_ringbuffer_status(&ringbuffer) < nbytes) { - if (srsran_ringbuffer_write(&ringbuffer, ) < SRSRAN_SUCCESS) { - logger return false; - } + while (srsran_ringbuffer_status(&ringbuffer) < nbytes and running) { + run_async_slot(); } + if (not running) { + return true; + } + + srsran_assert(srsran_ringbuffer_read(&ringbuffer, temp_buffer, nbytes) >= SRSRAN_SUCCESS, + "Error reading from ringbuffer"); + return true; } void set_tx_freq(const uint32_t& carrier_idx, const double& freq) override {} @@ -91,11 +146,159 @@ public: bool is_continuous_tx() override { return false; } bool get_is_start_of_burst() override { return false; } bool is_init() override { return false; } - void reset() override {} + void reset() override { running = false; } srsran_rf_info_t* get_info() override { return nullptr; } }; +class dummy_stack : public srsue::stack_interface_phy_sa_nr +{ +private: + srslog::basic_logger& logger = srslog::fetch_basic_logger("STACK"); + bool pending_tti = false; + std::mutex pending_tti_mutex; + std::condition_variable pending_tti_cvar; + std::atomic running = {true}; + +public: + dummy_stack() { logger.set_level(srslog::str_to_basic_level("info")); } + void in_sync() override {} + void out_of_sync() override {} + void run_tti(const uint32_t tti) override + { + logger.debug("Run TTI %d", tti); + + // Wait for tick + std::unique_lock lock(pending_tti_mutex); + while (not pending_tti and running) { + pending_tti_cvar.wait(lock); + } + + // Let the tick proceed + pending_tti = false; + pending_tti_cvar.notify_all(); + } + void cell_search_found_cell(const cell_search_result_t& result) override + { + std::array csi_info = {}; + + srsran_csi_meas_info(&result.measurements, csi_info.data(), (uint32_t)csi_info.size()); + + logger.info("Cell found pci=%d barred=%c intra_freq=%c %s", + result.pci, + result.barred ? 'y' : 'n', + result.intra_freq_meas ? 'y' : 'n', + csi_info.data()); + } + int sf_indication(const uint32_t tti) override + { + logger.info("SF %d indication", tti); + return 0; + } + sched_rnti_t get_dl_sched_rnti_nr(const uint32_t tti) override { return sched_rnti_t(); } + sched_rnti_t get_ul_sched_rnti_nr(const uint32_t tti) override { return sched_rnti_t(); } + void new_grant_dl(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_t* action) override {} + void tb_decoded(const uint32_t cc_idx, const mac_nr_grant_dl_t& grant, tb_action_dl_result_t result) override {} + void new_grant_ul(const uint32_t cc_idx, const mac_nr_grant_ul_t& grant, tb_action_ul_t* action) override {} + void prach_sent(uint32_t tti, uint32_t s_id, uint32_t t_id, uint32_t f_id, uint32_t ul_carrier_id) override {} + bool sr_opportunity(uint32_t tti, uint32_t sr_id, bool meas_gap, bool ul_sch_tx) override { return false; } + + void tick() + { + // Wait for TTI to get processed + std::unique_lock lock(pending_tti_mutex); + while (pending_tti) { + pending_tti_cvar.wait(lock); + } + + // Let the TTI proceed + pending_tti = true; + pending_tti_cvar.notify_all(); + } + + void stop() + { + running = false; + pending_tti_cvar.notify_all(); + } +}; + +struct args_t { + double srate_hz = 11.52e6; + srsran_carrier_nr_t carrier = SRSRAN_DEFAULT_CARRIER_NR; + srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; + uint32_t ssb_periodicity_ms = 10; + srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_15kHz; + srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + uint32_t duration_ms = 1000; + + void set_ssb_from_band(uint16_t band) + { + srsran::srsran_band_helper bands; + duplex_mode = bands.get_duplex_mode(band); + ssb_scs = bands.get_ssb_scs(band); + ssb_pattern = bands.get_ssb_pattern(band, ssb_scs); + carrier.ssb_center_freq_hz = bands.get_ssb_center_freq(carrier); + } +}; + int main(int argc, char** argv) { + // Parse Test arguments + args_t args; + + // Initialise logging infrastructure + srslog::init(); + + // Radio can be constructed from different options + std::shared_ptr radio = nullptr; + + // Create Radio as gNb emulator + gnb_emulator::args_t gnb_args = {}; + gnb_args.srate_hz = args.srate_hz; + gnb_args.carrier = args.carrier; + gnb_args.ssb_pattern = args.ssb_pattern; + gnb_args.ssb_periodicity_ms = args.ssb_periodicity_ms; + gnb_args.duplex_mode = args.duplex_mode; + radio = std::make_shared(gnb_args); + + // Create stack + dummy_stack stack; + + // Create UE PHY + srsue::phy_nr_sa phy(stack, *radio); + + // Initialise PHY, it will instantly start free running + srsue::phy_nr_sa::args_t phy_args = {}; + phy_args.srate_hz = args.srate_hz; + phy.init(phy_args); + + // Transition PHY to cell search + srsue::phy_nr_sa::cell_search_args_t cell_search_req = {}; + cell_search_req.center_freq_hz = args.carrier.dl_center_frequency_hz; + cell_search_req.ssb_freq_hz = args.carrier.ssb_center_freq_hz; + cell_search_req.ssb_scs = args.ssb_scs; + cell_search_req.ssb_pattern = args.ssb_pattern; + cell_search_req.duplex_mode = args.duplex_mode; + phy.start_cell_search(cell_search_req); + + for (uint32_t i = 0; i < args.duration_ms; i++) { + stack.tick(); + } + + // First transition PHY to IDLE + phy.reset(); + + // Make sure PHY transitioned to IDLE + // ... + + // Stop stack, it will let PHY free run + stack.stop(); + + // Stop PHY + phy.stop(); + + // Stop Radio + radio->reset(); + return 0; } \ No newline at end of file