diff --git a/lib/include/srsran/interfaces/gnb_interfaces.h b/lib/include/srsran/interfaces/gnb_interfaces.h index 91efba3dd..48ab3d340 100644 --- a/lib/include/srsran/interfaces/gnb_interfaces.h +++ b/lib/include/srsran/interfaces/gnb_interfaces.h @@ -268,7 +268,7 @@ public: srsran::unique_byte_buffer_t pdu = nullptr; // PUSCH signal measurements - // ... + srsran_csi_trs_measurements_t csi; ///< DMRS based signal Channel State Information (CSI) }; struct rach_info_t { diff --git a/lib/include/srsran/phy/ch_estimation/dmrs_sch.h b/lib/include/srsran/phy/ch_estimation/dmrs_sch.h index 477d51278..1ccbec53e 100644 --- a/lib/include/srsran/phy/ch_estimation/dmrs_sch.h +++ b/lib/include/srsran/phy/ch_estimation/dmrs_sch.h @@ -50,6 +50,8 @@ typedef struct { cf_t* temp; /// Temporal data vector of size SRSRAN_NRE * carrier.nof_prb float* filter; ///< Smoothing filter + + srsran_csi_trs_measurements_t csi; ///< Last estimated channel state information } srsran_dmrs_sch_t; /** diff --git a/lib/src/phy/ch_estimation/dmrs_sch.c b/lib/src/phy/ch_estimation/dmrs_sch.c index 37dd0b410..629e61b2f 100644 --- a/lib/src/phy/ch_estimation/dmrs_sch.c +++ b/lib/src/phy/ch_estimation/dmrs_sch.c @@ -808,7 +808,7 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, sync_err += srsran_vec_estimate_frequency(&q->pilot_estimates[nof_pilots_x_symbol * i], nof_pilots_x_symbol); } sync_err /= (float)nof_symbols; - chest_res->sync_error = sync_err / (dmrs_stride * SRSRAN_SUBC_SPACING_NR(q->carrier.scs)); + float delay_us = sync_err / (dmrs_stride * SRSRAN_SUBC_SPACING_NR(q->carrier.scs)); #if DMRS_SCH_SYNC_PRECOMPENSATE // Pre-compensate synchronization error @@ -836,36 +836,52 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, epre /= nof_symbols; rsrp = SRSRAN_MIN(rsrp, epre - epre * 1e-7); - chest_res->rsrp = rsrp; - chest_res->rsrp_dbm = srsran_convert_power_to_dB(chest_res->rsrp); - - chest_res->noise_estimate = epre - rsrp; - chest_res->noise_estimate_dbm = srsran_convert_power_to_dB(chest_res->noise_estimate); - - chest_res->snr_db = chest_res->rsrp_dbm - chest_res->noise_estimate_dbm; - // Measure CFO if more than one symbol is used - float cfo_avg = 0.0; + float cfo_avg_hz = 0.0; + float cfo_hz_max = INFINITY; for (uint32_t i = 0; i < nof_symbols - 1; i++) { float time_diff = srsran_symbol_distance_s(symbols[i], symbols[i + 1], q->carrier.scs); float phase_diff = cargf(corr[i + 1] * conjf(corr[i])); if (isnormal(time_diff)) { - cfo_avg += phase_diff / (2.0f * M_PI * time_diff * (nof_symbols - 1)); + cfo_avg_hz += phase_diff / (2.0f * M_PI * time_diff * (nof_symbols - 1)); + + // The maximum measured CFO depends on the symbol time difference + cfo_hz_max = SRSRAN_MIN(cfo_hz_max, 1 / time_diff); } } - chest_res->cfo = cfo_avg; + + // Store internal CSI + q->csi.rsrp = rsrp; + q->csi.rsrp_dB = srsran_convert_power_to_dB(rsrp); + q->csi.epre = epre; + q->csi.epre_dB = srsran_convert_power_to_dB(epre); + q->csi.n0 = epre - rsrp; + q->csi.n0_dB = srsran_convert_power_to_dB(q->csi.n0); + q->csi.snr_dB = q->csi.rsrp_dB - q->csi.n0_dB; + q->csi.cfo_hz = cfo_avg_hz; + q->csi.cfo_hz_max = cfo_hz_max; + q->csi.delay_us = delay_us; + + // Write CSI in estimated channel result + chest_res->rsrp = q->csi.rsrp; + chest_res->rsrp_dbm = q->csi.rsrp_dB; + chest_res->noise_estimate = q->csi.n0; + chest_res->noise_estimate_dbm = q->csi.n0_dB; + chest_res->snr_db = q->csi.snr_dB; + chest_res->cfo = q->csi.cfo_hz; + chest_res->sync_error = q->csi.delay_us; #if DMRS_SCH_CFO_PRECOMPENSATE // Pre-compensate CFO cf_t cfo_correction[SRSRAN_NSYMB_PER_SLOT_NR] = {1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}; - if (isnormal(cfo_avg)) { + if (isnormal(cfo_avg_hz)) { // Calculate phase of the first OFDM symbol (l = 0) - float arg0 = cargf(corr[0]) - 2.0f * M_PI * srsran_symbol_distance_s(0, symbols[0], q->carrier.scs) * cfo_avg; + float arg0 = cargf(corr[0]) - 2.0f * M_PI * srsran_symbol_distance_s(0, symbols[0], q->carrier.scs) * cfo_avg_hz; // Calculate CFO corrections for (uint32_t l = 0; l < SRSRAN_NSYMB_PER_SLOT_NR; l++) { - float arg = arg0 + 2.0f * M_PI * cfo_avg * srsran_symbol_distance_s(0, l, q->carrier.scs); + float arg = arg0 + 2.0f * M_PI * cfo_avg_hz * srsran_symbol_distance_s(0, l, q->carrier.scs); cfo_correction[l] = cexpf(I * arg); } @@ -883,7 +899,7 @@ int srsran_dmrs_sch_estimate(srsran_dmrs_sch_t* q, INFO("PDSCH-DMRS: RSRP=%+.2fdB EPRE=%+.2fdB CFO=%+.0fHz Sync=%.3fus", chest_res->rsrp_dbm, srsran_convert_power_to_dB(epre), - cfo_avg, + cfo_avg_hz, chest_res->sync_error * 1e6); // Average over time, only if more than one DMRS symbol diff --git a/srsenb/src/phy/nr/slot_worker.cc b/srsenb/src/phy/nr/slot_worker.cc index c0fcb4827..00fc13f2d 100644 --- a/srsenb/src/phy/nr/slot_worker.cc +++ b/srsenb/src/phy/nr/slot_worker.cc @@ -224,6 +224,9 @@ bool slot_worker::work_ul() return false; } + // Extract DMRS information + pusch_info.csi = gnb_ul.dmrs.csi; + // Inform stack if (stack.pusch_info(ul_slot_cfg, pusch_info) < SRSRAN_SUCCESS) { logger.error("Error pushing PUSCH information to stack"); diff --git a/test/phy/dummy_gnb_stack.h b/test/phy/dummy_gnb_stack.h index b9f80e10c..15b841d0b 100644 --- a/test/phy/dummy_gnb_stack.h +++ b/test/phy/dummy_gnb_stack.h @@ -61,6 +61,7 @@ public: uint32_t cqi_count = 0; ///< CQI opportunity counter uint32_t cqi_valid_count = 0; ///< Valid CQI counter pucch_metrics_t pucch = {}; + pucch_metrics_t pusch = {}; }; private: @@ -662,6 +663,21 @@ public: metrics.mac.rx_brate += rx_harq_proc[pusch_info.pid].get_tbs(); metrics.mac.rx_pkts++; + // Handle PHY metrics + metrics.pusch.epre_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.epre_dB, metrics.pusch.epre_db_avg, metrics.pusch.count); + metrics.pusch.epre_db_min = SRSRAN_MIN(metrics.pusch.epre_db_min, pusch_info.csi.epre_dB); + metrics.pusch.epre_db_max = SRSRAN_MAX(metrics.pusch.epre_db_max, pusch_info.csi.epre_dB); + metrics.pusch.rsrp_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.rsrp_dB, metrics.pusch.rsrp_db_avg, metrics.pusch.count); + metrics.pusch.rsrp_db_min = SRSRAN_MIN(metrics.pusch.rsrp_db_min, pusch_info.csi.rsrp_dB); + metrics.pusch.rsrp_db_max = SRSRAN_MAX(metrics.pusch.rsrp_db_max, pusch_info.csi.rsrp_dB); + metrics.pusch.snr_db_avg = SRSRAN_VEC_CMA(pusch_info.csi.snr_dB, metrics.pusch.snr_db_avg, metrics.pusch.count); + metrics.pusch.snr_db_min = SRSRAN_MIN(metrics.pusch.snr_db_min, pusch_info.csi.snr_dB); + metrics.pusch.snr_db_max = SRSRAN_MAX(metrics.pusch.snr_db_max, pusch_info.csi.snr_dB); + metrics.pusch.ta_us_avg = SRSRAN_VEC_CMA(pusch_info.csi.delay_us, metrics.pusch.ta_us_avg, metrics.pusch.count); + metrics.pusch.ta_us_min = SRSRAN_MIN(metrics.pusch.ta_us_min, pusch_info.csi.delay_us); + metrics.pusch.ta_us_max = SRSRAN_MAX(metrics.pusch.ta_us_max, pusch_info.csi.delay_us); + metrics.pusch.count++; + return SRSRAN_SUCCESS; } diff --git a/test/phy/nr_phy_test.cc b/test/phy/nr_phy_test.cc index b5df81698..57ed9a069 100644 --- a/test/phy/nr_phy_test.cc +++ b/test/phy/nr_phy_test.cc @@ -194,9 +194,9 @@ int main(int argc, char** argv) test_bench::metrics_t metrics = tb.get_metrics(); // Print PDSCH metrics if scheduled - double pdsch_bler = 0.0; if (metrics.gnb_stack.mac.tx_pkts > 0) { - pdsch_bler = (double)metrics.gnb_stack.mac.tx_errors / (double)metrics.gnb_stack.mac.tx_pkts; + float pdsch_bler = 0.0f; + pdsch_bler = (float)metrics.gnb_stack.mac.tx_errors / (float)metrics.gnb_stack.mac.tx_pkts; float pdsch_shed_rate = 0.0f; pdsch_shed_rate = (float)metrics.gnb_stack.mac.tx_brate / (float)metrics.gnb_stack.mac.tx_pkts / 1000.0f; @@ -215,10 +215,10 @@ int main(int argc, char** argv) } // Print PUSCH metrics if scheduled - double pusch_bler = 0.0; if (metrics.gnb_stack.mac.rx_pkts > 0) { + float pusch_bler = 0.0f; if (metrics.gnb_stack.mac.rx_pkts != 0) { - pusch_bler = (double)metrics.gnb_stack.mac.rx_errors / (double)metrics.gnb_stack.mac.rx_pkts; + pusch_bler = (float)metrics.gnb_stack.mac.rx_errors / (float)metrics.gnb_stack.mac.rx_pkts; } float pusch_shed_rate = 0.0f; @@ -295,6 +295,73 @@ int main(int argc, char** argv) srsran::console(" +------------+------------+------------+------------+\n"); } + // Print PDSCH metrics if scheduled + double pdsch_bler = 0.0; + if (metrics.gnb_stack.mac.tx_pkts > 0) { + pdsch_bler = (double)metrics.gnb_stack.mac.tx_errors / (double)metrics.gnb_stack.mac.tx_pkts; + + float pdsch_shed_rate = 0.0f; + pdsch_shed_rate = (float)metrics.gnb_stack.mac.tx_brate / (float)metrics.gnb_stack.mac.tx_pkts / 1000.0f; + + srsran::console("PDSCH:\n"); + srsran::console(" Count: %d\n", metrics.gnb_stack.mac.tx_pkts); + srsran::console(" BLER: %f\n", pdsch_bler); + srsran::console(" Sched Rate: %f Mbps\n", pdsch_shed_rate); + srsran::console(" Net Rate: %f Mbps\n", (1.0f - pdsch_bler) * pdsch_shed_rate); + srsran::console(" Retx Rate: %f Mbps\n", pdsch_bler * pdsch_shed_rate); + srsran::console("\n"); + } + + // Print PUSCH metrics if scheduled + double pusch_bler = 0.0; + if (metrics.gnb_stack.mac.rx_pkts > 0) { + if (metrics.gnb_stack.mac.rx_pkts != 0) { + pusch_bler = (double)metrics.gnb_stack.mac.rx_errors / (double)metrics.gnb_stack.mac.rx_pkts; + } + + float pusch_shed_rate = 0.0f; + if (metrics.gnb_stack.mac.rx_pkts != 0) { + pusch_shed_rate = (float)metrics.gnb_stack.mac.rx_brate / (float)metrics.gnb_stack.mac.rx_pkts / 1000.0f; + } + + srsran::console("PUSCH:\n"); + srsran::console(" Count: %d\n", metrics.gnb_stack.mac.rx_pkts); + srsran::console(" BLER: %f\n", pusch_bler); + srsran::console(" Sched Rate: %f Mbps\n", pusch_shed_rate); + srsran::console(" Net Rate: %f Mbps\n", (1.0f - pusch_bler) * pusch_shed_rate); + srsran::console(" Retx Rate: %f Mbps\n", pusch_bler * pusch_shed_rate); + srsran::console("\n"); + } + + // Print PUSCH + if (metrics.gnb_stack.pusch.count > 0) { + srsran::console("PUSCH DMRS Receiver metrics:\n"); + srsran::console(" +------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %10s | %10s | %10s |\n", "Measure", "Average", "Min", "Max"); + srsran::console(" +------------+------------+------------+------------+\n"); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "EPRE (dB)", + metrics.gnb_stack.pusch.epre_db_avg, + metrics.gnb_stack.pusch.epre_db_min, + metrics.gnb_stack.pusch.epre_db_min); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "RSRP (dB)", + metrics.gnb_stack.pusch.rsrp_db_avg, + metrics.gnb_stack.pusch.rsrp_db_min, + metrics.gnb_stack.pusch.rsrp_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "SINR (dB)", + metrics.gnb_stack.pusch.snr_db_avg, + metrics.gnb_stack.pusch.snr_db_min, + metrics.gnb_stack.pusch.snr_db_max); + srsran::console(" | %10s | %+10.2f | %+10.2f | %+10.2f |\n", + "TA (us)", + metrics.gnb_stack.pusch.ta_us_avg, + metrics.gnb_stack.pusch.ta_us_min, + metrics.gnb_stack.pusch.ta_us_max); + srsran::console(" +------------+------------+------------+------------+\n"); + } + srsran::console("UCI stats:\n"); srsran::console(" +------------+------------+------------+------------+------------+\n"); srsran::console(