Merge remote-tracking branch 'origin/sinr_based_amc' into integration_2025_w37 (!3601)

gNB: adaptive DL/UL MCS based on reported SSB-SINR and measured PUSCH SNR

Currently this is used esp. if the number of rounds is limited to 1
(e.g. disable_harq).
This commit is contained in:
Robert Schmidt
2025-09-11 15:42:38 +02:00
12 changed files with 124 additions and 37 deletions
@@ -16,6 +16,7 @@ gNBs =
////////// Physical parameters:
do_SINR = 1;
min_rxtxtime = 6;
disable_harq = 1;
cu_sibs = [2];
+3 -1
View File
@@ -78,7 +78,7 @@ available in the the file `nrMAC-stats.log` in the same directory in which
Example:
```
UE RNTI 2460 CU-UE-ID 2 in-sync PH 28 dB PCMAX 24 dBm, average RSRP -74 (8 meas)
UE RNTI 2460 CU-UE-ID 2 in-sync PH 28 dB PCMAX 24 dBm, average RSRP -74 (8 meas), average SINR 40.0 (32 meas)
UE 2460: CQI 15, RI 2, PMI (14,1)
UE 2460: UL-RI 2 TPMI 0
UE 2460: dlsch_rounds 32917/5113/1504/560, dlsch_errors 211, pucch0_DTX 1385, BLER 0.19557 MCS (1) 23 CCE fail 3
@@ -103,6 +103,8 @@ In the first line,
* `RSRP` (`-74`): measured power of the DL reference signals at the UE. >-80dBm
you should have full DL throughput. <-95 dBm, you are very limited in terms
of connectivity.
* `SINR` (`40.0`): measured signal to interference and noise ratio of the SSB
received at the UE. Maximum value that can be reported by the UE is 40.0 dB.
The second and third line reflect channel state information (CSI) as
reported by the UE, and only appear if CSI-RS/SRS are enabled and _received_
@@ -38,9 +38,8 @@
#include "common/utils/LOG/log.h"
#include "PHY/sse_intrin.h"
//#define k1 1000
#define k1 ((long long int) 1000)
#define k2 ((long long int) (1024-k1))
#define K1 ((long long int) 512)
#define K2 ((long long int) (1024-K1))
//#define DEBUG_MEAS_RRC
//#define DEBUG_MEAS_UE
@@ -136,11 +135,11 @@ void nr_ue_measurements(PHY_VARS_NR_UE *ue,
if (ue->init_averaging == 0) {
for (gNB_id = 0; gNB_id < ue->n_connected_gNB; gNB_id++)
ue->measurements.rx_power_avg[gNB_id] = (int)(((k1*((long long int)(ue->measurements.rx_power_avg[gNB_id]))) + (k2*((long long int)(ue->measurements.rx_power_tot[gNB_id])))) >> 10);
ue->measurements.rx_power_avg[gNB_id] = (int)((K1 * ue->measurements.rx_power_avg[gNB_id] + K2 * ue->measurements.rx_power_tot[gNB_id]) >> 10);
ue->measurements.n0_power_avg = (int)(((k1*((long long int) (ue->measurements.n0_power_avg))) + (k2*((long long int) (ue->measurements.n0_power_tot))))>>10);
ue->measurements.n0_power_avg = (int)((K1 * ue->measurements.n0_power_avg + K2 * ue->measurements.n0_power_tot) >> 10);
LOG_D(PHY, "Noise Power Computation: k1 %lld k2 %lld n0 avg %u n0 tot %u\n", k1, k2, ue->measurements.n0_power_avg, ue->measurements.n0_power_tot);
LOG_D(PHY, "Noise Power Computation: K1 %lld K2 %lld n0 avg %u n0 tot %u\n", K1, K2, ue->measurements.n0_power_avg, ue->measurements.n0_power_tot);
} else {
+15 -14
View File
@@ -311,9 +311,9 @@ int main(int argc, char **argv)
int i,aa;//,l;
double sigma2, sigma2_dB=10, SNR, snr0=-2.0, snr1=2.0;
uint8_t snr1set=0;
float effRate;
double effRate;
//float psnr;
float eff_tp_check = 0.7;
double eff_tp_check = 0.7;
uint32_t TBS = 0;
c16_t **txdata;
double **s_re,**s_im,**r_re,**r_im;
@@ -547,7 +547,7 @@ int main(int argc, char **argv)
break;
case 't':
eff_tp_check = (float)atoi(optarg)/100;
eff_tp_check = atof(optarg) / 100.0;
break;
case 'w':
@@ -1081,8 +1081,6 @@ printf("%d\n", slot);
Sched_INFO->sched_response_id = -1;
while (round < num_rounds && !UE_harq_process->decodeResult && !stop) {
round_trials[round]++;
clear_nr_nfapi_information(RC.nrmac[0], 0, frame, slot, &Sched_INFO->DL_req, &Sched_INFO->TX_req, &Sched_INFO->UL_dci_req);
UE_info->UE_sched_ctrl.harq_processes[harq_pid].ndi = !(trial&1);
UE_info->UE_sched_ctrl.harq_processes[harq_pid].round = round;
@@ -1235,6 +1233,9 @@ printf("%d\n", slot);
if (dlsch0->last_iteration_cnt >= dlsch0->max_ldpc_iterations)
n_errors[round]++;
if (dlsch0->active)
round_trials[round]++;
int16_t *UE_llr = (int16_t*)UE->phy_sim_pdsch_llr;
TBS = dlsch0->dlsch_config.TBS;//rel15->TBSize[0];
@@ -1266,8 +1267,8 @@ printf("%d\n", slot);
} // round
if (test_input_bit == NULL) {
test_input_bit = (unsigned char *)malloc16(sizeof(unsigned char) * TBS);
estimated_output_bit = (unsigned char *)malloc16(sizeof(unsigned char) * TBS);
test_input_bit = (unsigned char *)malloc16(8 * rel15->TBSize[0]);
estimated_output_bit = (unsigned char *)malloc16(8 * rel15->TBSize[0]);
}
for (i = 0; i < TBS; i++) {
@@ -1288,12 +1289,12 @@ printf("%d\n", slot);
if (n_trials == 1)
printf("errors_bit = %u (trial %d)\n", errors_bit, trial);
}
roundStats += ((float)round);
roundStats += round;
if (UE_harq_process->decodeResult)
effRate += ((float)TBS) / round;
effRate += ((double)TBS) / round;
} // noise trials
roundStats /= ((float)n_trials);
roundStats /= n_trials;
for (int r = 0; r < num_rounds; r++) {
blerStats[r] = (double)n_errors[r] / round_trials[r];
@@ -1316,7 +1317,7 @@ printf("%d\n", slot);
printf("), Channel BER (%e", berStats[0]);
for (int r = 1; r < num_rounds; r++)
printf(",%e", berStats[r]);
printf(") Avg round %.2f, Eff Rate %.4f bits/slot, Eff Throughput %.2f, TBS %u bits/slot\n", roundStats, effRate, effRate / TBS * 100, TBS);
printf(") Avg round %.2f, Eff Rate %.4f bits/slot, Eff Throughput %.2f, TBS %u bits/slot\n", roundStats, effRate, effRate / (8 * rel15->TBSize[0]) * 100, 8 * rel15->TBSize[0]);
printf("*****************************************\n");
printf("\n");
// writing to csv file
@@ -1324,7 +1325,7 @@ printf("%d\n", slot);
fprintf(csv_file,"%f,%d/%d,",SNR,n_false_positive,n_trials);
for (int r = 0; r < num_rounds; r++)
fprintf(csv_file,"%d/%d,%u/%u,%f,%e,",n_errors[r], round_trials[r], errors_scrambling[r], available_bits * round_trials[r],blerStats[r],berStats[r]);
fprintf(csv_file,"%.2f,%.4f,%.2f,%u\n", roundStats, effRate, effRate / TBS * 100, TBS);
fprintf(csv_file,"%.2f,%.4f,%.2f,%u\n", roundStats, effRate, effRate / (8 * rel15->TBSize[0]) * 100, 8 * rel15->TBSize[0]);
}
if (print_perf==1) {
printf("\ngNB TX function statistics (per %d us slot, NPRB %d, mcs %d, C %d, block %d)\n",
@@ -1332,7 +1333,7 @@ printf("%d\n", slot);
g_rbSize,
g_mcsIndex,
UE->dl_harq_processes[0][slot].C,
msgDataTx->dlsch[0][0].harq_process.pdsch_pdu.pdsch_pdu_rel15.TBSize[0] << 3);
8 * rel15->TBSize[0]);
printDistribution(&gNB->phy_proc_tx,table_tx,"PHY proc tx");
printStatIndent2(&gNB->dci_generation_stats, "DCI encoding time");
printStatIndent2(&gNB->dlsch_encoding_stats,"DLSCH encoding time");
@@ -1406,7 +1407,7 @@ printf("%d\n", slot);
break;
}
if (effRate > (eff_tp_check*TBS)) {
if (effRate >= (eff_tp_check * 8 * rel15->TBSize[0])) {
printf("PDSCH test OK\n");
ret = 0;
break;
+2 -2
View File
@@ -1478,8 +1478,8 @@ int main(int argc, char *argv[])
}
for (i = 0; i < available_bits; i++) {
if (((UE->ul_harq_processes[harq_pid].f[i] == 0) && (pusch_vars->llr[i] <= 0))
|| ((UE->ul_harq_processes[harq_pid].f[i] == 1) && (pusch_vars->llr[i] >= 0))) {
const uint8_t current_bit = (UE->ul_harq_processes[harq_pid].f[i / 8] >> (i & 7)) & 1;
if (((current_bit == 0) && (pusch_vars->llr[i] <= 0)) || ((current_bit == 1) && (pusch_vars->llr[i] >= 0))) {
/*if(errors_scrambling == 0)
printf("\x1B[34m" "[frame %d][trial %d]\t1st bit in error in unscrambling = %d\n" "\x1B[0m", frame, trial, i);*/
errors_scrambling[round]++;
@@ -683,7 +683,8 @@ static void pf_dl(module_id_t module_id,
const int max_mcs_table = current_BWP->mcsTableIdx == 1 ? 27 : 28;
const int max_mcs = min(sched_ctrl->dl_max_mcs, max_mcs_table);
if (bo->harq_round_max == 1) {
sched_pdsch->mcs = min(bo->max_mcs, max_mcs);
int new_mcs = min(bo->max_mcs, max_mcs);
sched_pdsch->mcs = max(bo->min_mcs, new_mcs);
sched_ctrl->dl_bler_stats.mcs = sched_pdsch->mcs;
} else
sched_pdsch->mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->dl_bler_stats, max_mcs, frame);
@@ -248,6 +248,38 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
}
}
// look-up table for AMC. Based on BLER vs SNR curves from the nr_dlsim simulation
// command line: nr_dlsim -n 10000 -m 0 -R 25 -b 25 -e MCS -s START_SNR -t 99.99
// SNR Thresholds for MCS=[0,...,28]; START_SNR=chosen values with a resolution of 0.2dB to maintain a BLER of 10^-3
static const int SINRx10_MCS_mapping[29] = {
-10, -4, 6, 16, 24, 34, 42, 50, 56, 62, // 0..9
86, 92, 98, 104, 112, 118, 124, 140, 146, 154, // 10..19
162, 170, 178, 186, 194, 202, 212, 220, 245 // 20..28
};
int get_mcs_from_SINRx10(int mcs_table, int SINRx10, int Nl)
{
if (mcs_table != 0) {
LOG_E(MAC, "mcs_table = %d, but get_mcs_from_SINRx10() only supports MCS table 0 (TS 38.214 - Table 5.1.3.1-1)\n", mcs_table);
return 28;
}
int MIMO_SNRx10 = 0;
if (Nl == 2)
MIMO_SNRx10 = 40;
else if (Nl == 4)
MIMO_SNRx10 = 70;
for (int i = 28; i >= 0; i--) {
if (SINRx10 >= SINRx10_MCS_mapping[i] + MIMO_SNRx10)
return i;
}
LOG_W(MAC, "SINR (%d.%d dB) too low, no MCS possible to achieve BLER of 10^-3\n", SINRx10 / 10, SINRx10 % 10);
return 0;
}
uint8_t get_mcs_from_cqi(int mcs_table, int cqi_table, int cqi_idx)
{
if (cqi_idx <= 0) {
+35 -5
View File
@@ -414,6 +414,25 @@ static void handle_dl_harq(NR_UE_info_t * UE,
}
}
// Referred Table 10.1.16.1-1 in 38.133 V16.7.0.
static int get_measured_sinr(int index)
{
if (index < 0 || index > 127)
return INT_MAX;
return -235 + index * 5;
}
// Referred Table 10.1.16.1-2 in 38.133 V16.7.0.
static int get_diff_sinr(int index, int best_SINRx10)
{
if (index <= 0)
return best_SINRx10;
if (index >= 15)
return best_SINRx10 - 150;
return best_SINRx10 - index * 10;
}
//returns the measured RSRP value (upper limit)
static bool get_measured_rsrp(uint8_t index, int *rsrp)
{
@@ -498,22 +517,33 @@ static void evaluate_sinr_report(gNB_MAC_INST *nrmac,
}
curr_payload = pickandreverse_bits(payload, 7, *cumul_bits);
sinr_report->SINR_index[0] = curr_payload & 0x7f;
int sinr_index = curr_payload & 0x7f;
*cumul_bits += 7;
csi_report->nb_of_csi_ssb_report++;
if (sinr_report->SINR_index[0] < 0 || sinr_report->SINR_index[0] > 127) {
LOG_E(NR_MAC, "UE %04x: reported SINR index %d invalid\n", UE->rnti, sinr_report->SINR_index[0]);
int SINRx10 = get_measured_sinr(sinr_index);
if (SINRx10 == INT_MAX) {
LOG_E(NR_MAC, "UE %04x: reported SINR index %d invalid\n", UE->rnti, sinr_index);
return;
}
sinr_report->SINRx10[0] = SINRx10;
for (int i = 1; i < sinr_report->nr_reports; i++) {
curr_payload = pickandreverse_bits(payload, 4, *cumul_bits);
sinr_report->SINR_index[i] = curr_payload & 0x0f;
sinr_report->SINRx10[i] = get_diff_sinr(curr_payload & 0x0f, sinr_report->SINRx10[0]);
*cumul_bits += 4;
}
LOG_D(MAC, "Reported SSB-SINR index = %d\n", sinr_report->SINR_index[0]);
NR_mac_stats_t *stats = &UE->mac_stats;
// including ssb SINR in mac stats
stats->cumul_sinrx10 += sinr_report->SINRx10[0];
stats->num_sinr_meas++;
const int mcs_table = UE->current_DL_BWP.mcsTableIdx;
const int nrOfLayers = get_dl_nrOfLayers(sched_ctrl, UE->current_DL_BWP.dci_format);
sched_ctrl->dl_max_mcs = get_mcs_from_SINRx10(mcs_table, sinr_report->SINRx10[0], nrOfLayers);
LOG_D(MAC, "Reported SSB-SINR = %d.%d, dl_max_mcs %d\n", sinr_report->SINRx10[0] / 10, sinr_report->SINRx10[0] % 10, sched_ctrl->dl_max_mcs);
}
static void evaluate_rsrp_report(gNB_MAC_INST *nrmac,
@@ -2061,7 +2061,11 @@ static int pf_ul(gNB_MAC_INST *nrmac,
const int max_mcs = min(bo->max_mcs, max_mcs_table); /* no per-user maximum MCS yet */
int selected_mcs;
if (bo->harq_round_max == 1) {
selected_mcs = sched_ctrl->ul_bler_stats.mcs = max_mcs;
int nrOfLayers = get_ul_nrOfLayers(sched_ctrl, current_BWP->dci_format);
selected_mcs = get_mcs_from_SINRx10(current_BWP->mcs_table, sched_ctrl->pusch_snrx10, nrOfLayers);
selected_mcs = min(max_mcs, selected_mcs);
selected_mcs = max(bo->min_mcs, selected_mcs);
sched_ctrl->ul_bler_stats.mcs = selected_mcs;
} else {
selected_mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->ul_bler_stats, max_mcs, frame);
LOG_D(NR_MAC, "%d.%d starting mcs %d bler %f\n", frame, slot, selected_mcs, sched_ctrl->ul_bler_stats.bler);
+1
View File
@@ -403,6 +403,7 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
int layers,
int xp_pdsch_antenna_ports);
int get_mcs_from_SINRx10(int mcs_table, int SINRx10, int Nl);
uint8_t get_mcs_from_cqi(int mcs_table, int cqi_table, int cqi_idx);
uint8_t get_dl_nrOfLayers(const NR_UE_sched_ctrl_t *sched_ctrl, const nr_dci_format_t dci_format);
+20 -5
View File
@@ -126,24 +126,37 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
NR_mac_stats_t *stats = &UE->mac_stats;
const int avg_rsrp = stats->num_rsrp_meas > 0 ? stats->cumul_rsrp / stats->num_rsrp_meas : 0;
const int avg_sinrx10 = stats->num_sinr_meas > 0 ? stats->cumul_sinrx10 / stats->num_sinr_meas : 0;
output += snprintf(output, end - output, "UE RNTI %04x CU-UE-ID ", UE->rnti);
if (du_exists_f1_ue_data(UE->rnti)) {
f1_ue_data_t ued = du_get_f1_ue_data(UE->rnti);
output += snprintf(output, end - output, "%d", ued.secondary_ue);
} else {
output += snprintf(output, end-output, "(none)");
output += snprintf(output, end - output, "(none)");
}
bool in_sync = !sched_ctrl->ul_failure;
output += snprintf(output,
end - output,
" %s PH %d dB PCMAX %d dBm, average RSRP %d (%d meas)\n",
" %s PH %d dB PCMAX %d dBm",
in_sync ? "in-sync" : "out-of-sync",
sched_ctrl->ph,
sched_ctrl->pcmax,
avg_rsrp,
stats->num_rsrp_meas);
sched_ctrl->pcmax);
if (stats->num_rsrp_meas)
output += snprintf(output, end - output, ", average RSRP %d (%d meas)", avg_rsrp, stats->num_rsrp_meas);
if (stats->num_sinr_meas) {
output += snprintf(output,
end - output,
", average SINR %d.%d (%d meas)",
avg_sinrx10 / 10,
avg_sinrx10 % 10,
stats->num_sinr_meas);
}
output += snprintf(output, end - output, "\n");
if(sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.print_report)
output += snprintf(output,
@@ -178,6 +191,8 @@ size_t dump_mac_stats(gNB_MAC_INST *gNB, char *output, size_t strlen, bool reset
if (reset_rsrp) {
stats->num_rsrp_meas = 0;
stats->cumul_rsrp = 0;
stats->num_sinr_meas = 0;
stats->cumul_sinrx10 = 0;
}
output += snprintf(output,
end - output,
+3 -2
View File
@@ -521,8 +521,7 @@ typedef struct RSRP_report {
uint8_t nr_reports;
uint8_t resource_id[MAX_NR_OF_REPORTED_RS];
int RSRP[MAX_NR_OF_REPORTED_RS];
// SINR index according to tables 10.1.16.1-1, 10.1.16.1-2
int SINR_index[MAX_NR_OF_REPORTED_RS];
int SINRx10[MAX_NR_OF_REPORTED_RS];
} RSRP_report_t;
struct CSI_Report {
@@ -694,6 +693,8 @@ typedef struct NR_mac_stats {
uint32_t pucch0_DTX;
int cumul_rsrp;
uint8_t num_rsrp_meas;
int cumul_sinrx10;
uint8_t num_sinr_meas;
char srs_stats[50]; // Statistics may differ depending on SRS usage
int pusch_snrx10;
int deltaMCS;