From c71ab70b83816d93de6de050217d7f1f8efd86aa Mon Sep 17 00:00:00 2001 From: Ismael Gomez Date: Tue, 30 Nov 2021 11:22:29 +0100 Subject: [PATCH] Completed basic sync module with bypass camping. Removed ue_phy_nr and ue_phy_lte base classes --- .../srsran/interfaces/ue_nr_interfaces.h | 10 +- srsue/hdr/phy/nr/cell_search.h | 16 +- srsue/hdr/phy/nr/sync_sa.h | 50 ++-- srsue/hdr/phy/phy.h | 19 +- srsue/hdr/phy/phy_nr_sa.h | 18 +- srsue/hdr/phy/sync_state.h | 9 + srsue/hdr/phy/ue_lte_phy_base.h | 46 --- srsue/hdr/phy/ue_nr_phy_base.h | 45 --- srsue/src/phy/nr/cell_search.cc | 68 +---- srsue/src/phy/phy_nr_sa.cc | 88 ++++-- srsue/src/phy/sync_sa.cc | 271 ++++++++++++++---- srsue/src/phy/test/nr_sa_cell_search_test.cc | 2 + srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc | 4 +- srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h | 11 +- srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc | 23 +- test/phy/dummy_ue_stack.h | 52 ++-- 16 files changed, 391 insertions(+), 341 deletions(-) delete mode 100644 srsue/hdr/phy/ue_lte_phy_base.h delete mode 100644 srsue/hdr/phy/ue_nr_phy_base.h diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index bd5b3a3ac..d51630103 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -34,6 +34,7 @@ public: * @brief Describes a cell search result */ struct cell_search_result_t { + bool cell_found = false; uint32_t pci = 0; ///< Physical Cell Identifier srsran_pbch_msg_nr_t pbch_msg; ///< Packed PBCH message for the upper layers srsran_csi_trs_measurements_t measurements = {}; ///< Measurements from SSB block @@ -262,13 +263,14 @@ public: PHY_NR_STATE_IDLE = 0, ///< There is no process going on PHY_NR_STATE_CELL_SEARCH, ///< Cell search is currently in progress PHY_NR_STATE_CELL_SELECT, ///< Cell selection is in progress or it is camped on a cell + PHY_NR_STATE_CAMPING } phy_nr_state_t; /** * @brief Retrieves the physical layer state * @return */ - virtual phy_nr_state_t get_state() const = 0; + virtual phy_nr_state_t get_state() = 0; /** * @brief Stops the ongoing process and transitions to IDLE @@ -279,6 +281,7 @@ public: * @brief Describes cell search arguments */ struct cell_search_args_t { + double srate_hz; double center_freq_hz; double ssb_freq_hz; srsran_subcarrier_spacing_t ssb_scs; @@ -297,7 +300,8 @@ public: * @brief Describes cell select arguments */ struct cell_select_args_t { - srsran::phy_cfg_nr_t phy_cfg; ///< Serving cell configuration + srsran_ssb_cfg_t ssb_cfg; + srsran_carrier_nr_t carrier; }; /** @@ -305,7 +309,7 @@ public: * @param args Cell Search arguments * @return true if the physical layer started successfully the cell search process */ - virtual bool start_cell_select(const cell_search_args_t& req) = 0; + virtual bool start_cell_select(const cell_select_args_t& req) = 0; }; // Combined interface for PHY to access stack (MAC and RRC) diff --git a/srsue/hdr/phy/nr/cell_search.h b/srsue/hdr/phy/nr/cell_search.h index 1fa9c361d..7333cd7a9 100644 --- a/srsue/hdr/phy/nr/cell_search.h +++ b/srsue/hdr/phy/nr/cell_search.h @@ -36,24 +36,24 @@ public: srsran_duplex_mode_t duplex_mode; }; + struct ret_t { + enum { CELL_FOUND = 1, CELL_NOT_FOUND = 0, ERROR = -1 } result; + srsran_ssb_search_res_t ssb_res; + }; + cell_search(srslog::basic_logger& logger); ~cell_search(); - bool init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); - - bool start(const cfg_t& cfg); + bool init(const args_t& args); - bool run(); + bool start(const cfg_t& cfg); + ret_t run_slot(const cf_t* buffer, uint32_t slot_sz); void reset(); private: srslog::basic_logger& logger; - stack_interface_phy_nr* stack = nullptr; - srsran::radio_interface_phy* radio = nullptr; srsran_ssb_t ssb = {}; - uint32_t sf_sz = 0; ///< subframe size in samples (1 ms) - cf_t* buffer = nullptr; ///< Receive buffer }; } // namespace nr } // namespace srsue diff --git a/srsue/hdr/phy/nr/sync_sa.h b/srsue/hdr/phy/nr/sync_sa.h index 350520cd9..48e5c10d4 100644 --- a/srsue/hdr/phy/nr/sync_sa.h +++ b/srsue/hdr/phy/nr/sync_sa.h @@ -22,6 +22,7 @@ #include "srsran/radio/rf_timestamp.h" #include "srsran/srslog/logger.h" #include "srsran/srsran.h" +#include "srsue/hdr/phy/sync_state.h" #include "worker_pool.h" #include #include @@ -63,25 +64,18 @@ public: } }; - typedef enum { - STATE_IDLE = 0, ///< No process is in progress - STATE_CELL_SEARCH, - STATE_CELL_SELECT - } state_t; - sync_sa(srslog::basic_logger& logger, worker_pool& workers_); ~sync_sa(); bool init(const args_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); - - // The following methods control the SYNC state machine - bool start_cell_search(const cell_search::cfg_t& cfg); - bool start_cell_select(); - bool go_idle(); - + bool reset(); void stop(); + sync_state::state_t get_state(); - state_t get_state() const; + // 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 worker_end(const worker_context_t& w_ctx, const bool& tx_enable, srsran::rf_buffer_t& buffer) override; @@ -91,25 +85,31 @@ private: srslog::basic_logger& logger; ///< General PHY logger worker_pool& workers; - state_t state = STATE_IDLE; - state_t next_state = STATE_IDLE; - mutable std::mutex state_mutex; - std::condition_variable state_cvar; - std::atomic running = {false}; - uint32_t sf_sz = 0; ///< Subframe size (1-ms) + // FSM that manages RRC commands for cell search/select/sync procedures + std::mutex rrc_mutex; + enum { PROC_IDLE = 0, PROC_SELECT_RUNNING, PROC_SEARCH_RUNNING } rrc_proc_state = PROC_IDLE; + sync_state phy_state; + + std::atomic running = {false}; + cf_t* rx_buffer = nullptr; + uint32_t slot_sz = 0; ///< Subframe size (1-ms) + uint32_t tti = 0; srsran::tti_semaphore tti_semaphore; - srsran_slot_cfg_t slot_cfg = {}; + srsran::rf_timestamp_t last_rx_time; + bool is_pending_tx_end = false; + uint32_t cell_search_nof_trials = 0; + const static uint32_t cell_search_max_trials = 100; - cell_search searcher; - slot_sync slot_synchronizer; + cell_search::ret_t cs_ret; + cell_search searcher; + slot_sync slot_synchronizer; // FSM States + bool wait_idle(); void run_state_idle(); void run_state_cell_search(); void run_state_cell_select(); - - // FSM transitions - void enter_state_idle(); + void run_state_cell_camping(); void run_thread() override; }; diff --git a/srsue/hdr/phy/phy.h b/srsue/hdr/phy/phy.h index 0eec95e12..2f00f9fdf 100644 --- a/srsue/hdr/phy/phy.h +++ b/srsue/hdr/phy/phy.h @@ -25,15 +25,18 @@ #include "srsran/srsran.h" #include "srsue/hdr/phy/lte/worker_pool.h" #include "srsue/hdr/phy/nr/worker_pool.h" -#include "srsue/hdr/phy/ue_lte_phy_base.h" -#include "srsue/hdr/phy/ue_nr_phy_base.h" +#include "srsue/hdr/phy/ue_phy_base.h" #include "sync.h" namespace srsue { typedef _Complex float cf_t; -class phy final : public ue_lte_phy_base, public ue_nr_phy_base, public srsran::thread +class phy final : public ue_phy_base, + public phy_interface_stack_lte, + public phy_interface_stack_nr, + public srsran::phy_interface_radio, + public srsran::thread { public: explicit phy() : @@ -50,7 +53,7 @@ public: ~phy() final { stop(); } // Init for LTE PHYs - int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) final; + int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_); void stop() final; @@ -130,23 +133,23 @@ public: std::string get_type() final { return "lte_soft"; } /********** NR INTERFACE ********************/ - int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) final; + 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; - void set_earfcn(std::vector earfcns) final; + void set_earfcn(std::vector earfcns); 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, srsran_rnti_type_t rnti_type) final; - phy_nr_state_t get_state() const override { return PHY_NR_STATE_IDLE; }; + phy_nr_state_t get_state() override { return PHY_NR_STATE_IDLE; }; void reset_nr() override{}; bool start_cell_search(const cell_search_args_t& req) override { return false; }; - bool start_cell_select(const cell_search_args_t& req) override { return false; }; + bool start_cell_select(const cell_select_args_t& req) override { return false; }; private: void run_thread() final; diff --git a/srsue/hdr/phy/phy_nr_sa.h b/srsue/hdr/phy/phy_nr_sa.h index 0f6cdc38f..257d4fb5b 100644 --- a/srsue/hdr/phy/phy_nr_sa.h +++ b/srsue/hdr/phy/phy_nr_sa.h @@ -16,19 +16,19 @@ #include "phy_common.h" #include "srsran/interfaces/ue_nr_interfaces.h" #include "srsue/hdr/phy/nr/sync_sa.h" -#include "srsue/hdr/phy/ue_nr_phy_base.h" +#include "srsue/hdr/phy/ue_phy_base.h" namespace srsue { /** * @brief NR Standalone PHY */ -class phy_nr_sa final : public ue_nr_phy_base +class phy_nr_sa final : public ue_phy_base, public phy_interface_stack_nr { public: phy_nr_sa(const char* logname); - int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) final; + int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); void wait_initialize() final; bool is_initialized() final; void stop() final; @@ -46,14 +46,14 @@ public: const int preamble_index, const float preamble_received_target_power, const float ta_base_sec) final; - void set_earfcn(std::vector earfcns) final{}; + void set_earfcn(std::vector earfcns); bool has_valid_sr_resource(uint32_t sr_id) final; void clear_pending_grants() final; bool set_config(const srsran::phy_cfg_nr_t& cfg) final; - phy_nr_state_t get_state() const final; + phy_nr_state_t get_state() final; bool start_cell_search(const cell_search_args_t& req) final; - bool start_cell_select(const cell_search_args_t& req) final { return false; } + 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 srsran_phy_logger(phy_logger_level_t log_level, char* str); @@ -64,11 +64,11 @@ private: nr::worker_pool workers; phy_common common; - prach prach_buffer; nr::sync_sa sync; - srsran::phy_cfg_nr_t config_nr = {}; - phy_args_nr_t args = {}; + srsran::phy_cfg_nr_t config_nr = {}; + phy_args_nr_t args = {}; + srsran_carrier_nr_t selected_cell = {}; srsran::radio_interface_phy* radio = nullptr; stack_interface_phy_nr* stack = nullptr; diff --git a/srsue/hdr/phy/sync_state.h b/srsue/hdr/phy/sync_state.h index a014cc38f..d16c68b89 100644 --- a/srsue/hdr/phy/sync_state.h +++ b/srsue/hdr/phy/sync_state.h @@ -13,6 +13,9 @@ #ifndef SRSUE_SYNC_STATE_H #define SRSUE_SYNC_STATE_H +#include +#include + namespace srsue { class sync_state @@ -58,6 +61,12 @@ public: next_state = SFN_SYNC; } + state_t get_state() + { + std::lock_guard lock(mutex); + return cur_state; + } + /* Functions to be called from outside the STM thread to instruct the STM to switch state. * The functions change the state and wait until it has changed it. * diff --git a/srsue/hdr/phy/ue_lte_phy_base.h b/srsue/hdr/phy/ue_lte_phy_base.h deleted file mode 100644 index 1cfea9a87..000000000 --- a/srsue/hdr/phy/ue_lte_phy_base.h +++ /dev/null @@ -1,46 +0,0 @@ -/** - * - * \section COPYRIGHT - * - * Copyright 2013-2021 Software Radio Systems Limited - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the distribution. - * - */ - -/****************************************************************************** - * File: ue_lte_phy_base.h - * Description: Base class for UE LTE PHYs. - *****************************************************************************/ - -#ifndef SRSUE_UE_LTE_PHY_BASE_H -#define SRSUE_UE_LTE_PHY_BASE_H - -#include "srsran/interfaces/radio_interfaces.h" -#include "srsue/hdr/phy/ue_phy_base.h" - -namespace srsue { - -class stack_interface_phy_lte; - -class ue_lte_phy_base : public ue_phy_base, public phy_interface_stack_lte, public srsran::phy_interface_radio -{ -public: - ue_lte_phy_base(){}; - virtual ~ue_lte_phy_base(){}; - - virtual std::string get_type() = 0; - - virtual int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) = 0; - virtual void stop() = 0; - - virtual void start_plot() = 0; - - virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0; -}; - -} // namespace srsue - -#endif // SRSUE_UE_LTE_PHY_BASE_H diff --git a/srsue/hdr/phy/ue_nr_phy_base.h b/srsue/hdr/phy/ue_nr_phy_base.h deleted file mode 100644 index 0f29f7705..000000000 --- a/srsue/hdr/phy/ue_nr_phy_base.h +++ /dev/null @@ -1,45 +0,0 @@ -/** - * - * \section COPYRIGHT - * - * Copyright 2013-2021 Software Radio Systems Limited - * - * By using this file, you agree to the terms and conditions set - * forth in the LICENSE file which can be found at the top level of - * the distribution. - * - */ - -/****************************************************************************** - * File: ue_nr_phy_base.h - * Description: Base class for UE NR PHYs. - *****************************************************************************/ - -#ifndef SRSUE_UE_NR_PHY_BASE_H -#define SRSUE_UE_NR_PHY_BASE_H - -#include "srsran/interfaces/radio_interfaces.h" -#include "srsran/interfaces/ue_nr_interfaces.h" -#include "srsue/hdr/phy/ue_phy_base.h" - -namespace srsue { - -class ue_nr_phy_base : public ue_phy_base, public phy_interface_stack_nr -{ -public: - ue_nr_phy_base(){}; - virtual ~ue_nr_phy_base() {} - - virtual std::string get_type() = 0; - - virtual int init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) = 0; - virtual void stop() = 0; - - virtual void set_earfcn(std::vector earfcns) = 0; - - virtual void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) = 0; -}; - -} // namespace srsue - -#endif // SRSUE_UE_NR_PHY_BASE_H \ No newline at end of file diff --git a/srsue/src/phy/nr/cell_search.cc b/srsue/src/phy/nr/cell_search.cc index 8d43b9813..e4b14b556 100644 --- a/srsue/src/phy/nr/cell_search.cc +++ b/srsue/src/phy/nr/cell_search.cc @@ -23,26 +23,10 @@ cell_search::cell_search(srslog::basic_logger& logger_) : logger(logger_) {} cell_search::~cell_search() { srsran_ssb_free(&ssb); - if (buffer != nullptr) { - free(buffer); - } } -bool cell_search::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) +bool cell_search::init(const args_t& args) { - stack = stack_; - radio = radio_; - - // Compute subframe size - sf_sz = (uint32_t)(args.max_srate_hz / 1000.0f); - - // Allocate receive buffer - buffer = srsran_vec_cf_malloc(2 * sf_sz); - if (buffer == nullptr) { - logger.error("Error allocating buffer"); - return false; - } - // Prepare SSB initialization arguments srsran_ssb_args_t ssb_args = {}; ssb_args.max_srate_hz = args.max_srate_hz; @@ -75,54 +59,24 @@ bool cell_search::start(const cfg_t& cfg) logger.error("Cell search: Error setting SSB configuration"); return false; } - - logger.info("Cell search: starting in center frequency %.2f and SSB frequency %.2f with subcarrier spacing of %s", - cfg.center_freq_hz / 1e6, - cfg.ssb_freq_hz / 1e6, - srsran_subcarrier_spacing_to_str(cfg.ssb_scs)); - - // Set RX frequency - radio->set_rx_freq(0, cfg.center_freq_hz); - - // Zero receive buffer - srsran_vec_zero(buffer, sf_sz); - return true; } -bool cell_search::run() +cell_search::ret_t cell_search::run_slot(const cf_t* buffer, uint32_t slot_sz) { - // Setup RF buffer for 1ms worth of samples - srsran::rf_buffer_t rf_buffer = {}; - rf_buffer.set_nof_samples(sf_sz); - rf_buffer.set(0, buffer + ssb.ssb_sz); - - // Receive - srsran::rf_timestamp_t rf_timestamp = {}; - if (not radio->rx_now(rf_buffer, rf_timestamp)) { - return false; - } + cell_search::ret_t ret = {}; // Search for SSB - srsran_ssb_search_res_t res = {}; - if (srsran_ssb_search(&ssb, buffer, sf_sz + ssb.ssb_sz, &res) < SRSRAN_SUCCESS) { + if (srsran_ssb_search(&ssb, buffer, slot_sz + ssb.ssb_sz, &ret.ssb_res) < SRSRAN_SUCCESS) { logger.error("Error occurred searching SSB"); - return false; + ret.result = ret_t::ERROR; + } else if (ret.ssb_res.measurements.snr_dB >= -10.0f and ret.ssb_res.pbch_msg.crc) { + // Consider the SSB is found and decoded if the PBCH CRC matched + ret.result = ret_t::CELL_FOUND; + } else { + ret.result = ret_t::CELL_NOT_FOUND; } - - // Consider the SSB is found and decoded if the PBCH CRC matched - if (res.measurements.snr_dB >= -10.0f and res.pbch_msg.crc) { - rrc_interface_phy_nr::cell_search_result_t cs_res = {}; - cs_res.pci = res.N_id; - cs_res.pbch_msg = res.pbch_msg; - cs_res.measurements = res.measurements; - stack->cell_search_found_cell(cs_res); - } - - // Advance stack TTI - stack->run_tti(0); - - return true; + return ret; } } // namespace nr diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc index 898f685bc..06f1a1da1 100644 --- a/srsue/src/phy/phy_nr_sa.cc +++ b/srsue/src/phy/phy_nr_sa.cc @@ -11,7 +11,6 @@ */ #include "srsue/hdr/phy/phy_nr_sa.h" -#include "srsran/common/band_helper.h" #include "srsran/common/standard_streams.h" #include "srsran/srsran.h" @@ -60,8 +59,7 @@ phy_nr_sa::phy_nr_sa(const char* logname) : logger_phy_lib(srslog::fetch_basic_logger("PHY_LIB")), sync(logger, workers), workers(logger, 4), - common(logger), - prach_buffer(logger) + common(logger) {} int phy_nr_sa::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_) @@ -100,7 +98,6 @@ void phy_nr_sa::init_background() logger.error("Error initialising SYNC"); return; } - prach_buffer.init(SRSRAN_MAX_PRB); workers.init(args, sync, stack, WORKERS_THREAD_PRIO); is_configured = true; @@ -113,7 +110,6 @@ void phy_nr_sa::stop() if (is_configured) { sync.stop(); workers.stop(); - prach_buffer.stop(); is_configured = false; } } @@ -128,16 +124,18 @@ void phy_nr_sa::wait_initialize() init_thread.join(); } -phy_interface_rrc_nr::phy_nr_state_t phy_nr_sa::get_state() const +phy_interface_rrc_nr::phy_nr_state_t phy_nr_sa::get_state() { { switch (sync.get_state()) { - case nr::sync_sa::STATE_IDLE: - break; - case nr::sync_sa::STATE_CELL_SEARCH: + case sync_state::state_t::IDLE: + return phy_interface_rrc_nr::PHY_NR_STATE_IDLE; + case sync_state::state_t::CELL_SEARCH: return phy_interface_rrc_nr::PHY_NR_STATE_CELL_SEARCH; - case nr::sync_sa::STATE_CELL_SELECT: + case sync_state::state_t::SFN_SYNC: return phy_interface_rrc_nr::PHY_NR_STATE_CELL_SELECT; + case sync_state::state_t::CAMPING: + return phy_interface_rrc_nr::PHY_NR_STATE_CAMPING; } } return phy_interface_rrc_nr::PHY_NR_STATE_IDLE; @@ -145,22 +143,68 @@ phy_interface_rrc_nr::phy_nr_state_t phy_nr_sa::get_state() const void phy_nr_sa::reset_nr() { - sync.go_idle(); + sync.reset(); } +// This function executes one part of the procedure immediately and returns to continue in the background. +// When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH +// processing. +// It will perform cell search procedure in the background and will signal stack with function cell_search_found_cell() +// when finished 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 = 0; // 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; + // TODO: verify arguments are valid before starting procedure + + logger.info("Cell Search: Going to IDLE"); + sync.cell_go_idle(); + + cmd_worker_cell.add_cmd([this, req]() { + // Prepare cell search configuration from the request + nr::cell_search::cfg_t cfg = {}; + cfg.srate_hz = req.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. + nr::cell_search::ret_t ret = sync.cell_search_run(cfg); + + // Pass result to stack + rrc_interface_phy_nr::cell_search_result_t rrc_cs_ret = {}; + rrc_cs_ret.cell_found = ret.result == nr::cell_search::ret_t::CELL_FOUND; + if (rrc_cs_ret.cell_found) { + rrc_cs_ret.pci = ret.ssb_res.N_id; + rrc_cs_ret.pbch_msg = ret.ssb_res.pbch_msg; + rrc_cs_ret.measurements = ret.ssb_res.measurements; + } + stack->cell_search_found_cell(rrc_cs_ret); + }); - // Request cell search to lower synchronization instance - return sync.start_cell_search(cfg); + return true; +} + +// This function executes one part of the procedure immediately and returns to continue in the background. +// When it returns, the caller thread can expect the PHY to have switched to IDLE and have stopped all DL/UL/PRACH +// processing. +// It will perform cell search procedure in the background and will signal stack with function cell_search_found_cell() +// when finished +bool phy_nr_sa::start_cell_select(const cell_select_args_t& req) +{ + // TODO: verify arguments are valid before starting procedure + + logger.info("Cell Select: Going to IDLE"); + sync.cell_go_idle(); + + selected_cell = req.carrier; + + cmd_worker_cell.add_cmd([this, req]() { + // Request cell search to lower synchronization instance. + start_cell_select(req); + }); + + return true; } bool phy_nr_sa::has_valid_sr_resource(uint32_t sr_id) @@ -196,8 +240,6 @@ bool phy_nr_sa::set_config(const srsran::phy_cfg_nr_t& cfg) // Setup carrier configuration asynchronously cmd_worker.add_cmd([this]() { - srsran::srsran_band_helper band_helper; - // tune radio for (uint32_t i = 0; i < common.args->nof_nr_carriers; i++) { logger.info("Tuning Rx channel %d to %.2f MHz", diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc index a52fbc966..e2266c425 100644 --- a/srsue/src/phy/sync_sa.cc +++ b/srsue/src/phy/sync_sa.cc @@ -9,6 +9,7 @@ * the distribution. * */ + #include "srsue/hdr/phy/nr/sync_sa.h" #include "srsran/radio/rf_buffer.h" @@ -22,12 +23,12 @@ 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_; - sf_sz = (uint32_t)(args.srate_hz / 1000.0f); + stack = stack_; + radio = radio_; + slot_sz = (uint32_t)(args.srate_hz / 1000.0f); // Initialise cell search internal object - if (not searcher.init(args.get_cell_search(), stack, radio)) { + if (not searcher.init(args.get_cell_search())) { logger.error("Error initialising cell searcher"); return false; } @@ -38,6 +39,16 @@ bool sync_sa::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::r return false; } + // Compute subframe size + slot_sz = (uint32_t)(args.srate_hz / 1000.0f); + + // Allocate receive buffer + rx_buffer = srsran_vec_cf_malloc(2 * slot_sz); + if (rx_buffer == nullptr) { + logger.error("Error allocating buffer"); + return false; + } + // Thread control running = true; start(args.thread_priority); @@ -46,116 +57,250 @@ bool sync_sa::init(const args_t& args, stack_interface_phy_nr* stack_, srsran::r return true; } -bool sync_sa::start_cell_search(const cell_search::cfg_t& cfg) +void sync_sa::stop() +{ + running = false; + wait_thread_finish(); + radio->reset(); +} + +bool sync_sa::reset() +{ + // Wait worker pool to finish any processing + tti_semaphore.wait_all(); + + return true; +} + +void sync_sa::cell_go_idle() +{ + std::unique_lock ul(rrc_mutex); + phy_state.go_idle(); +} + +bool sync_sa::wait_idle() { - // Make sure current state is IDLE - std::unique_lock lock(state_mutex); - if (state != STATE_IDLE or next_state != STATE_IDLE) { - logger.error("Sync: trying to start cell search but state is not IDLE"); + // Wait for SYNC thread to transition to IDLE (max. 2000ms) + if (!phy_state.wait_idle(100)) { return false; } + // Reset UE sync. Attention: doing this reset when the FSM is NOT IDLE can cause PSS/SSS out-of-sync + //... + + // Wait for workers to finish PHY processing + tti_semaphore.wait_all(); + + // As workers have finished, make sure the Tx burst is ended + radio->tx_end(); + + return phy_state.is_idle(); +} + +cell_search::ret_t sync_sa::cell_search_run(const cell_search::cfg_t& cfg) +{ + std::unique_lock ul(rrc_mutex); + + cs_ret = {}; + cs_ret.result = cell_search::ret_t::ERROR; + + // 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 cs_ret; + } + + rrc_proc_state = PROC_SEARCH_RUNNING; + // Configure searcher without locking state for avoiding stalling the Rx stream - lock.unlock(); + logger.info("Cell search: starting in center frequency %.2f and SSB frequency %.2f with subcarrier spacing of %s", + cfg.center_freq_hz / 1e6, + cfg.ssb_freq_hz / 1e6, + srsran_subcarrier_spacing_to_str(cfg.ssb_scs)); + if (not searcher.start(cfg)) { logger.error("Sync: failed to start cell search"); - return false; + return cs_ret; } - lock.lock(); - // Transition to search - next_state = STATE_CELL_SEARCH; + // Set RX frequency + radio->set_rx_freq(0, cfg.center_freq_hz); - return true; -} + // Zero receive buffer + srsran_vec_zero(rx_buffer, slot_sz); -bool sync_sa::start_cell_select() -{ - return true; + logger.info("Cell Search: Running Cell search state"); + cell_search_nof_trials = 0; + phy_state.run_cell_search(); + + rrc_proc_state = PROC_IDLE; + + return cs_ret; } -bool sync_sa::go_idle() +bool sync_sa::cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& req) { - std::unique_lock lock(state_mutex); + std::unique_lock ul(rrc_mutex); - // Force transition to IDLE - while (state != STATE_IDLE) { - next_state = STATE_IDLE; - state_cvar.wait(lock); + // 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; } - // Wait worker pool to finish any processing - tti_semaphore.wait_all(); + rrc_proc_state = PROC_SELECT_RUNNING; + + // Reconfigure cell if necessary + // SFN synchronization + phy_state.run_sfn_sync(); + if (phy_state.is_camping()) { + logger.info("Cell Select: SFN synchronized. CAMPING..."); + } else { + logger.info("Cell Select: Could not synchronize SFN"); + } + + rrc_proc_state = PROC_IDLE; return true; } -void sync_sa::stop() +sync_state::state_t sync_sa::get_state() { - running = false; - wait_thread_finish(); + return phy_state.get_state(); } -sync_sa::state_t sync_sa::get_state() const +void sync_sa::run_state_idle() { - std::unique_lock lock(state_mutex); - return state; + if (radio->is_init()) { + logger.debug("Discarding samples and sending tx_end"); + radio->tx_end(); + } else { + logger.debug("Sleeping 1 ms"); + usleep(1000); + } } -void sync_sa::run_state_idle() +void sync_sa::run_state_cell_search() { - srsran::rf_buffer_t rf_buffer = {}; - rf_buffer.set_nof_samples(sf_sz); + // Run Searcher + cs_ret = searcher.run_slot(rx_buffer, slot_sz); + if (cs_ret.result < 0) { + logger.error("Failed to run searcher. Transitioning to IDLE..."); + } - srsran::rf_timestamp_t ts = {}; + cell_search_nof_trials++; - // Receives from radio 1 slot - radio->rx_now(rf_buffer, ts); + // Leave CELL_SEARCH state if error or success and transition to IDLE + if (cs_ret.result || cell_search_nof_trials >= cell_search_max_trials) { + phy_state.state_exit(); + } +} - stack->run_tti(slot_cfg.idx); +void sync_sa::run_state_cell_select() +{ + // TODO + tti = 0; } -void sync_sa::run_state_cell_search() +void sync_sa::run_state_cell_camping() { - // Run Searcher - if (not searcher.run()) { - logger.error("Failed to run searcher. Transitioning to IDLE..."); + // Update logging TTI + logger.set_context(tti); - // Transition to IDLE if fails to run - state_mutex.lock(); - next_state = STATE_IDLE; - state_mutex.unlock(); + last_rx_time.add(FDD_HARQ_DELAY_DL_MS * 1e-3); + + nr::sf_worker* nr_worker = nullptr; + nr_worker = workers.wait_worker(tti); + if (nr_worker == nullptr) { + running = false; + return; } + + srsran::phy_common_interface::worker_context_t context; + context.sf_idx = tti; + context.worker_ptr = nr_worker; + context.last = true; // Set last if standalone + context.tx_time.copy(last_rx_time); + + nr_worker->set_context(context); + + // NR worker needs to be launched first, phy_common::worker_end expects first the NR worker and the LTE worker. + tti_semaphore.push(nr_worker); + workers.start_worker(nr_worker); + + tti = TTI_ADD(tti, 1); } void sync_sa::run_thread() { - while (running) { - state_mutex.lock(); - // Detect state transition - if (next_state != state) { - state = next_state; - state_cvar.notify_all(); - } - state_t current_state = state; - state_mutex.unlock(); + while (running.load(std::memory_order_relaxed)) { + logger.debug("SYNC: state=%s, tti=%d", phy_state.to_string(), tti); + + // Setup RF buffer for 1ms worth of samples + if (radio->is_init()) { + srsran::rf_buffer_t rf_buffer = {}; + rf_buffer.set_nof_samples(slot_sz); + rf_buffer.set(0, rx_buffer); - switch (current_state) { - case STATE_IDLE: + if (not radio->rx_now(rf_buffer, last_rx_time)) { + logger.error("SYNC: receiving from radio\n"); + } + } + switch (phy_state.run_state()) { + case sync_state::IDLE: run_state_idle(); break; - case STATE_CELL_SEARCH: + case sync_state::CELL_SEARCH: run_state_cell_search(); break; - case STATE_CELL_SELECT: + case sync_state::SFN_SYNC: + run_state_cell_select(); + case sync_state::CAMPING: + run_state_cell_camping(); break; } } + // Advance stack TTI + stack->run_tti(tti); } void sync_sa::worker_end(const srsran::phy_common_interface::worker_context_t& w_ctx, const bool& tx_enable, - srsran::rf_buffer_t& buffer) -{} + srsran::rf_buffer_t& tx_buffer) +{ + // Wait for the green light to transmit in the current TTI + tti_semaphore.wait(w_ctx.worker_ptr); + + // Add current time alignment + srsran::rf_timestamp_t tx_time = w_ctx.tx_time; // get transmit time from the last worker + // todo: tx_time.sub((double)ta.get_sec()); + + // Check if any worker had a transmission + if (tx_enable) { + // Actual baseband transmission + radio->tx(tx_buffer, tx_time); + + } else { + if (radio->is_continuous_tx()) { + if (is_pending_tx_end) { + radio->tx_end(); + is_pending_tx_end = false; + } else { + if (!radio->get_is_start_of_burst()) { + // TODO + /* + zeros_multi.set_nof_samples(buffer.get_nof_samples()); + radio->tx(zeros_multi, tx_time); + */ + } + } + } else { + radio->tx_end(); + } + } + + // Allow next TTI to transmit + tti_semaphore.release(); +} } // namespace nr } // namespace srsue \ No newline at end of file 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 64a16dc02..ad6048119 100644 --- a/srsue/src/phy/test/nr_sa_cell_search_test.cc +++ b/srsue/src/phy/test/nr_sa_cell_search_test.cc @@ -302,11 +302,13 @@ int main(int argc, char** argv) // Create dummy UE dummy_ue::args_t ue_args = {}; + ue_args.phy.log.phy_level = args.phy_log_level; ue_args.stack.log_level = args.stack_log_level; dummy_ue ue(ue_args, radio.get()); // Transition PHY to cell search srsue::phy_nr_sa::cell_search_args_t cell_search_req = {}; + cell_search_req.srate_hz = args.srate_hz; cell_search_req.center_freq_hz = args.base_carrier.dl_center_frequency_hz; cell_search_req.ssb_freq_hz = args.base_carrier.ssb_center_freq_hz; cell_search_req.ssb_scs = args.ssb_scs; diff --git a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc index 4065912f0..6ceef4a97 100644 --- a/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc +++ b/srsue/src/stack/rrc_nr/test/ue_rrc_nr_test.cc @@ -23,10 +23,10 @@ using namespace srsue; class dummy_phy : public phy_interface_rrc_nr { bool set_config(const srsran::phy_cfg_nr_t& cfg) override { return true; } - phy_nr_state_t get_state() const override { return PHY_NR_STATE_IDLE; }; + phy_nr_state_t get_state() override { return PHY_NR_STATE_IDLE; }; void reset_nr() override{}; bool start_cell_search(const cell_search_args_t& req) override { return false; }; - bool start_cell_select(const cell_search_args_t& req) override { return false; }; + bool start_cell_select(const cell_select_args_t& req) override { return false; }; }; class dummy_mac : public mac_interface_rrc_nr diff --git a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h index 1c34145d8..10e5c42f7 100644 --- a/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h +++ b/srsue/src/test/ttcn3/hdr/lte_ttcn3_phy.h @@ -16,7 +16,7 @@ #include "srsran/common/task_scheduler.h" #include "srsran/interfaces/ue_interfaces.h" #include "srsran/interfaces/ue_phy_interfaces.h" -#include "srsue/hdr/phy/ue_lte_phy_base.h" +#include "srsue/hdr/phy/ue_phy_base.h" #include "srsue/hdr/ue.h" #include "ttcn3_interfaces.h" #include @@ -26,7 +26,7 @@ using namespace srsran; namespace srsue { -class lte_ttcn3_phy : public ue_lte_phy_base +class lte_ttcn3_phy : public ue_phy_base, public phy_interface_stack_lte { public: void set_cells_to_meas(uint32_t earfcn, const std::set& pci) override; @@ -42,10 +42,9 @@ public: int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, syssim_interface_phy* syssim_); - int init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) override; - void stop() override; void wait_initialize() override; + bool is_initialized() override; void start_plot() override; void get_metrics(const srsran::srsran_rat_t& rat, phy_metrics_t* m) override; std::string get_type() override; @@ -94,10 +93,6 @@ public: void new_grant_ul(mac_interface_phy_lte::mac_grant_ul_t ul_mac_grant); void new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t, const uint8_t* data); - // Radio interface - void radio_overflow() override; - void radio_failure() override; - void run_tti(); private: diff --git a/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc b/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc index 5051a4b02..ce1be9e53 100644 --- a/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc +++ b/srsue/src/test/ttcn3/src/lte_ttcn3_phy.cc @@ -33,18 +33,13 @@ int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_ return SRSRAN_SUCCESS; } -int lte_ttcn3_phy::init(const phy_args_t& args_, stack_interface_phy_lte* stack_, srsran::radio_interface_phy* radio_) -{ - stack = stack_; - - logger.set_level(srslog::str_to_basic_level(args_.log.phy_level)); - logger.set_hex_dump_max_size(-1); +void lte_ttcn3_phy::stop(){}; - return SRSRAN_SUCCESS; +bool lte_ttcn3_phy::is_initialized() +{ + return true; } -void lte_ttcn3_phy::stop(){}; - void lte_ttcn3_phy::wait_initialize() {} void lte_ttcn3_phy::start_plot() {} @@ -334,16 +329,6 @@ void lte_ttcn3_phy::new_tb(const srsue::mac_interface_phy_lte::mac_grant_dl_t dl stack->tb_decoded(cc_idx, dl_grant, dl_ack); } -void lte_ttcn3_phy::radio_overflow() -{ - logger.debug("%s not implemented.", __FUNCTION__); -} - -void lte_ttcn3_phy::radio_failure() -{ - logger.debug("%s not implemented.", __FUNCTION__); -} - // Calling function set_tti() is holding mutex void lte_ttcn3_phy::run_tti() { diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h index e26b1a82e..2eac380e4 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -141,31 +141,33 @@ public: void cell_search_found_cell(const cell_search_result_t& result) override { -#if 0 - // Unpack MIB with ASN1 - asn1::rrc_nr::mib_s mib_asn1; - asn1::cbit_ref cbit(result.pbch_msg.payload, SRSRAN_PBCH_MSG_NR_SZ); - mib_asn1.unpack(cbit); - - // Convert MIB to JSON - asn1::json_writer json; - mib_asn1.to_json(json); - - // Unpack MIB with C lib - srsran_mib_nr_t mib_c = {}; - srsran_pbch_msg_nr_mib_unpack(&result.pbch_msg, &mib_c); - - // Convert MIB from C lib to info - std::array mib_info = {}; - srsran_pbch_msg_nr_mib_info(&mib_c, mib_info.data(), (uint32_t)mib_info.size()); - - // Convert CSI to string - std::array csi_info = {}; - srsran_csi_meas_info_short(&result.measurements, csi_info.data(), (uint32_t)csi_info.size()); - - logger.info( - "Cell found pci=%d %s %s ASN1: %s", result.pci, mib_info.data(), csi_info.data(), json.to_string().c_str()); -#endif + if (result.cell_found) { + // Unpack MIB with ASN1 + asn1::rrc_nr::mib_s mib_asn1; + asn1::cbit_ref cbit(result.pbch_msg.payload, SRSRAN_PBCH_MSG_NR_SZ); + mib_asn1.unpack(cbit); + + // Convert MIB to JSON + asn1::json_writer json; + mib_asn1.to_json(json); + + // Unpack MIB with C lib + srsran_mib_nr_t mib_c = {}; + srsran_pbch_msg_nr_mib_unpack(&result.pbch_msg, &mib_c); + + // Convert MIB from C lib to info + std::array mib_info = {}; + srsran_pbch_msg_nr_mib_info(&mib_c, mib_info.data(), (uint32_t)mib_info.size()); + + // Convert CSI to string + std::array csi_info = {}; + srsran_csi_meas_info_short(&result.measurements, csi_info.data(), (uint32_t)csi_info.size()); + + logger.info( + "Cell found pci=%d %s %s ASN1: %s", result.pci, mib_info.data(), csi_info.data(), json.to_string().c_str()); + } else { + logger.info("Cell not found\n"); + } } };