From aa5bd641fd56d19c087bd933696a1c6efd553743 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Wed, 15 Apr 2026 10:35:47 +0100 Subject: [PATCH 1/2] Fix EMA cold-start for noise and SNR/RSSI measurements Initialize n0_subband_power and power control avg_snr/avg_rssi directly from the first measurement instead of starting from zero --- openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c | 4 ++++ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 5 +++++ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 2 +- 3 files changed, 10 insertions(+), 1 deletion(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index a901f8dba1..13c828f129 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -162,6 +162,7 @@ void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_sy int32_t n0_subband_tot_perANT[frame_parms->nb_antennas_rx]; memset(n0_subband_tot_perANT, 0, sizeof(n0_subband_tot_perANT)); + bool init_meas = measurements->n0_subband_power == NULL; allocCast2D(n0_subband_power, unsigned int, measurements->n0_subband_power, @@ -174,6 +175,9 @@ void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_sy if (nb_symb[rb] > 0) { for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { tmp_n0_subband[aarx][rb] /= nb_symb[rb]; + // Initialize n0_subband_power from the first measurement before EMA + if (init_meas) + n0_subband_power[aarx][rb] = tmp_n0_subband[aarx][rb]; // apply exponential moving average to smooth noise measurements n0_subband_power[aarx][rb] = 0.9 * n0_subband_power[aarx][rb] + 0.1 * tmp_n0_subband[aarx][rb]; n0_subband_tot_perPRB += n0_subband_power[aarx][rb]; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index a6f66d738a..e85adc709d 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -910,6 +910,11 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, con NR_SCHED_UNLOCK(&nrmac->sched_lock); return; } + // Msg4 Ack: reset average with first measurement + if (uci_01->ul_cqi != 0xff) { + UE->UE_sched_ctrl.pucch_pc.avg_rssi = uci_01->rssi; + UE->UE_sched_ctrl.pucch_pc.avg_snr = (uci_01->ul_cqi * 5 - 640) * 0.1; + } } if (harq_confidence == 1) { UE->mac_stats.pucch0_DTX++; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index be869d9de9..c3ae6eaedf 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -763,7 +763,7 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, if (ul_cqi != 0xff) { NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; - // Msg3: reset average. If this fails (e.g., ul_cqi == 0xff) + // Msg3: reset average with first measurement. If this fails (e.g., ul_cqi == 0xff) // everything starts from predetermined value sched_ctrl->pusch_pc.avg_snr = 0.1 * (ul_cqi * 5 - 640); sched_ctrl->pusch_pc.avg_rssi = rssi; From 6f2d90fee9cb842a02828d2bd0fba084336d8cde Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 16 Apr 2026 14:52:03 +0200 Subject: [PATCH 2/2] Add nr_mac_pc_reset_snr() to harmonize SNR reset on first reception --- .../LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c | 15 +++++++++++++++ openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c | 6 ++---- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 4 +--- openair2/LAYER2/NR_MAC_gNB/mac_proto.h | 1 + 4 files changed, 19 insertions(+), 7 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 81e1ab1819..6be3e71b84 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -4200,6 +4200,21 @@ void nr_mac_pc_snr(nr_power_control_t *pc, int snrx10, int rssi) pc->tpc_in_flight = PC_AVG_CNST * pc->tpc_in_flight; // + (1 - PC_AVG_CNST) * 0.0f } +/** + * @brief Enter new SNR and RSSI value for the latest UL transmission as new + * fixed averages. Reset "TPC in flight" to zero. + * + * @param pc the power control loop + * @param snrx10 the current SNR measurement multiplied by 10 + * @param rssi the current RSSI measurement + */ +void nr_mac_pc_reset_snr(nr_power_control_t *pc, int snrx10, int rssi) +{ + pc->avg_snr = 0.1f * snrx10; + pc->avg_rssi = rssi; + pc->tpc_in_flight = 0.0f; +} + /** * @brief Set a new target SNR for this power control loop, which can be * updated on a continuous basis, and will be reflected immediately upon diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c index e85adc709d..d0ab235276 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_uci.c @@ -911,10 +911,8 @@ void handle_nr_uci_pucch_0_1(module_id_t mod_id, frame_t frame, slot_t slot, con return; } // Msg4 Ack: reset average with first measurement - if (uci_01->ul_cqi != 0xff) { - UE->UE_sched_ctrl.pucch_pc.avg_rssi = uci_01->rssi; - UE->UE_sched_ctrl.pucch_pc.avg_snr = (uci_01->ul_cqi * 5 - 640) * 0.1; - } + if (uci_01->ul_cqi != 0xff) + nr_mac_pc_reset_snr(&UE->UE_sched_ctrl.pucch_pc, uci_01->ul_cqi * 5 - 640, uci_01->rssi); } if (harq_confidence == 1) { UE->mac_stats.pucch0_DTX++; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index c3ae6eaedf..30213db346 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -762,11 +762,9 @@ static void nr_rx_ra_sdu(const module_id_t mod_id, } if (ul_cqi != 0xff) { - NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl; // Msg3: reset average with first measurement. If this fails (e.g., ul_cqi == 0xff) // everything starts from predetermined value - sched_ctrl->pusch_pc.avg_snr = 0.1 * (ul_cqi * 5 - 640); - sched_ctrl->pusch_pc.avg_rssi = rssi; + nr_mac_pc_reset_snr(&UE->UE_sched_ctrl.pusch_pc, ul_cqi * 5 - 640, rssi); } if (!sdu) { // NACK diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h index fb2f12e6d2..5aeace199c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_proto.h +++ b/openair2/LAYER2/NR_MAC_gNB/mac_proto.h @@ -500,6 +500,7 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE float nr_mac_get_snr(const nr_power_control_t *pc); void nr_mac_pc_snr(nr_power_control_t *pc, int snrx10, int rssi); +void nr_mac_pc_reset_snr(nr_power_control_t *pc, int snrx10, int rssi); void nr_mac_set_target_snrx10(nr_power_control_t *pc, int target_snrx10); void nr_mac_set_rssi_threshold(nr_power_control_t *pc, int rssi_threshold); void nr_mac_signal_dtx(nr_power_control_t *pc);