Compare commits

..

2 Commits

Author SHA1 Message Date
Bartosz Podrygajlo
d3d9380131 pre-commit config 2026-04-20 16:50:54 +02:00
Jaroslava Fiedlerova
38dc378224 Merge branch 'integration_2026_w16' into 'develop'
Integration `2026.w16`

* !4040 fix: issue 911 - replace timer oneshot to periodic.
* !4041 Compilation without T: move to physims
* !4046 fix(record_db doc): correct the path for T_messages.txt and mention use of ninja/make
* !3994 Update CN5G images to release v2.2.0 and update traffic generator image
* !4024 Fix fill_srs_channel_matrix
* !3983 Add SIB 3,4 support, configurable SIB2 and refactor CU/DU SIB management
* !4030 Miscellaneous code improvements
* !4039 Verify consistency of CSI report in L2 instead of RRC
* !3991 L1 RX: use queues instead of arrays and linear search for PUCCH, PUSCH, SRS, PRACH
* !4011 CI: remove cppcheck, cleanup in build and license checks
* !4051 Fix EMA cold-start for noise and SNR/RSSI measurements

Closes #1063

See merge request oai/openairinterface5g!4048
2026-04-17 09:40:42 +00:00
13 changed files with 89 additions and 106 deletions

View File

@@ -0,0 +1,20 @@
repos:
# Clang-Format: Ensures code style is consistent
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 'v22.1.3'
hooks:
- id: clang-format
# Cppcheck: Static analysis to find bugs
- repo: https://github.com/pocc/pre-commit-hooks
rev: 'v1.3.5'
hooks:
- id: cppcheck
# Basic sanity checks (trailing whitespace, end of file, etc.)
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 'v6.0.0'
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: end-of-file-fixer

View File

