Implemented UE open loop CFO compensation for NR

This commit is contained in:
Xavier Arteaga 2021-08-24 10:02:24 +02:00 committed by Xavier Arteaga
parent 5aa2279990
commit 83b9b2d1ff
9 changed files with 132 additions and 62 deletions

View File

@ -166,17 +166,21 @@ public:
};
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;
uint32_t nof_phy_threads = 3;
uint32_t worker_cpu_mask = 0;
srsran::phy_log_args_t log = {};
srsran_ue_dl_nr_args_t dl = {};
srsran_ue_ul_nr_args_t ul = {};
std::set<uint32_t> fixed_sr = {1};
uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report
bool store_pdsch_ko = false;
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;
uint32_t nof_phy_threads = 3;
uint32_t worker_cpu_mask = 0;
srsran::phy_log_args_t log = {};
srsran_ue_dl_nr_args_t dl = {};
srsran_ue_ul_nr_args_t ul = {};
std::set<uint32_t> fixed_sr = {1};
uint32_t fix_wideband_cqi = 15; // Set to a non-zero value for fixing the wide-band CQI report
bool store_pdsch_ko = false;
float trs_epre_ema_alpha = 0.1f; ///< EPRE measurement exponential average alpha
float trs_rsrp_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha
float trs_cfo_ema_alpha = 0.1f; ///< RSRP measurement exponential average alpha
bool enable_worker_cfo = true; ///< Enable/Disable open loop CFO correction at the workers
phy_args_nr_t()
{

View File

@ -343,12 +343,13 @@ uint32_t srsran_csi_meas_info(const srsran_csi_trs_measurements_t* meas, char* s
return srsran_print_check(str,
str_len,
0,
"rsrp=%+.1f epre=%+.1f n0=%+.1f snr=%+.1f cfo=%+.1f delay=%+.1f",
"rsrp=%+.1f epre=%+.1f n0=%+.1f snr=%+.1f cfo=%+.1f cfo_max=%.0f delay=%+.1f",
meas->rsrp_dB,
meas->epre_dB,
meas->n0_dB,
meas->snr_dB,
meas->cfo_hz,
meas->cfo_hz_max,
meas->delay_us);
}

View File

@ -59,6 +59,7 @@ private:
cf_t* prach_ptr = nullptr;
float prach_power = 0;
srsran::phy_common_interface::worker_context_t context = {};
float ul_ext_cfo = 0.0f; ///< UL CFO external offset in Hz
};
} // namespace nr

View File

@ -60,6 +60,13 @@ private:
std::mutex csi_measurements_mutex;
std::array<srsran_csi_channel_measurements_t, SRSRAN_CSI_MAX_NOF_RESOURCES> csi_measurements = {};
/// TRS measurements
mutable std::mutex trs_measurements_mutex;
srsran_csi_trs_measurements_t trs_measurements = {};
/// Other measurements
std::atomic<float> ul_ext_cfo_hz = {0.0f};
/**
* @brief Resets all metrics (unprotected)
*/
@ -453,6 +460,64 @@ public:
return;
}
}
/**
* @brief Processes a new Tracking Reference Signal (TRS) measurement
* @param new_measure New measurement
* @param resource_set_id NZP-CSI-RS resource set identifier used for the channel measurement if it is configured from
* a NZP-CSI-RS
* @param K_csi_rs Number of NZP-CSI-RS resources used for the measurement, set to 0 if another type of signal is
* measured (i.e. SSB)
*/
void new_csi_trs_measurement(const srsran_csi_trs_measurements_t& new_meas,
uint32_t resource_set_id = 0,
uint32_t K_csi_rs = 0)
{
// Compute channel metrics and push it
ch_metrics_t new_ch_metrics = {};
new_ch_metrics.sinr = new_meas.snr_dB;
new_ch_metrics.rsrp = new_meas.rsrp_dB;
new_ch_metrics.rsrq = 0.0f; // Not supported
new_ch_metrics.rssi = 0.0f; // Not supported
new_ch_metrics.sync_err = new_meas.delay_us;
set_channel_metrics(new_ch_metrics);
// Compute synch metrics and report it to the PHY state
sync_metrics_t new_sync_metrics = {};
new_sync_metrics.cfo = new_meas.cfo_hz;
set_sync_metrics(sync_metrics);
// Convert to CSI channel measurement and report new NZP-CSI-RS measurement to the PHY state
srsran_csi_channel_measurements_t measurements = {};
measurements.cri = 0;
measurements.wideband_rsrp_dBm = new_meas.rsrp_dB;
measurements.wideband_epre_dBm = new_meas.epre_dB;
measurements.wideband_snr_db = new_meas.snr_dB;
measurements.nof_ports = 1; // Other values are not supported
measurements.K_csi_rs = K_csi_rs;
new_nzp_csi_rs_channel_measurement(measurements, resource_set_id);
trs_measurements_mutex.lock();
trs_measurements.rsrp_dB = SRSRAN_VEC_SAFE_EMA(new_meas.rsrp_dB, trs_measurements.rsrp_dB, args.trs_epre_ema_alpha);
trs_measurements.epre_dB = SRSRAN_VEC_SAFE_EMA(new_meas.epre_dB, trs_measurements.epre_dB, args.trs_rsrp_ema_alpha);
trs_measurements.cfo_hz = SRSRAN_VEC_SAFE_EMA(new_meas.cfo_hz, trs_measurements.cfo_hz, args.trs_cfo_ema_alpha);
trs_measurements.nof_re++;
trs_measurements_mutex.unlock();
}
float get_dl_cfo()
{
std::lock_guard<std::mutex> lock(trs_measurements_mutex);
return trs_measurements.cfo_hz;
}
float get_ul_cfo() const
{
std::lock_guard<std::mutex> lock(trs_measurements_mutex);
return trs_measurements.cfo_hz + ul_ext_cfo_hz;
}
void set_ul_ext_cfo(float ext_cfo_hz) { ul_ext_cfo_hz = ext_cfo_hz; }
};
} // namespace nr
} // namespace srsue

