Implement and test initial SFN synchronization for UE standalone mode

master
Xavier Arteaga 3 years ago committed by Xavier Arteaga
parent 9e4e75bfda
commit 3b396c8a9a

@ -33,9 +33,9 @@ public:
* @brief Describes a cell search result * @brief Describes a cell search result
*/ */
struct cell_search_result_t { struct cell_search_result_t {
bool cell_found = false; bool cell_found = false;
uint32_t pci = 0; ///< Physical Cell Identifier uint32_t pci = 0; ///< Physical Cell Identifier
srsran_pbch_msg_nr_t pbch_msg; ///< Packed PBCH message for the upper layers srsran_pbch_msg_nr_t pbch_msg = {}; ///< Packed PBCH message for the upper layers
srsran_csi_trs_measurements_t measurements = {}; ///< Measurements from SSB block 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 rf_channel_offset = 0; ///< Specifies the RF channel the NR carrier shall fill
uint32_t nof_carriers = 1; uint32_t nof_carriers = 1;
uint32_t max_nof_prb = 106; uint32_t max_nof_prb = 106;
double srate_hz = 23.04e6;
uint32_t nof_phy_threads = 3; uint32_t nof_phy_threads = 3;
uint32_t worker_cpu_mask = 0; uint32_t worker_cpu_mask = 0;
srsran::phy_log_args_t log = {}; srsran::phy_log_args_t log = {};
@ -277,7 +278,6 @@ public:
* @brief Describes cell search arguments * @brief Describes cell search arguments
*/ */
struct cell_search_args_t { struct cell_search_args_t {
double srate_hz;
double center_freq_hz; double center_freq_hz;
double ssb_freq_hz; double ssb_freq_hz;
srsran_subcarrier_spacing_t ssb_scs; srsran_subcarrier_spacing_t ssb_scs;

@ -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() void phy_nr_sa::init_background()
{ {
nr::sync_sa::args_t sync_args = {}; 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)) { if (not sync.init(sync_args, stack, radio)) {
logger.error("Error initialising SYNC"); logger.error("Error initialising SYNC");
return; 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 // Prepare cell search configuration from the request
nr::cell_search::cfg_t cfg = {}; 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.center_freq_hz = req.center_freq_hz;
cfg.ssb_freq_hz = req.ssb_freq_hz; cfg.ssb_freq_hz = req.ssb_freq_hz;
cfg.ssb_scs = req.ssb_scs; 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; selected_cell = req.carrier;
cmd_worker_cell.add_cmd([this, req]() { cmd_worker_cell.add_cmd([this, req]() {
// Request cell search to lower synchronization instance. // Request cell search to lower synchronization instance.
sync.cell_select_run(req); 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 // Setup carrier configuration asynchronously
cmd_worker.add_cmd([this]() { cmd_worker.add_cmd([this]() {
// Set UE configuration // Set UE configuration
bool ret = workers.set_config(config_nr); bool ret = workers.set_config(config_nr);

@ -210,7 +210,7 @@ void sync_sa::run_state_cell_search()
} }
// Run Searcher // 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) { if (cs_ret.result < 0) {
logger.error("Failed to run searcher. Transitioning to IDLE..."); logger.error("Failed to run searcher. Transitioning to IDLE...");
} }

@ -232,6 +232,11 @@ public:
return phy.start_cell_search(args); 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 run_tti() { stack.tick(); }
void stop() void stop()
{ {
@ -249,8 +254,140 @@ public:
} }
const ue_dummy_stack::metrics_t& get_metrics() const { return stack.get_metrics(); } 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) int main(int argc, char** argv)
{ {
srsran_debug_handle_crash(argc, argv); srsran_debug_handle_crash(argc, argv);
@ -312,66 +449,30 @@ int main(int argc, char** argv)
// Create dummy UE // Create dummy UE
dummy_ue::args_t ue_args = {}; 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.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()); dummy_ue ue(ue_args, radio.get());
// Base cell search arguments // Perform cell search
srsue::phy_nr_sa::cell_search_args_t cs_args = {}; cell_search_result_t found_cell = cell_search(args, ue);
cs_args.srate_hz = args.srate_hz;
cs_args.center_freq_hz = args.base_carrier.dl_center_frequency_hz; // Perform cell select
cs_args.ssb_scs = args.ssb_scs; if (found_cell.found) {
cs_args.ssb_pattern = args.ssb_pattern; srsue::phy_interface_stack_nr::cell_select_args_t cs_args = {};
cs_args.duplex_mode = args.duplex_mode; cs_args.ssb_cfg.srate_hz = args.srate_hz;
cs_args.ssb_cfg.center_freq_hz = args.base_carrier.dl_center_frequency_hz;
std::vector<srsue::phy_nr_sa::cell_search_args_t> v_cs_args = {}; cs_args.ssb_cfg.ssb_freq_hz = found_cell.ssb_abs_freq_hz;
/*if (std::isnormal(args.base_carrier.ssb_center_freq_hz)) { cs_args.ssb_cfg.scs = found_cell.ssb_scs;
cs_args.ssb_freq_hz = args.base_carrier.ssb_center_freq_hz; cs_args.ssb_cfg.pattern = found_cell.ssb_pattern;
v_cs_args.push_back(cs_args); cs_args.ssb_cfg.duplex_mode = found_cell.duplex_mode;
} else*/ cs_args.ssb_cfg.periodicity_ms = 10;
{ cs_args.carrier = args.base_carrier;
srsran::srsran_band_helper bands; cs_args.carrier.pci = found_cell.pci;
// Deduce band number srsran_assert(ue.start_cell_select(cs_args), "Failed to start cell selection\n");
uint16_t band = bands.get_band_from_dl_freq_Hz(args.base_carrier.dl_center_frequency_hz);
srsran_assert(band != UINT16_MAX, "Invalid band"); for (uint32_t i = 0; i < 1000; i++) {
// 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()) {
ue.run_tti(); ue.run_tti();
} }
} }
@ -379,44 +480,12 @@ int main(int argc, char** argv)
// Tear down UE // Tear down UE
ue.stop(); ue.stop();
// Stop Radio for (uint32_t i = 0; i < 1000; i++) {
radio->reset(); ue.run_tti();
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 // Stop Radio
radio = nullptr; radio->reset();
return 0; return 0;
} }

@ -26,6 +26,10 @@ public:
}; };
struct cell_search_metrics_t { 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_avg = 0.0f;
float epre_db_min = +INFINITY; float epre_db_min = +INFINITY;
float epre_db_max = -INFINITY; float epre_db_max = -INFINITY;
@ -154,6 +158,12 @@ public:
bool is_valid() const { return valid; } bool is_valid() const { return valid; }
const metrics_t& get_metrics() const { return metrics; } 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 {} 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 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) { if (not result.cell_found) {
logger.info("Cell search finished without detecting any cell"); logger.info("Cell search finished without detecting any cell");
// Flag as cell search is done
cell_search_finished = true;
return; 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 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]; 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_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_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.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_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.cfo_hz_avg = SRSRAN_VEC_SAFE_CMA(result.measurements.cfo_hz, m.cfo_hz_avg, m.count);
m.count++; m.count++;
// Flag as cell search is done
cell_search_finished = true;
} }
}; };

Loading…
Cancel
Save