mirror of https://github.com/PentHertz/srsLTE.git
Implement and test initial SFN synchronization for UE standalone mode
This commit is contained in:
parent
9e4e75bfda
commit
3b396c8a9a
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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...");
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue