Implement and test initial SFN synchronization for UE standalone mode

This commit is contained in:
Xavier Arteaga 2021-12-24 12:58:01 +01:00 committed by Xavier Arteaga
parent 9e4e75bfda
commit 3b396c8a9a
5 changed files with 183 additions and 101 deletions

View File

@ -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;

View File

@ -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);

View File

@ -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...");
}

View File

@ -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;
// Perform cell search
cell_search_result_t found_cell = cell_search(args, ue);
std::vector<srsue::phy_nr_sa::cell_search_args_t> 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;
// 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;
// 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");
srsran_assert(ue.start_cell_select(cs_args), "Failed to start cell selection\n");
// 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()) {
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();
for (uint32_t i = 0; i < 1000; i++) {
ue.run_tti();
}
// 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;
}

View File

@ -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;
}
};