diff --git a/lib/include/srsran/phy/ue/ue_sync_nr.h b/lib/include/srsran/phy/ue/ue_sync_nr.h index da4fcb381..58fea2b2c 100644 --- a/lib/include/srsran/phy/ue/ue_sync_nr.h +++ b/lib/include/srsran/phy/ue/ue_sync_nr.h @@ -53,7 +53,7 @@ typedef struct SRSRAN_API { */ typedef struct SRSRAN_API { srsran_ssb_cfg_t ssb; ///< SSB configuration - uint32_t N_id; ///< Physicall cell identifier + uint32_t N_id; ///< Physical cell identifier } srsran_ue_sync_nr_cfg_t; /** diff --git a/lib/src/phy/sync/ssb.c b/lib/src/phy/sync/ssb.c index 0cf60f651..5223a6248 100644 --- a/lib/src/phy/sync/ssb.c +++ b/lib/src/phy/sync/ssb.c @@ -420,6 +420,12 @@ int srsran_ssb_set_cfg(srsran_ssb_t* q, const srsran_ssb_cfg_t* cfg) return SRSRAN_ERROR_INVALID_INPUTS; } + printf("-- srate=%.2fMHz; center_freq=%.2fMHz; ssb_freq=%.2fMHz; scs=%s;\n", + cfg->srate_hz / 1e6, + cfg->center_freq_hz / 1e6, + cfg->ssb_freq_hz / 1e6, + srsran_subcarrier_spacing_to_str(cfg->scs)); + // Calculate subcarrier spacing in Hz q->scs_hz = (float)SRSRAN_SUBC_SPACING_NR(cfg->scs); @@ -1127,6 +1133,14 @@ int srsran_ssb_search(srsran_ssb_t* q, const cf_t* in, uint32_t nof_samples, srs return SRSRAN_ERROR; } + if (fabs(q->cfg.ssb_freq_hz - 1842.05e6) < 1e6) { + printf("-- freq=%.3f; N_id=%d; EPRE=%+.2f; RSRP=%+.2f;\n", + q->cfg.ssb_freq_hz, + N_id, + measurements.epre_dB, + measurements.rsrp_dB); + } + // Save result res->N_id = N_id; res->t_offset = t_offset; diff --git a/lib/src/phy/ue/test/ue_sync_nr_test.c b/lib/src/phy/ue/test/ue_sync_nr_test.c index 9b064d3b7..497b26231 100644 --- a/lib/src/phy/ue/test/ue_sync_nr_test.c +++ b/lib/src/phy/ue/test/ue_sync_nr_test.c @@ -307,4 +307,4 @@ clean_exit: test_context_free(&ctx); return ret; -} \ No newline at end of file +} diff --git a/lib/src/phy/ue/ue_sync_nr.c b/lib/src/phy/ue/ue_sync_nr.c index 97ae3df10..5972ab5c0 100644 --- a/lib/src/phy/ue/ue_sync_nr.c +++ b/lib/src/phy/ue/ue_sync_nr.c @@ -175,7 +175,7 @@ static int ue_sync_nr_run_track(srsran_ue_sync_nr_t* q, cf_t* buffer) return SRSRAN_ERROR; } - // If the PBCH message was NOT decoded, transition to track + // If the PBCH message was NOT decoded, transition to find if (!pbch_msg.crc) { q->state = SRSRAN_UE_SYNC_NR_STATE_FIND; return SRSRAN_SUCCESS; diff --git a/srsue/hdr/phy/nr/slot_sync.h b/srsue/hdr/phy/nr/slot_sync.h index f4b8aa5a5..b3b519ffb 100644 --- a/srsue/hdr/phy/nr/slot_sync.h +++ b/srsue/hdr/phy/nr/slot_sync.h @@ -39,9 +39,14 @@ public: bool init(const args_t& args, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); + int set_sync_cfg(const srsran_ue_sync_nr_cfg_t& cfg); + int recv_callback(srsran::rf_buffer_t& rf_buffer, srsran_timestamp_t* timestamp); + bool run_sfn_sync(); 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 @@ -54,7 +59,8 @@ private: 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 - uint32_t tti = 0; + srsran::rf_buffer_t sfn_sync_buff = {}; + srsran_slot_cfg_t slot_cfg = {}; }; } // namespace nr } // namespace srsue diff --git a/srsue/hdr/phy/nr/sync_sa.h b/srsue/hdr/phy/nr/sync_sa.h index 1ac04b379..eacbcaaaf 100644 --- a/srsue/hdr/phy/nr/sync_sa.h +++ b/srsue/hdr/phy/nr/sync_sa.h @@ -67,9 +67,9 @@ public: 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_); + bool init(const args_t& args_, stack_interface_phy_nr* stack_, srsran::radio_interface_phy* radio_); bool reset(); - void stop(); + void stop(); sync_state::state_t get_state(); // The following methods control the SYNC state machine @@ -82,7 +82,7 @@ public: private: stack_interface_phy_nr* stack = nullptr; ///< Stand-Alone RRC interface srsran::radio_interface_phy* radio = nullptr; ///< Radio object - srslog::basic_logger& logger; ///< General PHY logger + srslog::basic_logger& logger; ///< General PHY logger worker_pool& workers; // FSM that manages RRC commands for cell search/select/sync procedures @@ -99,6 +99,8 @@ private: bool is_pending_tx_end = false; uint32_t cell_search_nof_trials = 0; const static uint32_t cell_search_max_trials = 100; + uint32_t sfn_sync_nof_trials = 0; + const static uint32_t sfn_sync_max_trials = 100; cell_search::ret_t cs_ret; cell_search searcher; @@ -108,7 +110,7 @@ private: bool wait_idle(); void run_state_idle(); void run_state_cell_search(); - void run_state_cell_select(); + void run_state_sfn_sync(); void run_state_cell_camping(); int radio_recv_fnc(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_time); diff --git a/srsue/src/phy/nr/cell_search.cc b/srsue/src/phy/nr/cell_search.cc index e4b14b556..51c75e27c 100644 --- a/srsue/src/phy/nr/cell_search.cc +++ b/srsue/src/phy/nr/cell_search.cc @@ -80,4 +80,4 @@ cell_search::ret_t cell_search::run_slot(const cf_t* buffer, uint32_t slot_sz) } } // namespace nr -} // namespace srsue \ No newline at end of file +} // namespace srsue diff --git a/srsue/src/phy/nr/slot_sync.cc b/srsue/src/phy/nr/slot_sync.cc index 31161d456..dbfcd2074 100644 --- a/srsue/src/phy/nr/slot_sync.cc +++ b/srsue/src/phy/nr/slot_sync.cc @@ -15,14 +15,14 @@ namespace srsue { namespace nr { -slot_sync::slot_sync(srslog::basic_logger& logger_) : logger(logger_) {} +slot_sync::slot_sync(srslog::basic_logger& logger_) : logger(logger_), sfn_sync_buff(1) {} slot_sync::~slot_sync() { srsran_ue_sync_nr_free(&ue_sync_nr); } -int sync_sa_recv_callback(void* ptr, cf_t** buffer, uint32_t nsamples, srsran_timestamp_t* ts) +static int slot_sync_recv_callback(void* ptr, cf_t** buffer, uint32_t nsamples, srsran_timestamp_t* ts) { if (ptr == nullptr) { return SRSRAN_ERROR_INVALID_INPUTS; @@ -45,6 +45,8 @@ bool slot_sync::init(const args_t& args, stack_interface_phy_nr* stack_, srsran: ue_sync_nr_args.disable_cfo = args.disable_cfo; ue_sync_nr_args.pbch_dmrs_thr = args.pbch_dmrs_thr; ue_sync_nr_args.cfo_alpha = args.cfo_alpha; + ue_sync_nr_args.recv_obj = this; + ue_sync_nr_args.recv_callback = slot_sync_recv_callback; if (srsran_ue_sync_nr_init(&ue_sync_nr, &ue_sync_nr_args) < SRSRAN_SUCCESS) { logger.error("Error initiating UE SYNC NR object"); @@ -54,6 +56,16 @@ bool slot_sync::init(const args_t& args, stack_interface_phy_nr* stack_, srsran: return true; } +int slot_sync::set_sync_cfg(const srsran_ue_sync_nr_cfg_t& cfg) +{ + if (srsran_ue_sync_nr_set_cfg(&ue_sync_nr, &cfg) < SRSRAN_SUCCESS) { + logger.error("SYNC: failed to set cell configuration for N_id %d", cfg.N_id); + return SRSRAN_ERROR; + } + + return SRSRAN_SUCCESS; +} + int slot_sync::recv_callback(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_time) { // This function is designed for being called from the UE sync object which will pass a null rx_time in case @@ -88,6 +100,21 @@ int slot_sync::recv_callback(srsran::rf_buffer_t& data, srsran_timestamp_t* rx_t return data.get_nof_samples(); } +bool slot_sync::run_sfn_sync() +{ + srsran_ue_sync_nr_outcome_t outcome = {}; + if (srsran_ue_sync_nr_zerocopy(&ue_sync_nr, sfn_sync_buff.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_SF_NR(srsran_subcarrier_spacing_15kHz) + outcome.sf_idx; + } + + return outcome.in_sync; +} + void slot_sync::run_stack_tti() { // check timestamp reset if (forced_rx_time_init || srsran_timestamp_iszero(&stack_tti_ts) || @@ -118,8 +145,8 @@ void slot_sync::run_stack_tti() } // Run stack - logger.debug("run_stack_tti: calling stack tti=%d, tti_jump=%d", tti, tti_jump); - stack->run_tti(tti, tti_jump); + logger.debug("run_stack_tti: calling stack tti=%d, tti_jump=%d", slot_cfg.idx, tti_jump); + stack->run_tti(slot_cfg.idx, tti_jump); logger.debug("run_stack_tti: stack called"); } @@ -127,5 +154,10 @@ void slot_sync::run_stack_tti() srsran_timestamp_copy(&stack_tti_ts, &stack_tti_ts_new); } +srsran_slot_cfg_t slot_sync::get_slot_cfg() +{ + return slot_cfg; +} + } // namespace nr } // 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 e554a05a1..de5bd74eb 100644 --- a/srsue/src/phy/sync_sa.cc +++ b/srsue/src/phy/sync_sa.cc @@ -157,6 +157,15 @@ bool sync_sa::cell_select_run(const phy_interface_rrc_nr::cell_select_args_t& re logger.info("Tuning Tx channel %d to %.2f MHz", 0, req.carrier.ul_center_frequency_hz / 1e6); radio->set_tx_freq(0, req.carrier.ul_center_frequency_hz); + // Configure cell + srsran_ue_sync_nr_cfg_t cfg = {}; + cfg.N_id = req.carrier.pci; + cfg.ssb = req.ssb_cfg; + if (slot_synchronizer.set_sync_cfg(cfg)) { + logger.error("Cell Search: Failed setting slot synchronizer configuration"); + return false; + } + // SFN synchronization phy_state.run_sfn_sync(); if (phy_state.is_camping()) { @@ -176,7 +185,7 @@ sync_state::state_t sync_sa::get_state() void sync_sa::run_state_idle() { -#define test 0 +#define test 1 if (radio->is_init() && test) { logger.debug("Discarding samples and sending tx_end"); srsran::rf_buffer_t rf_buffer = {}; @@ -197,30 +206,60 @@ void sync_sa::run_state_cell_search() // Receive samples srsran::rf_buffer_t rf_buffer = {}; rf_buffer.set_nof_samples(slot_sz); - rf_buffer.set(0, rx_buffer); + rf_buffer.set(0, rx_buffer + slot_sz); if (not slot_synchronizer.recv_callback(rf_buffer, last_rx_time.get_ptr(0))) { logger.error("SYNC: receiving from radio\n"); } // Run Searcher - cs_ret = searcher.run_slot(rx_buffer, slot_sz); + cs_ret = searcher.run_slot(rx_buffer, 2 * slot_sz); if (cs_ret.result < 0) { logger.error("Failed to run searcher. Transitioning to IDLE..."); } + srsran_vec_cf_copy(rx_buffer, rx_buffer + slot_sz, slot_sz); + cell_search_nof_trials++; + if (cs_ret.result == cell_search::ret_t::CELL_FOUND) { + logger.error("CELL FOUND!"); + + rrc_interface_phy_nr::cell_search_result_t result = {}; + result.cell_found = true; + result.pci = cs_ret.ssb_res.N_id; + result.pbch_msg = cs_ret.ssb_res.pbch_msg; + result.measurements = cs_ret.ssb_res.measurements; + stack->cell_search_found_cell(result); + } + // Leave CELL_SEARCH state if error or success and transition to IDLE - if (cs_ret.result || cell_search_nof_trials >= cell_search_max_trials) { + if (/*cs_ret.result || */ cell_search_nof_trials >= cell_search_max_trials) { phy_state.state_exit(); } } -void sync_sa::run_state_cell_select() +void sync_sa::run_state_sfn_sync() { - // TODO - tti = 10240 - 4; - phy_state.state_exit(); + // Run SFN synchronization + if (slot_synchronizer.run_sfn_sync()) { + tti = slot_synchronizer.get_slot_cfg().idx; + + 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(); + return; + } + + // If not synchonized, increment number of trials + sfn_sync_nof_trials++; + + // Abort SFN synchronization if the maximum number of trials is reached + if (sfn_sync_nof_trials >= sfn_sync_max_trials) { + logger.info("SYNC: The SFN sync reached the maximum number of trials (%d). Transitioning to IDLE...", + sfn_sync_nof_trials); + phy_state.state_exit(false); + } } void sync_sa::run_state_cell_camping() @@ -271,7 +310,7 @@ void sync_sa::run_thread() run_state_cell_search(); break; case sync_state::SFN_SYNC: - run_state_cell_select(); + run_state_sfn_sync(); break; case sync_state::CAMPING: run_state_cell_camping(); @@ -324,4 +363,4 @@ void sync_sa::worker_end(const srsran::phy_common_interface::worker_context_t& w } } // namespace nr -} // namespace srsue \ No newline at end of file +} // namespace srsue 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 ad6048119..f4fd7ef70 100644 --- a/srsue/src/phy/test/nr_sa_cell_search_test.cc +++ b/srsue/src/phy/test/nr_sa_cell_search_test.cc @@ -213,12 +213,13 @@ private: public: struct args_t { - srsue::phy_args_nr_t phy; - ue_dummy_stack::args_t stack; + srsue::phy_args_nr_t phy; + ue_dummy_stack::args_t stack; }; dummy_ue(const args_t& args, srsran::radio_interface_phy* radio) : stack(args.stack, phy), phy("PHY") { - srsran_assert(phy.init(args.phy, &stack, radio), "Failed to initialise PHY"); + srsran_assert(phy.init(args.phy, &stack, radio) == SRSRAN_SUCCESS, "Failed to initialise PHY"); + phy.wait_initialize(); } bool start_cell_search(const srsue::phy_interface_stack_nr::cell_search_args_t& args) @@ -275,13 +276,13 @@ int main(int argc, char** argv) srsran::rf_args_t rf_args = {}; rf_args.type = "multi"; rf_args.log_level = args.rf_log_level; - rf_args.srate_hz = args.srate_hz; - rf_args.rx_gain = args.rf_rx_gain_dB; - rf_args.nof_carriers = 1; - rf_args.nof_antennas = 1; - rf_args.device_args = args.rf_device_args; - rf_args.device_name = args.rf_device_name; - rf_args.freq_offset = args.rf_freq_offset_Hz; + // rf_args.srate_hz = args.srate_hz; + rf_args.rx_gain = args.rf_rx_gain_dB; + rf_args.nof_carriers = 1; + rf_args.nof_antennas = 1; + rf_args.device_args = args.rf_device_args; + rf_args.device_name = args.rf_device_name; + rf_args.freq_offset = args.rf_freq_offset_Hz; // Instantiate std::shared_ptr r = std::make_shared(); @@ -301,23 +302,68 @@ int main(int argc, char** argv) } // Create dummy UE - dummy_ue::args_t ue_args = {}; + 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; + 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; - cell_search_req.ssb_pattern = args.ssb_pattern; - cell_search_req.duplex_mode = args.duplex_mode; - srsran_assert(ue.start_cell_search(cell_search_req), "Failed cell search start"); - - for (uint32_t i = 0; i < args.duration_ms; i++) { - ue.run_tti(); + // Base cell search arguments + srsue::phy_nr_sa::cell_search_args_t cs_args = {}; + cs_args.srate_hz = args.srate_hz; + cs_args.center_freq_hz = args.base_carrier.dl_center_frequency_hz; + cs_args.ssb_scs = args.ssb_scs; + cs_args.ssb_pattern = args.ssb_pattern; + cs_args.duplex_mode = args.duplex_mode; + + std::vector v_cs_args = {}; + /*if (std::isnormal(args.base_carrier.ssb_center_freq_hz)) { + cs_args.ssb_freq_hz = args.base_carrier.ssb_center_freq_hz; + v_cs_args.push_back(cs_args); + } else*/ + { + srsran::srsran_band_helper bands; + + // Deduce band number + uint16_t band = bands.get_band_from_dl_freq_Hz(args.base_carrier.dl_center_frequency_hz); + srsran_assert(band != UINT16_MAX, "Invalid band"); + + // Get sync raster + srsran::srsran_band_helper::sync_raster_t ss = bands.get_sync_raster(band, args.ssb_scs); + srsran_assert(ss.valid(), "Invalid synchronization raster"); + + // Calculate SSB center frequency boundaries + double ssb_bw_hz = SRSRAN_SSB_BW_SUBC * bands.get_ssb_scs(band); + double ssb_center_freq_min_hz = args.base_carrier.dl_center_frequency_hz - (args.srate_hz * 0.7 - ssb_bw_hz) / 2.0; + double ssb_center_freq_max_hz = args.base_carrier.dl_center_frequency_hz + (args.srate_hz * 0.7 - ssb_bw_hz) / 2.0; + uint32_t ssb_scs_hz = SRSRAN_SUBC_SPACING_NR(args.ssb_scs); + + // Iterate every possible synchronization raster + while (not ss.end()) { + // Get SSB center frequency + double abs_freq_ssb_hz = ss.get_frequency(); + + uint32_t offset = (uint32_t)std::abs(std::round(abs_freq_ssb_hz - args.base_carrier.dl_center_frequency_hz)); + + // Use frequency if it is within the range + if ((abs_freq_ssb_hz > ssb_center_freq_min_hz) and (abs_freq_ssb_hz < ssb_center_freq_max_hz) and + (offset % ssb_scs_hz == 0)) { + cs_args.ssb_freq_hz = abs_freq_ssb_hz; + v_cs_args.push_back(cs_args); + } + + // Next frequency + ss.next(); + } + } + + // For each SSB center frequency... + for (const srsue::phy_nr_sa::cell_search_args_t& cs_args_ : v_cs_args) { + // Transition PHY to cell search + srsran_assert(ue.start_cell_search(cs_args_), "Failed cell search start"); + + for (uint32_t i = 0; i < args.duration_ms; i++) { + ue.run_tti(); + } } // Tear down UE