@@ -23,7 +23,7 @@ gNBs =
nr_cellid = 12345678L
////////// Physical parameters:
uess_agg_levels = [4, 2, 2, 0, 0];
uess_agg_levels = [2, 2, 2, 0, 0];
servingCellConfigCommon = (
{

View File

@@ -90,7 +90,8 @@
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/700Mbps/UDP)(30 sec)</desc>
<desc>Optional iperf (DL/700Mbps/UDP)(30 sec)</desc>
<may_fail>true</may_fail>
<iperf_args>-u -b 700M -t 30 -R</iperf_args>
<id>up2-fhi72</id>
<iperf_packetloss_threshold>10</iperf_packetloss_threshold>

View File

@@ -5,7 +5,7 @@
#ifndef OPENAIRINTERFACE5G_LIMITS_H_
#define OPENAIRINTERFACE5G_LIMITS_H_
# define MAX_MOBILES_PER_GNB 128
# define MAX_MOBILES_PER_GNB 16
# define NUMBER_OF_eNB_MAX 1
# define NUMBER_OF_gNB_MAX 1
# define NUMBER_OF_RU_MAX 2

View File

@@ -22,17 +22,16 @@ UEs with retransmissions are allocated first; similarly, UEs that have not been
scheduled for some time in UL are scheduled automatically in UL and have
therefore priority over data with "normal" traffic.
The MCS selection is done in `estimate_next_mcs()` in file
The MCS selection is done in `get_mcs_from_bler()` in file
[`gNB_scheduler_primitives.c`](../../openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c).
On UE inactivity (low traffic), it returns an estimated MCS (from CQI, SINR,
...). On UE activity (UE with active traffic), it calculates the MCS from BLER
computed from the number of first-round retransmissions over total
transmissions in the last window (50ms). If that ratio is higher than an
"upper" threshold (see `dl/ul_bler_target_upper` in the configuration section
below), it is interpreted as "bad channel" and MCS is decremented by 1. If the
ratio is lower than a "lower" threshold (see `dl/ul_bler_target_lower`), it is
It consider two thresholds for a "BLER" that is computed from the number of
first-round retransmissions over total transmissions in the last window (50ms).
If that ratio is higher than an "upper" threshold (see
`dl/ul_bler_target_upper` in the configuration section below), it is
interpreted as "bad channel" and MCS is decremented by 1. If the ratio is
lower than a "lower" threshold (see `dl/ul_bler_target_lower`), it is
interpreted as "good channel" and MCS is incremented by 1. This happens each
window of 10 frames.
window.
The actual scheduler implementation can be found in functions `pf_dl()` and
`pf_ul()` in files

View File

@@ -411,10 +411,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int
}
#endif
DevAssert(UL_INFO->crc_ind.number_crcs == UL_INFO->rx_ind.number_of_pdus);
DevAssert(UL_INFO->crc_ind.number_crcs < sizeofArray(UL_INFO->crc_pdu_list));
nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++];
DevAssert(UL_INFO->rx_ind.number_of_pdus < sizeofArray(UL_INFO->rx_pdu_list));
nfapi_nr_rx_data_pdu_t *pdu = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++];
if (crc_valid && !check_abort(&ulsch_harq->abort_decode) && !pusch->DTX) {
LOG_D(NR_PHY,
@@ -1054,10 +1051,7 @@ static bool handle_pusch_decode_trigger(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_v
if (!get_softmodem_params()->phy_test) {
/* in case of phy_test mode, we still want to decode to measure execution time.
Therefore, we don't yet call nr_fill_indication, it will be called later */
DevAssert(UL_INFO->crc_ind.number_crcs == UL_INFO->rx_ind.number_of_pdus);
DevAssert(UL_INFO->crc_ind.number_crcs < sizeofArray(UL_INFO->crc_pdu_list));
nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++];
DevAssert(UL_INFO->rx_ind.number_of_pdus < sizeofArray(UL_INFO->rx_pdu_list));
nfapi_nr_rx_data_pdu_t *rx = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++];
nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch_vars, pdu, stats, NULL, 1, crc, rx);
(*pusch_DTX)++;

View File

@@ -607,15 +607,6 @@ static int comparator(const void *p, const void *q)
return 0;
}
static uint8_t get_est_mcs(const dl_est_mcs_t *est_mcs)
{
if (est_mcs->from_csirs_cqi)
return est_mcs->from_csirs_cqi;
if (est_mcs->from_ssb_sinr)
return est_mcs->from_ssb_sinr;
return 0;
}
static void pf_dl(gNB_MAC_INST *mac,
post_process_pdsch_t *pp_pdsch,
NR_UE_info_t **UE_list,
@@ -649,7 +640,7 @@ static void pf_dl(gNB_MAC_INST *mac,
int harq_pid = sched_ctrl->retrans_dl_harq.head;
/* Calculate Throughput */
const float a = 0.05f;
const float a = 0.01f;
const uint32_t b = stats->current_bytes;
UE->dl_thr_ue = (1 - a) * UE->dl_thr_ue + a * b;
@@ -701,12 +692,14 @@ static void pf_dl(gNB_MAC_INST *mac,
/* Calculate coeff */
const NR_bler_options_t *bo = &mac->dl_bler;
const int max_mcs_table = current_BWP->mcsTableIdx == 1 ? 27 : 28;
// UE is active if there is some traffic over time (more 1 Kb/s),
// or sudden burst
bool ue_is_active = UE->dl_thr_ue > 1000 || sched_ctrl->num_total_bytes >= 300;
const int est_mcs = get_est_mcs(&sched_ctrl->dl_est_mcs);
int selected_mcs = estimate_next_mcs(bo, stats, &sched_ctrl->dl_bler_stats, max_mcs_table, est_mcs, frame, ue_is_active);
LOG_D(NR_MAC, "%d.%d UE %04x mcs %d bler %f\n", frame, slot, UE->rnti, selected_mcs, sched_ctrl->dl_bler_stats.bler);
const int max_mcs = min(sched_ctrl->dl_max_mcs, max_mcs_table);
int selected_mcs;
if (bo->harq_round_max == 1) {
int new_mcs = min(bo->max_mcs, max_mcs);
selected_mcs = max(bo->min_mcs, new_mcs);
sched_ctrl->dl_bler_stats.mcs = selected_mcs;
} else
selected_mcs = get_mcs_from_bler(bo, stats, &sched_ctrl->dl_bler_stats, max_mcs, frame);
int l = get_dl_nrOfLayers(sched_ctrl, current_BWP->dci_format);
const uint8_t Qm = nr_get_Qm_dl(selected_mcs, current_BWP->mcsTableIdx);
const uint16_t R = nr_get_code_rate_dl(selected_mcs, current_BWP->mcsTableIdx);
@@ -739,10 +732,6 @@ static void pf_dl(gNB_MAC_INST *mac,
UEsched_t *iterator = UE_sched;
const int min_rbSize = 5;
const int UEperTTI = 4;
const int schedUE = max(1, min(UEperTTI, numUE));
DevAssert(num_beams == 1);
const int rbPerUE = n_rb_sched[0] / schedUE;
/* Loop UE_sched to find max coeff and allocate transmission */
while (iterator->UE != NULL) {
@@ -799,7 +788,7 @@ static void pf_dl(gNB_MAC_INST *mac,
int bwp_size = bwp_info.bwpSize;
// Freq-demain allocation
int max_rbSize = 0;
if (!get_rb_alloc(min_rbSize, rbPerUE, bwp_start, bwp_size, rballoc_mask, slbitmap, &rbStart, &max_rbSize)) {
if (!get_rb_alloc(min_rbSize, bwp_size, bwp_start, bwp_size, rballoc_mask, slbitmap, &rbStart, &max_rbSize)) {
LOG_D(NR_MAC,
"(%d.%d) Cannot schedule RNTI %04x, rbStart %d, rbSize %d\n",
frame,
@@ -916,7 +905,8 @@ static void nr_dlsch_preprocessor(gNB_MAC_INST *mac, post_process_pdsch_t *pp_pd
for (int i = 0; i < num_beams; i++)
n_rb_sched[i] = bw;
int max_sched_ues = 10;
int average_agg_level = 4; // TODO find a better estimation
int max_sched_ues = bw / (average_agg_level * NR_NB_REG_PER_CCE);
// FAPI cannot handle more than MAX_DCI_CORESET DCIs
max_sched_ues = min(max_sched_ues, MAX_DCI_CORESET);

View File

@@ -228,9 +228,9 @@ uint16_t get_pm_index(const gNB_MAC_INST *nrmac,
// 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] = {
-16, -8, 2, 12, 22, 32, 40, 48, 52, 60, // 0..9
82, 86, 94, 100, 110, 116, 122, 136, 142, 150, // 10..19
160, 164, 174, 182, 192, 200, 212, 216, 242 // 20..28
-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)
@@ -811,61 +811,40 @@ NR_pusch_dmrs_t get_ul_dmrs_params(const NR_ServingCellConfigCommon_t *scc,
#define BLER_UPDATE_FRAME 10
#define BLER_FILTER 0.9f
#define MAX_BLER_EST_MCS_DIFF 10
#define MAX_FRAMES_ACTIVE 100
int estimate_next_mcs(const NR_bler_options_t *bler_options,
int get_mcs_from_bler(const NR_bler_options_t *bler_options,
const NR_mac_dir_stats_t *stats,
NR_bler_stats_t *bler_stats,
int max_mcs,
int est_mcs,
frame_t frame,
bool ue_is_active)
frame_t frame)
{
int diff = frame - bler_stats->last_frame;
if (diff < 0) // wrap around
diff += 1024;
bler_stats->frames_inactive = ue_is_active ? 0 : bler_stats->frames_inactive + diff;
max_mcs = min(max_mcs, bler_options->max_mcs);
int new_mcs = bler_stats->mcs;
if (bler_stats->frames_inactive >= MAX_FRAMES_ACTIVE || bler_options->harq_round_max == 1) {
// if UE is inactive, return MCS estimation (or previous if no estimation)
// or if there is no HARQ => no retransmission => no BLER
if (est_mcs > 0) {
// bound the estimation between user configured min/max MCS and current
// MCS table max
est_mcs = min(est_mcs, max_mcs);
new_mcs = max(est_mcs, bler_options->min_mcs);
}
} else if (est_mcs > 0 && abs(bler_stats->mcs - est_mcs) >= MAX_BLER_EST_MCS_DIFF) {
// there is a big difference between MCS from BLER and what is estimated,
// correct this quickly
LOG_D(NR_MAC, "correct MCS %d => %d\n", bler_stats->mcs, est_mcs);
est_mcs = min(est_mcs, max_mcs);
new_mcs = max(est_mcs, bler_options->min_mcs);
} else {
if (diff < BLER_UPDATE_FRAME)
return bler_stats->mcs; // no update
const uint8_t old_mcs = min(bler_stats->mcs, max_mcs);
if (diff < BLER_UPDATE_FRAME)
return old_mcs; // no update
// last update is longer than x frames ago
const int num_dl_sched = (int)(stats->rounds[0] - bler_stats->rounds[0]);
const int num_dl_retx = (int)(stats->rounds[1] - bler_stats->rounds[1]);
const float bler_window = num_dl_sched > 0 ? (float) num_dl_retx / num_dl_sched : bler_stats->bler;
bler_stats->bler = BLER_FILTER * bler_stats->bler + (1 - BLER_FILTER) * bler_window;
// last update is longer than x frames ago
const int num_dl_sched = (int)(stats->rounds[0] - bler_stats->rounds[0]);
const int num_dl_retx = (int)(stats->rounds[1] - bler_stats->rounds[1]);
const float bler_window = num_dl_sched > 0 ? (float) num_dl_retx / num_dl_sched : bler_stats->bler;
bler_stats->bler = BLER_FILTER * bler_stats->bler + (1 - BLER_FILTER) * bler_window;
if (bler_stats->bler < bler_options->lower && new_mcs < max_mcs)
new_mcs += 1;
else if (bler_stats->bler > bler_options->upper && new_mcs > bler_options->min_mcs)
new_mcs -= 1;
// else we are within threshold boundaries or at limits
LOG_D(MAC, "frame %4d MCS %d -> %d (num_dl_sched %d, num_dl_retx %d, BLER wnd %.3f avg %.6f)\n",
frame, bler_stats->mcs, new_mcs, num_dl_sched, num_dl_retx, bler_window, bler_stats->bler);
}
int new_mcs = old_mcs;
if (bler_stats->bler < bler_options->lower && old_mcs < max_mcs && num_dl_sched > 3)
new_mcs += 1;
else if (bler_stats->bler > bler_options->upper || num_dl_sched <= 3) // above threshold or no activity
new_mcs -= 1;
// else we are within threshold boundaries
new_mcs = max(new_mcs, bler_options->min_mcs);
bler_stats->last_frame = frame;
bler_stats->mcs = new_mcs;
memcpy(bler_stats->rounds, stats->rounds, sizeof(stats->rounds));
LOG_D(MAC, "frame %4d MCS %d -> %d (num_dl_sched %d, num_dl_retx %d, BLER wnd %.3f avg %.6f)\n",
frame, old_mcs, new_mcs, num_dl_sched, num_dl_retx, bler_window, bler_stats->bler);
return new_mcs;
}
@@ -3095,6 +3074,7 @@ bool add_connected_nr_ue(gNB_MAC_INST *nr_mac, NR_UE_info_t *UE)
}
NR_UE_sched_ctrl_t *sched_ctrl = &UE->UE_sched_ctrl;
sched_ctrl->dl_max_mcs = 28; /* do not limit MCS for individual UEs */
sched_ctrl->pdcch_cl_adjust = 0;
reset_srs_stats(UE);

View File

@@ -498,10 +498,9 @@ static void evaluate_sinr_report(NR_UE_info_t *UE,
const int mcs_table = UE->current_DL_BWP.mcsTableIdx;
const int nrOfLayers = get_dl_nrOfLayers(sched_ctrl, UE->current_DL_BWP.dci_format);
int est_mcs = get_mcs_from_SINRx10(mcs_table, sinr_report->r[0].SINRx10, nrOfLayers);
sched_ctrl->dl_est_mcs.from_ssb_sinr = est_mcs;
sched_ctrl->dl_max_mcs = get_mcs_from_SINRx10(mcs_table, sinr_report->r[0].SINRx10, nrOfLayers);
LOG_D(MAC, "Reported SSB-SINR = %.1f, est_mcs %d\n", (float) sinr_report->r[0].SINRx10 / 10.f, est_mcs);
LOG_D(MAC, "Reported SSB-SINR = %01f, dl_max_mcs %d\n", sinr_report->r[0].SINRx10 / 10.0, sched_ctrl->dl_max_mcs);
}
static void evaluate_rsrp_report(NR_UE_info_t *UE,
@@ -640,10 +639,9 @@ static void evaluate_cqi_report(uint8_t *payload,
// TODO for wideband case and multiple TB
const int cqi_idx = sched_ctrl->CSI_report.cri_ri_li_pmi_cqi_report.wb_cqi_1tb;
const int mcs_table = UE->current_DL_BWP.mcsTableIdx;
int est_mcs = get_mcs_from_cqi(mcs_table, cqi_Table, cqi_idx);
sched_ctrl->dl_est_mcs.from_csirs_cqi = est_mcs;
sched_ctrl->dl_max_mcs = get_mcs_from_cqi(mcs_table, cqi_Table, cqi_idx);
LOG_D(MAC, "Reported CQI = %d, est_mcs %d\n", temp_cqi, est_mcs);
LOG_D(MAC, "Reported CQI = %d, dl_max_mcs %d\n", temp_cqi, sched_ctrl->dl_max_mcs);
}
static uint8_t evaluate_pmi_report(uint8_t *payload,

View File

@@ -1915,7 +1915,7 @@ static int pf_ul(gNB_MAC_INST *nrmac,
NR_mac_dir_stats_t *stats = &UE->mac_stats.ul;
/* Calculate throughput */
const float a = 0.05f;
const float a = 0.01f;
const uint32_t b = stats->current_bytes;
UE->ul_thr_ue = (1 - a) * UE->ul_thr_ue + a * b;
@@ -2001,11 +2001,18 @@ static int pf_ul(gNB_MAC_INST *nrmac,
const NR_bler_options_t *bo = &nrmac->ul_bler;
const int max_mcs_table = (current_BWP->mcs_table == 0 || current_BWP->mcs_table == 2) ? 28 : 27;
const int max_mcs = min(bo->max_mcs, max_mcs_table); /* no per-user maximum MCS yet */
int selected_mcs;
int nrOfLayers = get_ul_nrOfLayers(sched_ctrl, current_BWP->dci_format);
bool sched_inactive = B == 0 && do_sched;
const int est_mcs = get_mcs_from_SINRx10(current_BWP->mcs_table, sched_ctrl->pusch_pc.avg_snr * 10, nrOfLayers);
int selected_mcs = estimate_next_mcs(bo, stats, &sched_ctrl->ul_bler_stats, max_mcs_table, est_mcs, frame, !sched_inactive);
LOG_D(NR_MAC, "%d.%d UE %04x mcs %d bler %f\n", frame, slot, UE->rnti, selected_mcs, sched_ctrl->ul_bler_stats.bler);
if (bo->harq_round_max == 1) {
selected_mcs = get_mcs_from_SINRx10(current_BWP->mcs_table, sched_ctrl->pusch_pc.avg_snr * 10, 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);
}
/* Create UE_sched for UEs eligibale for new data transmission*/
/* Calculate coefficient*/
@@ -2021,6 +2028,7 @@ static int pf_ul(gNB_MAC_INST *nrmac,
nrOfLayers)
>> 3;
float coeff_ue = (float) tbs / UE->ul_thr_ue;
bool sched_inactive = B == 0 && do_sched;
LOG_D(NR_MAC, "[UE %04x][%4d.%2d] b %d, ul_thr_ue %f, tbs %d, coeff_ue %f, sched_inactive %d\n",
UE->rnti,
frame,

View File

@@ -434,13 +434,11 @@ bool get_rb_alloc(int rbSize_min,
int *rbStart_ptr,
int *rbSize_ptr);
int estimate_next_mcs(const NR_bler_options_t *bler_options,
int get_mcs_from_bler(const NR_bler_options_t *bler_options,
const NR_mac_dir_stats_t *stats,
NR_bler_stats_t *bler_stats,
int max_mcs,
int est_mcs,
frame_t frame,
bool ue_is_active);
frame_t frame);
int ul_buffer_index(int frame, int slot, int slots_per_frame, int size);
void UL_tti_req_ahead_initialization(gNB_MAC_INST *gNB, int n, int CCid, frame_t frameP, int slotP);

View File

@@ -523,7 +523,6 @@ typedef struct NR_bler_stats {
float bler;
uint8_t mcs;
uint64_t rounds[8];
int frames_inactive;
} NR_bler_stats_t;
//
@@ -608,11 +607,6 @@ typedef struct nr_power_control {
float tpc_in_flight; /// TPCs applied by UE but not yet in average SNR
} nr_power_control_t;
typedef struct dl_est_mcs {
uint8_t from_csirs_cqi;
uint8_t from_ssb_sinr;
} dl_est_mcs_t;
/*! \brief scheduling control information set through an API */
typedef struct {
/// CCE index and aggregation, should be coherent with cce_list
@@ -643,7 +637,8 @@ typedef struct {
/// PHR info: nominal UE transmit power levels (dBm)
int pcmax;
dl_est_mcs_t dl_est_mcs;
/// UE-estimated maximum MCS (from CSI-RS)
uint8_t dl_max_mcs;
/// For UL synchronization: store last UL scheduling grant
frame_t last_ul_frame;

View File

@@ -23,7 +23,7 @@
#define MAX_NUM_TX_REQUEST_PDU 100
#define MAX_NUM_NR_RX_RACH_PDUS 4
#define MAX_UL_PDUS_PER_SLOT 16
#define MAX_UL_PDUS_PER_SLOT 8
#define MAX_NUM_NR_SRS_PDUS 8
#define MAX_NUM_NR_UCI_PDUS MAX_MOBILES_PER_GNB
typedef struct {