From 3b396c8a9a985267de63928c78d26af3abdbbd30 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Fri, 24 Dec 2021 12:58:01 +0100 Subject: [PATCH] Implement and test initial SFN synchronization for UE standalone mode --- .../srsran/interfaces/ue_nr_interfaces.h | 8 +- srsue/src/phy/phy_nr_sa.cc | 6 +- srsue/src/phy/sync_sa.cc | 2 +- srsue/src/phy/test/nr_sa_cell_search_test.cc | 253 +++++++++++------- test/phy/dummy_ue_stack.h | 21 +- 5 files changed, 186 insertions(+), 104 deletions(-) diff --git a/lib/include/srsran/interfaces/ue_nr_interfaces.h b/lib/include/srsran/interfaces/ue_nr_interfaces.h index c2f359323..4c7502e9e 100644 --- a/lib/include/srsran/interfaces/ue_nr_interfaces.h +++ b/lib/include/srsran/interfaces/ue_nr_interfaces.h @@ -33,9 +33,9 @@ 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 + 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 }; @@ -185,6 +185,7 @@ 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 = {}; @@ -277,7 +278,6 @@ 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; diff --git a/srsue/src/phy/phy_nr_sa.cc b/srsue/src/phy/phy_nr_sa.cc index 5c6453bbf..65738635c 100644 --- a/srsue/src/phy/phy_nr_sa.cc +++ b/srsue/src/phy/phy_nr_sa.cc @@ -95,7 +95,7 @@ int phy_nr_sa::init(const phy_args_nr_t& args_, stack_interface_phy_nr* stack_, void phy_nr_sa::init_background() { nr::sync_sa::args_t sync_args = {}; - sync_args.srate_hz = srsran_sampling_freq_hz(args.max_nof_prb); + sync_args.srate_hz = args.srate_hz; if (not sync.init(sync_args, stack, radio)) { logger.error("Error initialising SYNC"); return; @@ -165,7 +165,7 @@ 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 = req.srate_hz; + 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; @@ -204,7 +204,6 @@ 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); }); @@ -245,7 +244,6 @@ bool phy_nr_sa::set_config(const srsran::phy_cfg_nr_t& cfg) // Setup carrier configuration asynchronously cmd_worker.add_cmd([this]() { - // Set UE configuration bool ret = workers.set_config(config_nr); diff --git a/srsue/src/phy/sync_sa.cc b/srsue/src/phy/sync_sa.cc index d0d530608..8e3063798 100644 --- a/srsue/src/phy/sync_sa.cc +++ b/srsue/src/phy/sync_sa.cc @@ -210,7 +210,7 @@ void sync_sa::run_state_cell_search() } // Run Searcher - cs_ret = searcher.run_slot(rx_buffer, 2 * slot_sz); + cs_ret = searcher.run_slot(rx_buffer, slot_sz); if (cs_ret.result < 0) { logger.error("Failed to run searcher. Transitioning to IDLE..."); } 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 4b2dc5cac..b9e698ff0 100644 --- a/srsue/src/phy/test/nr_sa_cell_search_test.cc +++ b/srsue/src/phy/test/nr_sa_cell_search_test.cc @@ -232,6 +232,11 @@ public: return phy.start_cell_search(args); } + bool start_cell_select(const srsue::phy_interface_stack_nr::cell_select_args_t& args) + { + return phy.start_cell_select(args); + } + void run_tti() { stack.tick(); } void stop() { @@ -249,8 +254,140 @@ public: } const ue_dummy_stack::metrics_t& get_metrics() const { return stack.get_metrics(); } + void reset_metrics() { stack.reset_metrics(); } }; +struct cell_search_result_t { + bool found = false; + double ssb_abs_freq_hz = 0.0f; + srsran_subcarrier_spacing_t ssb_scs = srsran_subcarrier_spacing_15kHz; + srsran_ssb_patern_t ssb_pattern = SRSRAN_SSB_PATTERN_A; + srsran_duplex_mode_t duplex_mode = SRSRAN_DUPLEX_MODE_FDD; + srsran_mib_nr_t mib = {}; + uint32_t pci = 0; +}; + +/* + * The following function searches for cells in all possible SSB absolute frequencies within the baseband range. It + * returns the first found cell. + */ +static cell_search_result_t cell_search(const args_t& args, dummy_ue& ue) +{ + cell_search_result_t ret = {}; + + // Base cell search arguments + srsue::phy_nr_sa::cell_search_args_t cs_args = {}; + 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; + + // Deduce band number + srsran::srsran_band_helper bands; + uint16_t band = bands.get_band_from_dl_freq_Hz(args.base_carrier.dl_center_frequency_hz); + srsran_assert(band != UINT16_MAX, "Invalid band"); + + // 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); + + // 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"); + + // Iterate every possible frequency in the synchronization raster + while (not ss.end()) { + // Get SSB center frequency + cs_args.ssb_freq_hz = ss.get_frequency(); + + // Advance SSB frequency raster + ss.next(); + + // Calculate frequency offset between the base-band center frequency and the SSB absolute frequency + uint32_t offset_hz = (uint32_t)std::abs(std::round(cs_args.ssb_freq_hz - args.base_carrier.dl_center_frequency_hz)); + + // The SSB absolute frequency is invalid if it is outside the range and the offset is NOT multiple of the subcarrier + // spacing + if ((cs_args.ssb_freq_hz < ssb_center_freq_min_hz) or (cs_args.ssb_freq_hz > ssb_center_freq_max_hz) or + (offset_hz % ssb_scs_hz != 0)) { + // Skip this frequency + continue; + } + + // Transition PHY to cell search + srsran_assert(ue.start_cell_search(cs_args), "Failed cell search start"); + + // Run slot until the PHY reported to the stack + while (not ue.cell_search_read_and_clear()) { + ue.run_tti(); + } + + const ue_dummy_stack::metrics_t& metrics = ue.get_metrics(); + + // Skip printing cell search findings if no SSB is found + if (metrics.cell_search.empty()) { + continue; + } + + // Print found cells + printf("Cells found at SSB center frequency %.2f MHz:\n", cs_args.ssb_freq_hz / 1e6); + printf("| %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s |\n", + "PCI", + "SSB", + "Count", + "RSRP min", + "RSRP avg", + "RSRP max", + "SNR min", + "SNR avg", + "SNR max", + "CFO min", + "CFO avg", + "CFO max"); + + // For each found PCI... + for (auto& pci : metrics.cell_search) { + // For each found beam... + for (auto& ssb : pci.second) { + // Print stats + printf("| %10d | %10d | %10d | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | " + "%+10.1f |\n", + pci.first, + ssb.first, + (uint32_t)ssb.second.count, + ssb.second.rsrp_db_min, + ssb.second.rsrp_db_avg, + ssb.second.rsrp_db_max, + ssb.second.snr_db_min, + ssb.second.snr_db_avg, + ssb.second.snr_db_max, + ssb.second.cfo_hz_min, + ssb.second.cfo_hz_avg, + ssb.second.cfo_hz_max); + + // If this is the first found cell, then set return value + if (not ret.found) { + ret.found = true; + ret.ssb_abs_freq_hz = cs_args.ssb_freq_hz; + ret.ssb_scs = cs_args.ssb_scs; + ret.ssb_pattern = cs_args.ssb_pattern; + ret.duplex_mode = cs_args.duplex_mode; + ret.pci = pci.first; + srsran_assert(srsran_pbch_msg_nr_mib_unpack(&ssb.second.last_result.pbch_msg, &ret.mib) == SRSRAN_SUCCESS, + "Error unpacking MIB"); + } + } + } + + // Reset stack metrics + ue.reset_metrics(); + } + + return ret; +} + int main(int argc, char** argv) { srsran_debug_handle_crash(argc, argv); @@ -312,66 +449,30 @@ int main(int argc, char** argv) // Create dummy UE dummy_ue::args_t ue_args = {}; + ue_args.phy.srate_hz = args.srate_hz; 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()); - // 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"); - - // Run slot until the PHY reported to the stack - while (not ue.cell_search_read_and_clear()) { + // Perform cell search + cell_search_result_t found_cell = cell_search(args, ue); + + // Perform cell select + if (found_cell.found) { + srsue::phy_interface_stack_nr::cell_select_args_t cs_args = {}; + cs_args.ssb_cfg.srate_hz = args.srate_hz; + cs_args.ssb_cfg.center_freq_hz = args.base_carrier.dl_center_frequency_hz; + cs_args.ssb_cfg.ssb_freq_hz = found_cell.ssb_abs_freq_hz; + cs_args.ssb_cfg.scs = found_cell.ssb_scs; + cs_args.ssb_cfg.pattern = found_cell.ssb_pattern; + cs_args.ssb_cfg.duplex_mode = found_cell.duplex_mode; + cs_args.ssb_cfg.periodicity_ms = 10; + cs_args.carrier = args.base_carrier; + cs_args.carrier.pci = found_cell.pci; + + srsran_assert(ue.start_cell_select(cs_args), "Failed to start cell selection\n"); + + for (uint32_t i = 0; i < 1000; i++) { ue.run_tti(); } } @@ -379,44 +480,12 @@ int main(int argc, char** argv) // Tear down UE ue.stop(); - // Stop Radio - radio->reset(); - - const ue_dummy_stack::metrics_t& metrics = ue.get_metrics(); - printf("| %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s | %10s |\n", - "PCI", - "SSB", - "Count", - "RSRP min", - "RSRP avg", - "RSRP max", - "SNR min", - "SNR avg", - "SNR max", - "CFO min", - "CFO avg", - "CFO max"); - for (auto& pci : metrics.cell_search) { - for (auto& ssb : pci.second) { - printf("| %10d | %10d | %10d | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | %+10.1f | " - "%+10.1f |\n", - pci.first, - ssb.first, - (uint32_t)ssb.second.count, - ssb.second.rsrp_db_min, - ssb.second.rsrp_db_avg, - ssb.second.rsrp_db_max, - ssb.second.snr_db_min, - ssb.second.snr_db_avg, - ssb.second.snr_db_max, - ssb.second.cfo_hz_min, - ssb.second.cfo_hz_avg, - ssb.second.cfo_hz_max); - } + for (uint32_t i = 0; i < 1000; i++) { + ue.run_tti(); } - // Erase radio - radio = nullptr; + // Stop Radio + radio->reset(); return 0; } \ No newline at end of file diff --git a/test/phy/dummy_ue_stack.h b/test/phy/dummy_ue_stack.h index 5bbbc20fd..6518e9e6c 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -26,6 +26,10 @@ public: }; struct cell_search_metrics_t { + // Last cell search result for the PCI and SSB candidate + srsue::stack_interface_phy_nr::cell_search_result_t last_result; + + // Signal Measurements float epre_db_avg = 0.0f; float epre_db_min = +INFINITY; float epre_db_max = -INFINITY; @@ -154,6 +158,12 @@ public: bool is_valid() const { return valid; } const metrics_t& get_metrics() const { return metrics; } + void reset_metrics() + { + metrics.cell_search.clear(); + metrics.prach.clear(); + metrics.sr_count = 0; + } void set_phy_config_complete(bool status) override {} @@ -168,11 +178,12 @@ public: void cell_search_found_cell(const cell_search_result_t& result) override { - // Flag as cell search is done - cell_search_finished = true; - if (not result.cell_found) { logger.info("Cell search finished without detecting any cell"); + + // Flag as cell search is done + cell_search_finished = true; + return; } @@ -205,6 +216,7 @@ public: "Cell found pci=%d %s %s ASN1: %s", result.pci, mib_info.data(), csi_info.data(), json.to_string().c_str()); cell_search_metrics_t& m = metrics.cell_search[result.pci][result.pbch_msg.ssb_idx]; + m.last_result = result; m.epre_db_min = SRSRAN_MIN(m.epre_db_min, result.measurements.epre_dB); m.epre_db_max = SRSRAN_MAX(m.epre_db_max, result.measurements.epre_dB); m.epre_db_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.epre_dB, m.epre_db_avg, m.count); @@ -218,6 +230,9 @@ public: m.cfo_hz_max = SRSRAN_MAX(m.cfo_hz_max, result.measurements.cfo_hz); m.cfo_hz_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.cfo_hz, m.cfo_hz_avg, m.count); m.count++; + + // Flag as cell search is done + cell_search_finished = true; } };