From 4f86c2fac7949c20efad974927e12982e2f9c9d9 Mon Sep 17 00:00:00 2001 From: Xavier Arteaga Date: Mon, 20 Dec 2021 21:13:31 +0100 Subject: [PATCH] Added more information in NR PHY SA cell search test --- srsue/src/phy/test/gnb_rf_emulator.h | 7 ++ srsue/src/phy/test/nr_sa_cell_search_test.cc | 116 +++++++++++++------ test/phy/dummy_ue_stack.h | 103 +++++++++++----- 3 files changed, 163 insertions(+), 63 deletions(-) diff --git a/srsue/src/phy/test/gnb_rf_emulator.h b/srsue/src/phy/test/gnb_rf_emulator.h index 36c0af036..ab54efc88 100644 --- a/srsue/src/phy/test/gnb_rf_emulator.h +++ b/srsue/src/phy/test/gnb_rf_emulator.h @@ -64,6 +64,8 @@ public: uint32_t ssb_periodicity_ms; srsran_duplex_mode_t duplex_mode; std::set pci_list; + float channel_hst_fd_hz = 0.0f; + float channel_hst_period_s = 7.2f; }; gnb_rf_emulator(const args_t& args) @@ -83,6 +85,11 @@ public: gnb_args.ssb_periodicity_ms = args.ssb_periodicity_ms; gnb_args.duplex_mode = args.duplex_mode; + gnb_args.channel.hst_enable = std::isnormal(args.channel_hst_fd_hz) and std::isnormal(args.channel_hst_period_s); + gnb_args.channel.hst_fd_hz = args.channel_hst_fd_hz; + gnb_args.channel.hst_period_s = args.channel_hst_period_s; + gnb_args.channel.enable = gnb_args.channel.hst_enable; + gnb_vector.emplace_back(std::make_shared(gnb_args)); } 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 84cb16f1d..4b2dc5cac 100644 --- a/srsue/src/phy/test/nr_sa_cell_search_test.cc +++ b/srsue/src/phy/test/nr_sa_cell_search_test.cc @@ -11,7 +11,6 @@ */ #include "gnb_rf_emulator.h" -#include "srsran/asn1/rrc_nr.h" #include "srsran/common/band_helper.h" #include "srsran/common/crash_handler.h" #include "srsran/common/string_helpers.h" @@ -46,8 +45,7 @@ public: pending_tti_cvar.notify_all(); } - uint32_t count = 0; - void tick() + void tick() { // Wait for TTI to get processed std::unique_lock lock(pending_tti_mutex); @@ -79,7 +77,9 @@ struct args_t { std::string stack_log_level = "warning"; // Simulation parameters - uint32_t sim_ssb_periodicity_ms = 10; + uint32_t sim_ssb_periodicity_ms = 10; + float sim_channel_hst_fd_hz = 0.0f; + float sim_channel_hst_period_s = 7.2f; std::set sim_cell_pci; // RF parameters @@ -152,34 +152,36 @@ int parse_args(int argc, char** argv, args_t& args) bpo::options_description simulation("Mode 2: Simulation options (enabled if simulation_cell_list is not empty)"); // clang-format off - over_the_air.add_options() - ("rf.device_name", bpo::value(&args.rf_device_name)->default_value(args.rf_device_name), "RF Device Name") - ("rf.device_args", bpo::value(&args.rf_device_args)->default_value(args.rf_device_args), "RF Device arguments") - ("rf.log_level", bpo::value(&args.rf_log_level)->default_value(args.rf_log_level), "RF Log level (none, warning, info, debug)") - ("rf.rx_gain", bpo::value(&args.rf_rx_gain_dB)->default_value(args.rf_rx_gain_dB), "RF Receiver gain in dB") - ("rf.freq_offset", bpo::value(&args.rf_freq_offset_Hz)->default_value(args.rf_freq_offset_Hz), "RF Frequency offset") - ; + over_the_air.add_options() + ("rf.device_name", bpo::value(&args.rf_device_name)->default_value(args.rf_device_name), "RF Device Name") + ("rf.device_args", bpo::value(&args.rf_device_args)->default_value(args.rf_device_args), "RF Device arguments") + ("rf.log_level", bpo::value(&args.rf_log_level)->default_value(args.rf_log_level), "RF Log level (none, warning, info, debug)") + ("rf.rx_gain", bpo::value(&args.rf_rx_gain_dB)->default_value(args.rf_rx_gain_dB), "RF Receiver gain in dB") + ("rf.freq_offset", bpo::value(&args.rf_freq_offset_Hz)->default_value(args.rf_freq_offset_Hz), "RF Frequency offset") + ; - simulation.add_options() - ("sim.pci_list", bpo::value(&simulation_cell_list)->default_value(simulation_cell_list), "Comma separated PCI cell list to simulate") - ("sim.bw", bpo::value(&args.base_carrier.nof_prb)->default_value(args.base_carrier.nof_prb), "Carrier bandwidth in RB") - ("sim.ssb_period", bpo::value(&args.sim_ssb_periodicity_ms)->default_value(args.sim_ssb_periodicity_ms), "SSB period in ms") - ; + simulation.add_options() + ("sim.pci_list", bpo::value(&simulation_cell_list)->default_value(simulation_cell_list), "Comma separated PCI cell list to simulate") + ("sim.bw", bpo::value(&args.base_carrier.nof_prb)->default_value(args.base_carrier.nof_prb), "Carrier bandwidth in RB") + ("sim.ssb_period", bpo::value(&args.sim_ssb_periodicity_ms)->default_value(args.sim_ssb_periodicity_ms), "SSB period in ms") + ("sim.channel.hst.fd", bpo::value(&args.sim_channel_hst_fd_hz)->default_value(args.sim_channel_hst_fd_hz), "Channel emulator HST maximum frequency") + ("sim.channel.hst.period", bpo::value(&args.sim_channel_hst_period_s)->default_value(args.sim_channel_hst_period_s), "Channel emulator HST period") + ; - phy.add_options() - ("phy.srate", bpo::value(&args.srate_hz)->default_value(args.srate_hz), "Sampling Rate in Hz") - ("phy.log.level", bpo::value(&args.phy_log_level)->default_value(args.phy_log_level), "Physical layer logging level") - ; + phy.add_options() + ("phy.srate", bpo::value(&args.srate_hz)->default_value(args.srate_hz), "Sampling Rate in Hz") + ("phy.log.level", bpo::value(&args.phy_log_level)->default_value(args.phy_log_level), "Physical layer logging level") + ; - stack.add_options() - ("stack.log.level", bpo::value(&args.stack_log_level)->default_value(args.stack_log_level), "Stack logging level") - ; + stack.add_options() + ("stack.log.level", bpo::value(&args.stack_log_level)->default_value(args.stack_log_level), "Stack logging level") + ; - options.add(over_the_air).add(simulation).add(phy).add(stack).add_options() - ("help,h", "Show this message") - ("duration", bpo::value(&args.duration_ms)->default_value(args.duration_ms), "Duration of the test in milli-seconds") - ("freq_dl", bpo::value(&args.base_carrier.dl_center_frequency_hz)->default_value(args.base_carrier.dl_center_frequency_hz), "Carrier center frequency in Hz") - ; + options.add(over_the_air).add(simulation).add(phy).add(stack).add_options() + ("help,h", "Show this message") + ("duration", bpo::value(&args.duration_ms)->default_value(args.duration_ms), "Duration of the test in milli-seconds") + ("freq_dl", bpo::value(&args.base_carrier.dl_center_frequency_hz)->default_value(args.base_carrier.dl_center_frequency_hz), "Carrier center frequency in Hz") + ; // clang-format on bpo::variables_map vm; @@ -223,6 +225,8 @@ public: phy.wait_initialize(); } + bool cell_search_read_and_clear() { return stack.get_cell_search_finished(); } + bool start_cell_search(const srsue::phy_interface_stack_nr::cell_search_args_t& args) { return phy.start_cell_search(args); @@ -243,6 +247,8 @@ public: // Stop PHY phy.stop(); } + + const ue_dummy_stack::metrics_t& get_metrics() const { return stack.get_metrics(); } }; int main(int argc, char** argv) @@ -270,6 +276,8 @@ int main(int argc, char** argv) gnb_args.ssb_scs = args.ssb_scs; gnb_args.duplex_mode = args.duplex_mode; gnb_args.pci_list = args.sim_cell_pci; + gnb_args.channel_hst_fd_hz = args.sim_channel_hst_fd_hz; + gnb_args.channel_hst_period_s = args.sim_channel_hst_period_s; radio = std::make_shared(gnb_args); } else { @@ -277,13 +285,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(); @@ -362,7 +370,8 @@ int main(int argc, char** argv) // 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++) { + // Run slot until the PHY reported to the stack + while (not ue.cell_search_read_and_clear()) { ue.run_tti(); } } @@ -373,8 +382,41 @@ int main(int argc, char** argv) // 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); + } + } + // Erase radio radio = nullptr; 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 76ba2d60b..5bbbc20fd 100644 --- a/test/phy/dummy_ue_stack.h +++ b/test/phy/dummy_ue_stack.h @@ -25,7 +25,24 @@ public: uint32_t count; }; + struct cell_search_metrics_t { + float epre_db_avg = 0.0f; + float epre_db_min = +INFINITY; + float epre_db_max = -INFINITY; + float rsrp_db_avg = 0.0f; + float rsrp_db_min = +INFINITY; + float rsrp_db_max = -INFINITY; + float snr_db_avg = 0.0f; + float snr_db_min = +INFINITY; + float snr_db_max = -INFINITY; + float cfo_hz_avg = 0.0f; + float cfo_hz_min = +INFINITY; + float cfo_hz_max = -INFINITY; + uint32_t count = 0; + }; + struct metrics_t { + std::map > cell_search; std::map prach = {}; ///< PRACH metrics indexed with premable index uint32_t sr_count = 0; ///< Counts number of transmitted SR }; @@ -48,6 +65,8 @@ private: dummy_tx_harq_entity tx_harq_proc; dummy_rx_harq_entity rx_harq_proc; + std::atomic cell_search_finished = {false}; + public: struct args_t { uint16_t rnti = 0x1234; ///< C-RNTI for PUSCH and PDSCH transmissions @@ -134,39 +153,71 @@ public: } bool is_valid() const { return valid; } - metrics_t get_metrics() { return metrics; } + const metrics_t& get_metrics() const { return metrics; } void set_phy_config_complete(bool status) override {} + bool get_cell_search_finished() + { + bool ret = cell_search_finished; + + cell_search_finished = false; + + return ret; + } + void cell_search_found_cell(const cell_search_result_t& result) override { - 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); + // Flag as cell search is done + cell_search_finished = true; - // 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"); + if (not result.cell_found) { + logger.info("Cell search finished without detecting any cell"); + return; } + + // Pack PBCH message bits + std::array bit_pack_pbch_msg = {}; + asn1::cbit_ref cbit(bit_pack_pbch_msg.data(), bit_pack_pbch_msg.size()); + srsran_bit_pack_vector((uint8_t*)result.pbch_msg.payload, bit_pack_pbch_msg.data(), SRSRAN_PBCH_MSG_NR_SZ); + + // Unpack MIB with ASN1 + asn1::rrc_nr::bcch_bch_msg_s bcch; + bcch.unpack(cbit); + + // Convert MIB to JSON + asn1::json_writer json; + bcch.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()); + + cell_search_metrics_t& m = metrics.cell_search[result.pci][result.pbch_msg.ssb_idx]; + 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); + m.rsrp_db_min = SRSRAN_MIN(m.rsrp_db_min, result.measurements.rsrp_dB); + m.rsrp_db_max = SRSRAN_MAX(m.rsrp_db_max, result.measurements.rsrp_dB); + m.rsrp_db_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.rsrp_dB, m.rsrp_db_avg, m.count); + m.snr_db_min = SRSRAN_MIN(m.snr_db_min, result.measurements.snr_dB); + m.snr_db_max = SRSRAN_MAX(m.snr_db_max, result.measurements.snr_dB); + m.snr_db_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.snr_dB, m.snr_db_avg, m.count); + m.cfo_hz_min = SRSRAN_MIN(m.cfo_hz_min, result.measurements.cfo_hz); + 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++; } };