View File

@ -49,6 +49,12 @@ public:
void clear_pending_grants() override;
void get_metrics(phy_metrics_t& m);
int tx_request(const tx_request_t& request) override;
/**
* @brief Sets external CFO to compensate UL signal frequency offset
* @param ext_cfo_hz External CFO in Hz
*/
void set_ul_ext_cfo(float ext_cfo_hz) { phy_state.set_ul_ext_cfo(ext_cfo_hz); }
};
} // namespace nr

View File

@ -27,7 +27,7 @@ struct info_metrics_t {
#define PHY_METRICS_SET(PARAM) \
do { \
PARAM = PARAM + (other.PARAM - PARAM) / count; \
PARAM = SRSRAN_VEC_SAFE_CMA(other.PARAM, PARAM, count); \
} while (false)
struct sync_metrics_t {
@ -41,12 +41,12 @@ struct sync_metrics_t {
void set(const sync_metrics_t& other)
{
count++;
ta_us = other.ta_us;
distance_km = other.distance_km;
speed_kmph = other.speed_kmph;
PHY_METRICS_SET(cfo);
PHY_METRICS_SET(sfo);
count++;
}
void reset()

View File

@ -286,10 +286,11 @@ bool cc_worker::decode_pdsch_dl()
srsran_sch_cfg_nr_info(&pdsch_cfg, str_extra.data(), (uint32_t)str_extra.size());
logger.info(pdsch_res.tb[0].payload,
pdsch_cfg.grant.tb[0].tbs / 8,
"PDSCH: cc=%d pid=%d %s\n%s",
"PDSCH: cc=%d pid=%d %s cfo=%.1f\n%s",
cc_idx,
pid,
str.data(),
ue_dl.chest.cfo,
str_extra.data());
} else {
logger.info(pdsch_res.tb[0].payload,
@ -381,27 +382,13 @@ bool cc_worker::measure_csi()
logger.debug("SSB-CSI: %s", str.data());
}
// Compute channel metrics and push it
ch_metrics_t ch_metrics = {};
ch_metrics.sinr = meas.snr_dB;
ch_metrics.rsrp = meas.rsrp_dB;
ch_metrics.rsrq = 0.0f; // Not supported
ch_metrics.rssi = 0.0f; // Not supported
ch_metrics.sync_err =
meas.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs));
phy.set_channel_metrics(ch_metrics);
// Compute synch metrics and report it to the PHY state
sync_metrics_t sync_metrics = {};
sync_metrics.cfo = meas.cfo_hz;
phy.set_sync_metrics(sync_metrics);
// Report SSB candidate channel measurement to the PHY state
// ...
phy.new_csi_trs_measurement(meas);
}
}
// Iterate all NZP-CSI-RS marked as TRS and perform channel measurements
bool estimate_fft = false;
for (uint32_t resource_set_id = 0; resource_set_id < SRSRAN_PHCH_CFG_MAX_NOF_CSI_RS_SETS; resource_set_id++) {
// Select NZP-CSI-RS set
const srsran_csi_rs_nzp_set_t& nzp_set = phy.cfg.pdsch.nzp_csi_rs_sets[resource_set_id];
@ -411,6 +398,12 @@ bool cc_worker::measure_csi()
continue;
}
// Run FFT if not done before in this slot
if (not estimate_fft) {
srsran_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg);
estimate_fft = true;
}
// Perform measurement, n > 0 is any measurement is performed, n = 0 otherwise
srsran_csi_trs_measurements_t trs_measurements = {};
int n = srsran_ue_dl_nr_csi_measure_trs(&ue_dl, &dl_slot_cfg, &nzp_set, &trs_measurements);
@ -430,30 +423,7 @@ bool cc_worker::measure_csi()
logger.debug("NZP-CSI-RS (TRS): id=%d %s", resource_set_id, str.data());
}
// Compute channel metrics and push it
ch_metrics_t ch_metrics = {};
ch_metrics.sinr = trs_measurements.snr_dB;
ch_metrics.rsrp = trs_measurements.rsrp_dB;
ch_metrics.rsrq = 0.0f; // Not supported
ch_metrics.rssi = 0.0f; // Not supported
ch_metrics.sync_err =
trs_measurements.delay_us / (float)(ue_dl.fft->fft_plan.size * SRSRAN_SUBC_SPACING_NR(phy.cfg.carrier.scs));
phy.set_channel_metrics(ch_metrics);
// Compute synch metrics and report it to the PHY state
sync_metrics_t sync_metrics = {};
sync_metrics.cfo = trs_measurements.cfo_hz;
phy.set_sync_metrics(sync_metrics);
// Convert to CSI channel measurement and report new NZP-CSI-RS measurement to the PHY state
srsran_csi_channel_measurements_t measurements = {};
measurements.cri = 0;
measurements.wideband_rsrp_dBm = trs_measurements.rsrp_dB;
measurements.wideband_epre_dBm = trs_measurements.epre_dB;
measurements.wideband_snr_db = trs_measurements.snr_dB;
measurements.nof_ports = 1; // Other values are not supported
measurements.K_csi_rs = (uint32_t)n;
phy.new_nzp_csi_rs_channel_measurement(measurements, resource_set_id);
phy.new_csi_trs_measurement(trs_measurements, resource_set_id, (uint32_t)n);
}
// Iterate all NZP-CSI-RS not marked as TRS and perform channel measurements
@ -466,6 +436,12 @@ bool cc_worker::measure_csi()
continue;
}
// Run FFT if not done before in this slot
if (not estimate_fft) {
srsran_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg);
estimate_fft = true;
}
// Perform channel measurement, n > 0 is any measurement is performed, n = 0 otherwise
srsran_csi_channel_measurements_t measurements = {};
int n = srsran_ue_dl_nr_csi_measure_channel(&ue_dl, &dl_slot_cfg, &nzp_set, &measurements);
@ -504,6 +480,22 @@ bool cc_worker::work_dl()
return true;
}
// Measure CSI
if (not measure_csi()) {
logger.error("Error measuring, aborting work DL");
return false;
}
// Compensate CFO from TRS measurements
if (std::isnormal(phy.args.enable_worker_cfo)) {
float dl_cfo_hz = phy.get_dl_cfo();
for (cf_t* b : rx_buffer) {
if (b != nullptr and ue_ul.ifft.sf_sz != 0) {
srsran_vec_apply_cfo(b, dl_cfo_hz / (1000.0f * ue_ul.ifft.sf_sz), b, ue_ul.ifft.sf_sz);
}
}
}
// Run FFT
srsran_ue_dl_nr_estimate_fft(&ue_dl, &dl_slot_cfg);
@ -519,12 +511,6 @@ bool cc_worker::work_dl()
return false;
}
// Measure CSI
if (not measure_csi()) {
logger.error("Error measuring, aborting work DL");
return false;
}
return true;
}
@ -582,6 +568,9 @@ bool cc_worker::work_ul()
// Add CSI reports to UCI data if available
phy.get_periodic_csi(ul_slot_cfg.idx, uci_data);
// Setup frequency offset
srsran_ue_ul_nr_set_freq_offset(&ue_ul, phy.get_ul_cfo());
if (has_pusch_grant) {
// Notify MAC about PUSCH found grant
mac_interface_phy_nr::tb_action_ul_t ul_action = {};

View File

@ -79,7 +79,7 @@ sf_worker* worker_pool::wait_worker(uint32_t tti)
if (prach_buffer->is_ready_to_send(tti, phy_state.cfg.carrier.pci)) {
uint32_t nof_prach_sf = 0;
float prach_target_power = 0.0f;
cf_t* prach_ptr = prach_buffer->generate(0.0f, &nof_prach_sf, &prach_target_power);
cf_t* prach_ptr = prach_buffer->generate(-phy_state.get_ul_cfo() / 15000, &nof_prach_sf, &prach_target_power);
worker->set_prach(prach_ptr, prach_target_power);
}

View File

@ -541,6 +541,10 @@ void sync::run_camping_in_sync_state(lte::sf_worker* lte_worker,
nr_worker->set_context(context);
// As UE sync compensates CFO externally based on LTE signal and the NR carrier may estimate the CFO from the LTE
// signal. It is necessary setting an NR external CFO offset to compensate it.
nr_worker_pool->set_ul_ext_cfo(-srsran_ue_sync_get_cfo(&ue_sync));
// NR worker needs to be launched first, phy_common::worker_end expects first the NR worker and the LTE worker.
worker_com->semaphore.push(nr_worker);
nr_worker_pool->start_worker(nr_worker);