From 14cceed229c16f82fadd382f1bc256c9c3aee134 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Mon, 30 Mar 2026 11:56:13 +0100 Subject: [PATCH 01/72] Fix incorrect SRS subcarrier mapping in fill_srs_channel_matrix The previous implementation of fill_srs_channel_matrix assumed an incorrect SRS subcarrier mapping, leading to wrong indexing of the estimated channel in frequency domain --- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 65 ++++++++++------------- 1 file changed, 27 insertions(+), 38 deletions(-) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index de72db32db..63f5aa8c87 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -712,56 +712,50 @@ static int fill_srs_reported_symbol(nfapi_nr_srs_reported_symbol_t *reported_sym return 0; } -static int fill_srs_channel_matrix( - nfapi_nr_srs_normalized_channel_iq_matrix_t *nr_srs_channel_iq_matrix, - const nfapi_nr_srs_pdu_t *srs_pdu, - const nr_srs_info_t *nr_srs_info, - const uint8_t normalized_iq_representation, - const uint16_t num_gnb_antenna_elements, - const uint16_t num_ue_srs_ports, - const uint16_t prg_size, - const uint16_t num_prgs, - const NR_DL_FRAME_PARMS *frame_parms, - const c16_t srs_estimated_channel_freq[][1 << srs_pdu->num_ant_ports] - [frame_parms->ofdm_symbol_size * (1 << srs_pdu->num_symbols)]) +static int fill_srs_channel_matrix(nfapi_nr_srs_normalized_channel_iq_matrix_t *nr_srs_channel_iq_matrix, + const nfapi_nr_srs_pdu_t *srs_pdu, + const nr_srs_info_t *nr_srs_info, + const NR_DL_FRAME_PARMS *frame_parms, + const c16_t srs_estimated_channel_freq[][1 << srs_pdu->num_ant_ports][frame_parms->ofdm_symbol_size * (1 << srs_pdu->num_symbols)]) { + const uint16_t num_gnb_antenna_elements = frame_parms->nb_antennas_rx; + const uint16_t num_ue_srs_ports = srs_pdu->srs_parameters_v4.num_total_ue_antennas; + const uint8_t normalized_iq_representation = srs_pdu->srs_parameters_v4.iq_representation; + const uint16_t prg_size = srs_pdu->srs_parameters_v4.prg_size; + const uint16_t num_prgs = srs_pdu->srs_parameters_v4.srs_bandwidth_size / srs_pdu->srs_parameters_v4.prg_size; + const uint64_t first_subcarrier = + (frame_parms->first_carrier_offset - (frame_parms->ofdm_symbol_size >> 1)) + srs_pdu->bwp_start * NR_NB_SC_PER_RB; + const uint16_t step = prg_size * NR_NB_SC_PER_RB; + nr_srs_channel_iq_matrix->normalized_iq_representation = normalized_iq_representation; nr_srs_channel_iq_matrix->num_gnb_antenna_elements = num_gnb_antenna_elements; nr_srs_channel_iq_matrix->num_ue_srs_ports = num_ue_srs_ports; nr_srs_channel_iq_matrix->prg_size = prg_size; nr_srs_channel_iq_matrix->num_prgs = num_prgs; + uint8_t *channel_matrix = nr_srs_channel_iq_matrix->channel_matrix; - const uint64_t subcarrier_offset = frame_parms->first_carrier_offset + srs_pdu->bwp_start*NR_NB_SC_PER_RB; - const uint16_t step = prg_size*NR_NB_SC_PER_RB; + c16_t *channel_matrix16 = (c16_t *)channel_matrix; + c8_t *channel_matrix8 = (c8_t *)channel_matrix; - c16_t *channel_matrix16 = (c16_t*)channel_matrix; - c8_t *channel_matrix8 = (c8_t*)channel_matrix; + for (int uI = 0; uI < num_ue_srs_ports; uI++) { + for (int gI = 0; gI < num_gnb_antenna_elements; gI++) { - for(int uI = 0; uI < num_ue_srs_ports; uI++) { - for(int gI = 0; gI < num_gnb_antenna_elements; gI++) { + // channel_matrix8 or channel_matrix16 will only be populated for the symbol 0 + uint16_t subcarrier_abs = first_subcarrier + nr_srs_info->k_0_p[uI][0]; - uint16_t subcarrier = subcarrier_offset + nr_srs_info->k_0_p[uI][0]; - if (subcarrier >= frame_parms->ofdm_symbol_size) { - subcarrier -= frame_parms->ofdm_symbol_size; - } - - for(int pI = 0; pI < num_prgs; pI++) { - const c16_t *srs_estimated_channel16 = srs_estimated_channel_freq[gI][uI] + subcarrier; - uint16_t index = uI*num_gnb_antenna_elements*num_prgs + gI*num_prgs + pI; + for (int pI = 0; pI < num_prgs; pI++) { + const c16_t *srs_estimated_channel16 = srs_estimated_channel_freq[gI][uI] + subcarrier_abs; + uint16_t index = uI * num_gnb_antenna_elements * num_prgs + gI * num_prgs + pI; if (normalized_iq_representation == 0) { - channel_matrix8[index].r = (int8_t)(srs_estimated_channel16->r>>8); - channel_matrix8[index].i = (int8_t)(srs_estimated_channel16->i>>8); + channel_matrix8[index].r = (int8_t)(srs_estimated_channel16->r >> 8); + channel_matrix8[index].i = (int8_t)(srs_estimated_channel16->i >> 8); } else { channel_matrix16[index].r = srs_estimated_channel16->r; channel_matrix16[index].i = srs_estimated_channel16->i; } - // Subcarrier increment - subcarrier += step; - if (subcarrier >= frame_parms->ofdm_symbol_size) { - subcarrier -= frame_parms->ofdm_symbol_size; - } + subcarrier_abs += step; } } } @@ -1312,11 +1306,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N fill_srs_channel_matrix(&nr_srs_channel_iq_matrix, srs_pdu, gNB->nr_srs_info[i], - srs_pdu->srs_parameters_v4.iq_representation, - nb_antennas_rx, - srs_pdu->srs_parameters_v4.num_total_ue_antennas, - srs_pdu->srs_parameters_v4.prg_size, - srs_pdu->srs_parameters_v4.srs_bandwidth_size / srs_pdu->srs_parameters_v4.prg_size, frame_parms, srs_estimated_channel_freq); From ec327d26fc6508021a2bafdd208ba69059daf281 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 12:26:35 +0200 Subject: [PATCH 02/72] Compilation without T: move to physims --- docker/Dockerfile.clang.rhel9 | 2 +- docker/Dockerfile.phySim.rhel9 | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/docker/Dockerfile.clang.rhel9 b/docker/Dockerfile.clang.rhel9 index 81fd8ccc83..4fae70955f 100644 --- a/docker/Dockerfile.clang.rhel9 +++ b/docker/Dockerfile.clang.rhel9 @@ -25,4 +25,4 @@ RUN /bin/sh oaienv && \ CFLAGS="-Werror" CXXFLAGS="-Werror -Wno-vla-cxx-extension" \ ./build_oai --phy_simulators --gNB --eNB --nrUE --UE \ --ninja --build-tool-opt -k10 \ - --noavx512 --disable-T-Tracer -c + --noavx512 -c diff --git a/docker/Dockerfile.phySim.rhel9 b/docker/Dockerfile.phySim.rhel9 index ac1c8680ca..31ca26adef 100644 --- a/docker/Dockerfile.phySim.rhel9 +++ b/docker/Dockerfile.phySim.rhel9 @@ -24,6 +24,7 @@ RUN /bin/sh oaienv && \ -DAVX512=OFF -DCMAKE_C_FLAGS=-Werror -DCMAKE_CXX_FLAGS=-Werror \ -DENABLE_PHYSIM_TESTS=ON \ -DSANITIZE_ADDRESS=ON -DSANITIZE_UNDEFINED=ON \ + -DT_TRACER=OFF \ -DCMAKE_RUNTIME_OUTPUT_DIRECTORY=/opt/oai-physim/bin && \ ninja -k10 -j36 From dc260bd4d974928d9c5dd119ad8263748c97fbf4 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 25 Mar 2026 10:02:47 +0100 Subject: [PATCH 03/72] Fix (config API): print correct integer pointer in range check Use the integer pointer when reporting invalid integer values: in `config_check_intrange()` error print from `param->uptr` to `param->iptr`. --- common/config/config_userapi.c | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/common/config/config_userapi.c b/common/config/config_userapi.c index 76f9722d0a..cb08fa8b58 100644 --- a/common/config/config_userapi.c +++ b/common/config/config_userapi.c @@ -218,8 +218,12 @@ int config_check_intrange(configmodule_interface_t *cfg, paramdef_t *param) return 0; } - fprintf(stderr,"[CONFIG] config_check_intrange: %s: %i invalid value, authorized range: %i %i\n", - param->optname, (int)*(param->uptr), param->chkPptr->s2.okintrange[0], param->chkPptr->s2.okintrange[1]); + fprintf(stderr, + "[CONFIG] config_check_intrange: %s: %i invalid value, authorized range: %i %i\n", + param->optname, + (int)*(param->iptr), + param->chkPptr->s2.okintrange[0], + param->chkPptr->s2.okintrange[1]); return -1; } From 425ea41ba52c12cc66c53d6396be8f873a5d2df9 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 25 Mar 2026 10:11:58 +0100 Subject: [PATCH 04/72] fix (Config API): make config integer checks type-correct and add unsigned range validator Change 1: config_check_intval() now uses the correct integer pointer for TYPE_INT Problem: config_check_intval() previously dereferenced param->uptr regardless of param->type. For parameters declared as signed (TYPE_INT / TYPE_INT32), the active union member is param->iptr, and param->uptr may be NULL -> using uptr makes validation unsafe and type-inconsistent. What changed: Updated config_check_intval() behavior (signature unchanged). Handle both param->type, param->iptr and param->uptr. Change 1: new config_check_uintrange() for unsigned range constraints Problem: there was a signed range checker (config_check_intrange()) that reads param->iptr, but there was no dedicated unsigned range validator using param->uptr. What changed: added config_check_uintrange() in config_userapi.h/.c The new function reads param->uptr as uint32_t and validates against param->chkPptr->s2.okintrange[] endpoints. Change 3: constify input params for f2 function pointers Problem: checkedparam_t.s2.f2 was typed with non-const input params, even though range checks only read. A safer const-safe signature is required. What changed: s2.f2 now takes const configmodule_interface_t * and const paramdef_t *. config_check_intrange() and config_check_uintrange() use the same const-qualified parameters. --- common/config/config_paramdesc.h | 4 +-- common/config/config_userapi.c | 54 +++++++++++++++++++++++++++----- common/config/config_userapi.h | 3 +- 3 files changed, 51 insertions(+), 10 deletions(-) diff --git a/common/config/config_paramdesc.h b/common/config/config_paramdesc.h index 51c8626116..efb5c956d2 100644 --- a/common/config/config_paramdesc.h +++ b/common/config/config_paramdesc.h @@ -48,8 +48,8 @@ typedef union checkedparam { int num_okintval; /* number of valid values in the checkingval array */ } s1a; struct { - int (*f2)(configmodule_interface_t *cfg, - paramdef_t *param); /* check an integer against an authorized range, defined by its min and max value */ + int (*f2)(const configmodule_interface_t *cfg, + const paramdef_t *param); /* check an integer against an authorized range, defined by its min and max value */ int okintrange[CONFIG_MAX_NUMCHECKVAL]; /* integer array, store min and max values */ } s2; diff --git a/common/config/config_userapi.c b/common/config/config_userapi.c index cb08fa8b58..c009187800 100644 --- a/common/config/config_userapi.c +++ b/common/config/config_userapi.c @@ -183,14 +183,35 @@ int config_check_intval(configmodule_interface_t *cfg, paramdef_t *param) return -1; } - for ( int i=0; ichkPptr->s1.num_okintval ; i++) { - if( *(param->uptr) == param->chkPptr->s1.okintval[i] ) { - return 0; + if (param->type == TYPE_INT32 || param->type == TYPE_INT) { + if (param->iptr == NULL) { + fprintf(stderr, "[CONFIG] config_check_intval: %s: NULL iptr\n", param->optname); + return -1; } + const int v = *param->iptr; + for (int i = 0; i < param->chkPptr->s1.num_okintval; i++) { + if (v == param->chkPptr->s1.okintval[i]) + return 0; + } + fprintf(stderr, "[CONFIG] config_check_intval: %s: %i invalid value, authorized values:\n ", param->optname, v); + for (int i = 0; i < param->chkPptr->s1.num_okintval; i++) { + fprintf(stderr, " %i", param->chkPptr->s1.okintval[i]); + } + fprintf(stderr, " \n"); + return -1; + } else { + if (param->uptr == NULL) { + fprintf(stderr, "[CONFIG] config_check_intval: %s: NULL uptr\n", param->optname); + return -1; + } + for (int i = 0; i < param->chkPptr->s1.num_okintval; i++) { + if (*(param->uptr) == (uint32_t)param->chkPptr->s1.okintval[i]) { + return 0; + } + } + print_intvalueerror(param, "config_check_intval", param->chkPptr->s1.okintval, param->chkPptr->s1.num_okintval); + return -1; } - - print_intvalueerror(param,"config_check_intval", param->chkPptr->s1.okintval,param->chkPptr->s1.num_okintval); - return -1; } int config_check_modify_integer(configmodule_interface_t *cfg, paramdef_t *param) @@ -211,7 +232,7 @@ int config_check_modify_integer(configmodule_interface_t *cfg, paramdef_t *param return -1; } -int config_check_intrange(configmodule_interface_t *cfg, paramdef_t *param) +int config_check_intrange(const configmodule_interface_t *cfg, const paramdef_t *param) { UNUSED(cfg); if( *(param->iptr) >= param->chkPptr->s2.okintrange[0] && *(param->iptr) <= param->chkPptr->s2.okintrange[1] ) { @@ -227,6 +248,25 @@ int config_check_intrange(configmodule_interface_t *cfg, paramdef_t *param) return -1; } +int config_check_uintrange(const configmodule_interface_t *cfg, const paramdef_t *param) +{ + (void)cfg; + const uint32_t *v = param->uptr; + const int *range = param->chkPptr->s2.okintrange; + + if (*v >= (uint32_t)range[0] && *v <= (uint32_t)range[1]) { + return 0; + } + + fprintf(stderr, + "[CONFIG] config_check_uintrange: %s: %u invalid, authorized range: %i %i\n", + param->optname, + *v, + range[0], + range[1]); + return -1; +} + void print_strvalueerror(paramdef_t *param, char *fname, char **okval, int numokval) { fprintf(stderr,"[CONFIG] %s: %s: %s invalid value, authorized values:\n ", fname,param->optname, *param->strptr); diff --git a/common/config/config_userapi.h b/common/config/config_userapi.h index fafbd81019..b959cb57fe 100644 --- a/common/config/config_userapi.h +++ b/common/config/config_userapi.h @@ -51,7 +51,8 @@ int config_get_processedint(configmodule_interface_t *cfg, paramdef_t *cfgoption /* functions to be used in parameters definition, to check parameters values */ int config_check_intval(configmodule_interface_t *cfg, paramdef_t *param); int config_check_modify_integer(configmodule_interface_t *cfg, paramdef_t *param); -int config_check_intrange(configmodule_interface_t *cfg, paramdef_t *param); +int config_check_intrange(const configmodule_interface_t *cfg, const paramdef_t *param); +int config_check_uintrange(const configmodule_interface_t *cfg, const paramdef_t *param); int config_check_strval(configmodule_interface_t *cfg, paramdef_t *param); int config_checkstr_assign_integer(configmodule_interface_t *cfg, paramdef_t *param); const paramdef_t *config_get_paramdef_from_name(const paramdef_t *pd, int num, const char *name); From afb3005a7cbe7aa4991bd241b8a30cbe62a1095e Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Fri, 16 Jan 2026 00:38:52 +0100 Subject: [PATCH 05/72] Refactor (gNB Config): reuse PLMN helper and neighbour config parsing Refactor gNB configuration code to share PLMN extraction logic and to split neighbour-cell configuration into smaller helpers with safer allocation and clearer list handling. Changes: - Add `extract_plmn_from_params()` in `gnb_config_common.c` and declare it in `gnb_config_common.h` to centralize PLMN extraction from `paramdef_t`. - Update `set_plmn_config()` to use `extract_plmn_from_params()` when filling the PLMN array instead of duplicating field assignments. - Refactor `fill_neighbour_cell_configuration()` in `gnb_config.c` to delegate per-cell neighbour parsing to `parse_neighbour_cells_list()` and sorting to `sort_neighbour_configuration()`, simplifying the main loop. - Use `extract_plmn_from_params()` when parsing neighbour PLMNs in `parse_neighbour_cells_list()` and log each neighbour with its PLMN, PCI, and other radio parameters before adding it to the sequence. - Switch allocation of `rrc->neighbour_cell_configuration` in `fill_neighbour_cell_configuration()` from `malloc` to `malloc_or_fail()` to enforce consistent handling of allocation failures. --- openair2/GNB_APP/gnb_config.c | 137 ++++++++++++--------------- openair2/GNB_APP/gnb_config_common.c | 16 +++- openair2/GNB_APP/gnb_config_common.h | 2 + 3 files changed, 75 insertions(+), 80 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index a73a77b0ee..10ab9a73d0 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2012,94 +2012,81 @@ static nr_neighbour_cell_t *get_neighbour_by_pci(seq_arr_t *neighbour_cells, int return NULL; } -static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc) +static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, const paramlist_def_t *list, const char *n_path) { - char gnbpath[MAX_OPTNAME_SIZE + 8]; - snprintf(gnbpath, sizeof(gnbpath), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx); + cell->neighbour_cells = malloc_or_fail(sizeof(seq_arr_t)); + seq_arr_init(cell->neighbour_cells, sizeof(nr_neighbour_cell_t)); - GET_PARAMS_LIST(neighbour_list_param_list, - neighbour_list_params, - GNB_NEIGHBOUR_LIST_PARAM_LIST, - GNB_CONFIG_STRING_NEIGHBOUR_LIST, - gnbpath); + for (int l = 0; l < list->numelt; ++l) { + const paramdef_t *cell_params = list->paramarray[l]; + nr_neighbour_cell_t n = {.gNB_ID = *cell_params[GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr, + .nrcell_id = (uint64_t)*cell_params[GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr, + .physicalCellId = *cell_params[GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr, + .subcarrierSpacing = *cell_params[GNB_CONFIG_N_CELL_SCS_IDX].uptr, + .band = *cell_params[GNB_CONFIG_N_CELL_BAND_IDX].uptr, + .absoluteFrequencySSB = *cell_params[GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr, + .tac = *cell_params[GNB_CONFIG_N_CELL_TAC_IDX].uptr}; - if (neighbour_list_param_list.numelt < 1) - return; + char p[CONFIG_MAXOPTLENGTH]; // plmn path + snprintf(p, sizeof(p), "%s.%s.[%i].%s", n_path, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, l, GNB_CONFIG_STRING_NEIGHBOUR_PLMN); + GET_PARAMS(NeighbourPlmn, GNBPLMNPARAMS_DESC, p); + n.plmn = extract_plmn_from_params(NeighbourPlmn, sizeofArray(NeighbourPlmn)); - rrc->neighbour_cell_configuration = malloc(sizeof(seq_arr_t)); - seq_arr_init(rrc->neighbour_cell_configuration, sizeof(neighbour_cell_configuration_t)); + LOG_I(GNB_APP, + "Neighbour[%d]: cellId %ld, PLMN %03u.%0*u, gNB %d, PCI %d, SCS %d, SSB ARFCN %u, TAC %u\n", + l, + n.nrcell_id, + n.plmn.mcc, + n.plmn.mnc_digit_length, + n.plmn.mnc, + n.gNB_ID, + n.physicalCellId, + n.subcarrierSpacing, + n.absoluteFrequencySSB, + n.tac); - for (int elm = 0; elm < neighbour_list_param_list.numelt; ++elm) { - neighbour_cell_configuration_t cell = {0}; - cell.nr_cell_id = (uint64_t)*neighbour_list_param_list.paramarray[elm][0].u64ptr; - - char neighbourpath[MAX_OPTNAME_SIZE + 8]; - snprintf(neighbourpath, sizeof(neighbourpath), "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_NEIGHBOUR_LIST, elm); - GET_PARAMS_LIST(NeighbourCellParamList, - NeighbourCellParams, - GNBNEIGHBOURCELLPARAMS_DESC, - GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, - neighbourpath); - LOG_I(GNB_APP, "Cell %ld has %d neighbours\n", cell.nr_cell_id, NeighbourCellParamList.numelt); - if (NeighbourCellParamList.numelt < 1) - continue; - - cell.neighbour_cells = malloc_or_fail(sizeof(seq_arr_t)); - seq_arr_init(cell.neighbour_cells, sizeof(nr_neighbour_cell_t)); - for (int l = 0; l < NeighbourCellParamList.numelt; ++l) { - nr_neighbour_cell_t n = {0}; - n.gNB_ID = *(NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr); - n.nrcell_id = (uint64_t) * (NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr); - n.physicalCellId = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr; - n.subcarrierSpacing = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_SCS_IDX].uptr; - n.band = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_BAND_IDX].uptr; - n.absoluteFrequencySSB = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr; - n.tac = *NeighbourCellParamList.paramarray[l][GNB_CONFIG_N_CELL_TAC_IDX].uptr; - - char neighbour_plmn_path[CONFIG_MAXOPTLENGTH]; - snprintf(neighbour_plmn_path, - sizeof(neighbour_plmn_path), - "%s.%s.[%i].%s", - neighbourpath, - GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, - l, - GNB_CONFIG_STRING_NEIGHBOUR_PLMN); - GET_PARAMS(NeighbourPlmn, GNBPLMNPARAMS_DESC, neighbour_plmn_path); - - n.plmn.mcc = *NeighbourPlmn[GNB_MOBILE_COUNTRY_CODE_IDX].uptr; - n.plmn.mnc = *NeighbourPlmn[GNB_MOBILE_NETWORK_CODE_IDX].uptr; - n.plmn.mnc_digit_length = *NeighbourPlmn[GNB_MNC_DIGIT_LENGTH].uptr; - if (get_neighbour_by_pci(cell.neighbour_cells, n.physicalCellId) != NULL) { - LOG_E(GNB_APP, - "Cell %ld: duplicate PCI %d in neighbour list (nrcell_id %ld)\n", - cell.nr_cell_id, - n.physicalCellId, - n.nrcell_id); - AssertFatal(false, "PCI must be unique within a cell's neighbour list\n"); - } - seq_arr_push_back(cell.neighbour_cells, &n, sizeof(n)); - LOG_I(GNB_APP, - " [%d] neighbor ID %d cellId %ld PCI %d SCS %d SSB ARFCN %u TAC %u PLMN %03u.%0*u\n", - l, - n.gNB_ID, - n.nrcell_id, - n.physicalCellId, - n.subcarrierSpacing, - n.absoluteFrequencySSB, - n.tac, - n.plmn.mcc, - n.plmn.mnc_digit_length, - n.plmn.mnc); + if (get_neighbour_by_pci(cell->neighbour_cells, n.physicalCellId) != NULL) { + LOG_E(GNB_APP, "Cell %ld: duplicate PCI %d in neighbour list (NCI=%ld)\n", cell->nr_cell_id, n.physicalCellId, n.nrcell_id); + AssertFatal(false, "PCI must be unique within a cell's neighbour list\n"); } - seq_arr_push_back(rrc->neighbour_cell_configuration, &cell, sizeof(cell)); + + seq_arr_push_back(cell->neighbour_cells, &n, sizeof(n)); } +} + +static void sort_neighbour_configuration(gNB_RRC_INST *rrc) +{ void *base = seq_arr_front(rrc->neighbour_cell_configuration); size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration); size_t element_size = sizeof(neighbour_cell_configuration_t); - qsort(base, nmemb, element_size, sort_neighbour_cell_config_by_cell_id); } +static void fill_neighbour_cell_configuration(const uint8_t gnb_idx, gNB_RRC_INST *rrc) +{ + char path[MAX_OPTNAME_SIZE + 8]; + snprintf(path, sizeof(path), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx); + GET_PARAMS_LIST(nlist, nparams, GNB_NEIGHBOUR_LIST_PARAM_LIST, GNB_CONFIG_STRING_NEIGHBOUR_LIST, path); + + if (nlist.numelt < 1) + return; + + rrc->neighbour_cell_configuration = malloc_or_fail(sizeof(seq_arr_t)); + seq_arr_init(rrc->neighbour_cell_configuration, sizeof(neighbour_cell_configuration_t)); + + for (int elm = 0; elm < nlist.numelt; ++elm) { + neighbour_cell_configuration_t cell = {.nr_cell_id = *nlist.paramarray[elm][0].u64ptr}; + snprintf(path, sizeof(path), "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_NEIGHBOUR_LIST, elm); + GET_PARAMS_LIST(ncell_list, ncell_params, GNBNEIGHBOURCELLPARAMS_DESC, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, path); + if (ncell_list.numelt < 1) + continue; + parse_neighbour_cells_list(&cell, &ncell_list, path); + seq_arr_push_back(rrc->neighbour_cell_configuration, &cell, sizeof(cell)); + } + + sort_neighbour_configuration(rrc); +} + static void fill_measurement_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc) { char measurement_path[MAX_OPTNAME_SIZE + 8]; diff --git a/openair2/GNB_APP/gnb_config_common.c b/openair2/GNB_APP/gnb_config_common.c index 395af98750..5aed92f810 100644 --- a/openair2/GNB_APP/gnb_config_common.c +++ b/openair2/GNB_APP/gnb_config_common.c @@ -31,19 +31,25 @@ uint16_t set_snssai_config(nssai_t *nssai, const int max_num_ssi, uint8_t k, uin return num_ssi; } +plmn_id_t extract_plmn_from_params(const paramdef_t *params, int n_params) +{ + plmn_id_t plmn = {.mcc = *gpd(params, n_params, GNB_CONFIG_STRING_MOBILE_COUNTRY_CODE)->uptr, + .mnc = *gpd(params, n_params, GNB_CONFIG_STRING_MOBILE_NETWORK_CODE)->uptr, + .mnc_digit_length = *gpd(params, n_params, GNB_CONFIG_STRING_MNC_DIGIT_LENGTH)->uptr}; + AssertFatal((plmn.mnc_digit_length == 2) || (plmn.mnc_digit_length == 3), "BAD MNC DIGIT LENGTH %d", plmn.mnc_digit_length); + return plmn; +} + uint8_t set_plmn_config(plmn_id_t *p, uint8_t idx) { char gnbpath[MAX_OPTNAME_SIZE * 2 + 8]; snprintf(gnbpath, sizeof(gnbpath), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, idx); GET_PARAMS_LIST(PLMNParamList, PLMNParams, GNBPLMNPARAMS_DESC, GNB_CONFIG_STRING_PLMN_LIST, gnbpath, PLMNPARAMS_CHECK); uint8_t num_plmn = PLMNParamList.numelt; + const int n_plmn_params = sizeofArray(PLMNParams); AssertFatal(num_plmn >= 1 && num_plmn <= 6, "The number of PLMN IDs must be in [1,6], but is %d\n", num_plmn); for (int l = 0; l < num_plmn; ++l) { - plmn_id_t *plmn = &p[l]; - plmn->mcc = *PLMNParamList.paramarray[l][GNB_MOBILE_COUNTRY_CODE_IDX].uptr; - plmn->mnc = *PLMNParamList.paramarray[l][GNB_MOBILE_NETWORK_CODE_IDX].uptr; - plmn->mnc_digit_length = *PLMNParamList.paramarray[l][GNB_MNC_DIGIT_LENGTH].u8ptr; - AssertFatal((plmn->mnc_digit_length == 2) || (plmn->mnc_digit_length == 3), "BAD MNC DIGIT LENGTH %d", plmn->mnc_digit_length); + p[l] = extract_plmn_from_params(PLMNParamList.paramarray[l], n_plmn_params); } return num_plmn; } diff --git a/openair2/GNB_APP/gnb_config_common.h b/openair2/GNB_APP/gnb_config_common.h index ae2ae4e799..bfa875c13d 100644 --- a/openair2/GNB_APP/gnb_config_common.h +++ b/openair2/GNB_APP/gnb_config_common.h @@ -4,6 +4,8 @@ #include #include "common/5g_platform_types.h" +#include "common/config/config_paramdesc.h" uint16_t set_snssai_config(nssai_t *nssai, const int max_num_ssi, uint8_t k, uint8_t l); uint8_t set_plmn_config(plmn_id_t *p, uint8_t idx); +plmn_id_t extract_plmn_from_params(const paramdef_t *params, int n_params); From 196855364caa657b62f43efb2b3f2c03f052a503 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 11 Feb 2026 11:48:58 +0100 Subject: [PATCH 06/72] Add documentation to refactored functions in gnb_config.c --- openair2/GNB_APP/gnb_config.c | 11 +++++++++++ openair2/GNB_APP/gnb_config_common.c | 3 +++ 2 files changed, 14 insertions(+) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 10ab9a73d0..c758b391b8 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2012,11 +2012,16 @@ static nr_neighbour_cell_t *get_neighbour_by_pci(seq_arr_t *neighbour_cells, int return NULL; } +/** @brief Parse and add neighbour cells for a given cell configuration + * @param[in,out] cell Neighbour cell configuration to populate + * @param[in] list Parameter list describing neighbour cells + * @param[in] n_path Base path for neighbour configuration in the config tree */ static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, const paramlist_def_t *list, const char *n_path) { cell->neighbour_cells = malloc_or_fail(sizeof(seq_arr_t)); seq_arr_init(cell->neighbour_cells, sizeof(nr_neighbour_cell_t)); + /* Parse each neighbour cell in the list */ for (int l = 0; l < list->numelt; ++l) { const paramdef_t *cell_params = list->paramarray[l]; nr_neighbour_cell_t n = {.gNB_ID = *cell_params[GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr, @@ -2045,6 +2050,7 @@ static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, con n.absoluteFrequencySSB, n.tac); + /* Check for duplicate PCI in the neighbour list */ if (get_neighbour_by_pci(cell->neighbour_cells, n.physicalCellId) != NULL) { LOG_E(GNB_APP, "Cell %ld: duplicate PCI %d in neighbour list (NCI=%ld)\n", cell->nr_cell_id, n.physicalCellId, n.nrcell_id); AssertFatal(false, "PCI must be unique within a cell's neighbour list\n"); @@ -2054,6 +2060,8 @@ static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, con } } +/** @brief Sort neighbour cell configuration by cell ID + * @param[in] rrc Pointer to RRC instance */ static void sort_neighbour_configuration(gNB_RRC_INST *rrc) { void *base = seq_arr_front(rrc->neighbour_cell_configuration); @@ -2062,6 +2070,9 @@ static void sort_neighbour_configuration(gNB_RRC_INST *rrc) qsort(base, nmemb, element_size, sort_neighbour_cell_config_by_cell_id); } +/** @brief Fill neighbour cell configuration from config file + * @param[in] gnb_idx gNB index + * @param[in,out] rrc Pointer to RRC instance */ static void fill_neighbour_cell_configuration(const uint8_t gnb_idx, gNB_RRC_INST *rrc) { char path[MAX_OPTNAME_SIZE + 8]; diff --git a/openair2/GNB_APP/gnb_config_common.c b/openair2/GNB_APP/gnb_config_common.c index 5aed92f810..aca2457d08 100644 --- a/openair2/GNB_APP/gnb_config_common.c +++ b/openair2/GNB_APP/gnb_config_common.c @@ -31,6 +31,9 @@ uint16_t set_snssai_config(nssai_t *nssai, const int max_num_ssi, uint8_t k, uin return num_ssi; } +/** @brief Extract PLMN from parameter array + * @param[in] params Parameter array containing PLMN fields + * @return plmn_id_t structure filled from parameters */ plmn_id_t extract_plmn_from_params(const paramdef_t *params, int n_params) { plmn_id_t plmn = {.mcc = *gpd(params, n_params, GNB_CONFIG_STRING_MOBILE_COUNTRY_CODE)->uptr, From 442b3045d6c0f8a8543fef86ba2e189321f36d08 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 3 Mar 2026 14:54:59 +0100 Subject: [PATCH 07/72] Refactor (gNB Config): look up neighbour-list nr_cellid by name with gpd Outer gNB neighbour_list entries now only declare the serving cell nr_cellid. The code resolves that field with gpd instead of a fixed column index. Changes: - gnb_paramdef.h: remove unused NEIGHBOUR_CELL_PHYSICAL_ID from GNB_NEIGHBOUR_LIST_PARAM_LIST (keep NRCELLID only). - gnb_config.c: in fill_neighbour_cell_configuration, set nr_cell_id from gpd --- openair2/GNB_APP/gnb_config.c | 3 ++- openair2/GNB_APP/gnb_paramdef.h | 1 - 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index c758b391b8..49ff67a8de 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2086,7 +2086,8 @@ static void fill_neighbour_cell_configuration(const uint8_t gnb_idx, gNB_RRC_INS seq_arr_init(rrc->neighbour_cell_configuration, sizeof(neighbour_cell_configuration_t)); for (int elm = 0; elm < nlist.numelt; ++elm) { - neighbour_cell_configuration_t cell = {.nr_cell_id = *nlist.paramarray[elm][0].u64ptr}; + neighbour_cell_configuration_t cell = {0}; + cell.nr_cell_id = *gpd(nlist.paramarray[elm], sizeofArray(nparams), GNB_CONFIG_STRING_NRCELLID)->u64ptr; snprintf(path, sizeof(path), "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_NEIGHBOUR_LIST, elm); GET_PARAMS_LIST(ncell_list, ncell_params, GNBNEIGHBOURCELLPARAMS_DESC, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, path); if (ncell_list.numelt < 1) diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 8265303969..05714771b4 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -289,7 +289,6 @@ typedef enum { #define GNB_NEIGHBOUR_LIST_PARAM_LIST { \ /* optname helpstr paramflags XXXptr def val type numelt */ \ {GNB_CONFIG_STRING_NRCELLID, "cell nrCell Id which has neighbours", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0}, \ - {GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID, "neighbour cell physical id", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ } // clang-format on #define GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST "neighbour_cell_configuration" From 185690cb94cac5071cbcbf383dc4aa97d7525a24 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 11 Feb 2026 17:28:47 +0100 Subject: [PATCH 08/72] Refactor (RRC): inline neighbour cell arrays and constify access Inline neighbour cell arrays into the RRC neighbour configuration and update RRC/DU helpers to use const-aware access and shared iteration helpers when walking neighbour lists and building measurement configs. Changes: - Change neighbour_cells in neighbour_cell_configuration_t from a seq_arr_t * to an embedded seq_arr_t field. - Initialize and populate the embedded neighbour_cells directly in parse_neighbour_cells_list in gnb_config.c, dropping dynamic allocation of the neighbour list container. - Refactor get_neighbour_cell_by_pci and nr_rrc_get_measconfig in rrc_gNB.c to access the embedded neighbour_cells via const pointers, introduce a local meas_cfg and a3_event_list, and iterate neighbour cells with FOR_EACH_SEQ_ARR. - Treat neighbour cell configuration entries as const in rrc_gNB_du.c (get_cell_neighbour_list, label_intra_frequency_neighbours, valid_du_in_neighbour_configs) and iterate neighbour lists with FOR_EACH_SEQ_ARR instead of index-based loops. --- openair2/GNB_APP/gnb_config.c | 7 +++---- openair2/RRC/NR/nr_rrc_defs.h | 2 +- openair2/RRC/NR/rrc_gNB.c | 27 +++++++++++++-------------- openair2/RRC/NR/rrc_gNB_du.c | 16 +++++++--------- 4 files changed, 24 insertions(+), 28 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 49ff67a8de..213584a44d 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2018,8 +2018,7 @@ static nr_neighbour_cell_t *get_neighbour_by_pci(seq_arr_t *neighbour_cells, int * @param[in] n_path Base path for neighbour configuration in the config tree */ static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, const paramlist_def_t *list, const char *n_path) { - cell->neighbour_cells = malloc_or_fail(sizeof(seq_arr_t)); - seq_arr_init(cell->neighbour_cells, sizeof(nr_neighbour_cell_t)); + seq_arr_init(&cell->neighbour_cells, sizeof(nr_neighbour_cell_t)); /* Parse each neighbour cell in the list */ for (int l = 0; l < list->numelt; ++l) { @@ -2051,12 +2050,12 @@ static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, con n.tac); /* Check for duplicate PCI in the neighbour list */ - if (get_neighbour_by_pci(cell->neighbour_cells, n.physicalCellId) != NULL) { + if (get_neighbour_by_pci(&cell->neighbour_cells, n.physicalCellId) != NULL) { LOG_E(GNB_APP, "Cell %ld: duplicate PCI %d in neighbour list (NCI=%ld)\n", cell->nr_cell_id, n.physicalCellId, n.nrcell_id); AssertFatal(false, "PCI must be unique within a cell's neighbour list\n"); } - seq_arr_push_back(cell->neighbour_cells, &n, sizeof(n)); + seq_arr_push_back(&cell->neighbour_cells, &n, sizeof(n)); } } diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 606eb86327..3b07ec6a6c 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -326,7 +326,7 @@ typedef struct { typedef struct neighbour_cell_configuration_s { uint64_t nr_cell_id; - seq_arr_t *neighbour_cells; + seq_arr_t neighbour_cells; } neighbour_cell_configuration_t; typedef struct nr_mac_rrc_dl_if_s { diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 096dc98eea..4c47e6a23b 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -263,10 +263,8 @@ static bool eq_pci(const void *vval, const void *vit) const nr_neighbour_cell_t *get_neighbour_cell_by_pci(const neighbour_cell_configuration_t *cell, int pci) { - seq_arr_t *head = cell->neighbour_cells; - DevAssert(head != NULL); - LOG_D(NR_RRC, "Number of neighbour cells: %ld\n", head->size); - elm_arr_t e = find_if(head, &pci, eq_pci); + DevAssert(cell); + elm_arr_t e = find_if((seq_arr_t *)&cell->neighbour_cells, &pci, eq_pci); if (e.found) { const nr_neighbour_cell_t *neighbour = (const nr_neighbour_cell_t *)e.it; LOG_D(NR_RRC, "Found matching neighbour cell with PCI %d and Cell ID %ld\n", neighbour->physicalCellId, neighbour->nrcell_id); @@ -772,23 +770,23 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell const NR_MeasTimingList_t *mtlist = cell->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; const NR_MeasTiming_t *mt = mtlist->list.array[0]; const neighbour_cell_configuration_t *neighbour_config = get_neighbour_cell_config(rrc, cell->info.cell_id); - seq_arr_t *neighbour_cells = NULL; - if (neighbour_config) - neighbour_cells = neighbour_config->neighbour_cells; + const seq_arr_t *neighbour_cells = neighbour_config ? &neighbour_config->neighbour_cells : NULL; int *neigh_a3_id = NULL; if (neighbour_cells && neighbour_cells->size) neigh_a3_id = calloc_or_fail(neighbour_cells->size, sizeof(int)); - if (neighbour_cells && rrc->measurementConfiguration.a3_event_list && rrc->measurementConfiguration.a3_event_list->size > 0) { + const nr_measurement_configuration_t *meas_cfg = &rrc->measurementConfiguration; + const seq_arr_t *a3_event_list = meas_cfg->a3_event_list; + if (neighbour_cells && a3_event_list && a3_event_list->size > 0) { /* Loop through neighbours and find related A3 configuration If no related A3 but there is default add the default one. If default one added once as a report, no need to add it again && duplication. */ LOG_D(NR_RRC, "Preparing A3 Event Measurement Configuration!\n"); bool default_a3_added = false; // To ensure that the default configuration is only added once - for (int i = 0; i < neighbour_cells->size; i++) { - nr_neighbour_cell_t *neighbourCell = (nr_neighbour_cell_t *)seq_arr_at(neighbour_cells, i); + int i = 0; + FOR_EACH_SEQ_ARR(nr_neighbour_cell_t *, neighbourCell, neighbour_cells) { seq_arr_push_back(&neigh_seq, neighbourCell, sizeof(nr_neighbour_cell_t)); const nr_a3_event_t *a3Event = get_a3_configuration((gNB_RRC_INST *)rrc, neighbourCell->physicalCellId); if (!a3Event) { @@ -814,12 +812,13 @@ NR_MeasConfig_t *nr_rrc_get_measconfig(const gNB_RRC_INST *rrc, uint64_t nr_cell } NR_ReportConfigId_t reportConfigId = neigh_a3_id[i]; seq_arr_push_back(&rc_A3_seq, prepare_a3_event_report(a3Event, reportConfigId), sizeof(NR_ReportConfigToAddMod_t)); + i++; } } - if (rrc->measurementConfiguration.per_event) - rc_PER = prepare_periodic_event_report(rrc->measurementConfiguration.per_event); - if (rrc->measurementConfiguration.a2_event) - rc_A2 = prepare_a2_event_report(rrc->measurementConfiguration.a2_event); + if (meas_cfg->per_event) + rc_PER = prepare_periodic_event_report(meas_cfg->per_event); + if (meas_cfg->a2_event) + rc_A2 = prepare_a2_event_report(meas_cfg->a2_event); NR_MeasConfig_t *result = get_MeasConfig(mt, band, cell->info.pci, rc_PER, rc_A2, &rc_A3_seq, &neigh_seq, neigh_a3_id); diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index ec139e684d..71da3f3d1b 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -152,7 +152,7 @@ static int nr_cell_id_match(const void *key, const void *element) return 1; } -static neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INST *rrc, const nr_rrc_cell_container_t *cell) +static const neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INST *rrc, const nr_rrc_cell_container_t *cell) { void *base = seq_arr_front(rrc->neighbour_cell_configuration); size_t nmemb = seq_arr_size(rrc->neighbour_cell_configuration); @@ -160,7 +160,7 @@ static neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_RRC_INS void *it = bsearch((void *)&cell->info.cell_id, base, nmemb, size, nr_cell_id_match); - return (neighbour_cell_configuration_t *)it; + return (const neighbour_cell_configuration_t *)it; } static void is_intra_frequency_neighbour(void *ssb_arfcn, void *neighbour_cell) @@ -183,7 +183,7 @@ static void label_intra_frequency_neighbours(gNB_RRC_INST *rrc, const nr_rrc_cel if (!rrc->neighbour_cell_configuration) return; - neighbour_cell_configuration_t *neighbour_cell_config = get_cell_neighbour_list(rrc, cell); + const neighbour_cell_configuration_t *neighbour_cell_config = get_cell_neighbour_list(rrc, cell); if (!neighbour_cell_config) return; @@ -194,8 +194,8 @@ static void label_intra_frequency_neighbours(gNB_RRC_INST *rrc, const nr_rrc_cel cell->info.pci, ssb_arfcn); - seq_arr_t *cell_neighbour_list = neighbour_cell_config->neighbour_cells; - for_each(cell_neighbour_list, (void *)&ssb_arfcn, is_intra_frequency_neighbour); + const seq_arr_t *cell_neighbour_list = &neighbour_cell_config->neighbour_cells; + for_each((seq_arr_t *)cell_neighbour_list, (void *)&ssb_arfcn, is_intra_frequency_neighbour); } static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_configuration, const f1ap_served_cell_info_t *cell) @@ -206,10 +206,8 @@ static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_config int ssb_arfcn = ssb_arfcn_mtc(mtc); ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc); - for (int c = 0; c < neighbour_cell_configuration->size; c++) { - const neighbour_cell_configuration_t *neighbour_config = seq_arr_at(neighbour_cell_configuration, c); - for (int ni = 0; ni < neighbour_config->neighbour_cells->size; ni++) { - const nr_neighbour_cell_t *nc = seq_arr_at(neighbour_config->neighbour_cells, ni); + FOR_EACH_SEQ_ARR(const neighbour_cell_configuration_t *, neighbour_config, neighbour_cell_configuration) { + FOR_EACH_SEQ_ARR(const nr_neighbour_cell_t *, nc, &neighbour_config->neighbour_cells) { if (nc->nrcell_id != cell->nr_cellid) continue; // current cell is in the nc config, check that config matches From 7606a93f57ec77b0472e98ecd95df364e6c41271 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 7 Apr 2026 11:20:25 +0200 Subject: [PATCH 09/72] Refactor (RRC): validate neighbour-cell params and use gpd lookups Tighten neighbour-cell config parsing by enforcing parameter constraints in the descriptor and switching parsing code to name-based lookups with gpd. Changes: - Update `GNBNEIGHBOURCELLPARAMS_DESC` in `openair2/GNB_APP/gnb_paramdef.h` to add checks for `physical id`, `absoluteFrequencySSB`, `scs`, `band` `tracking_area_code`. - Remove neighbour-cell parameter index macros. - Refactor `parse_neighbour_cells_list` in `openair2/GNB_APP/gnb_config.c` to use `gpd()` lookups for neighbour fields instead of fixed indexes. - Extend `parse_neighbour_cells_list` with an `n_cell_params` argument and update `fill_neighbour_cell_configuration` to pass `sizeofArray(ncell_params)`. --- openair2/GNB_APP/gnb_config.c | 25 +++++++++++++--------- openair2/GNB_APP/gnb_paramdef.h | 38 +++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 26 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 213584a44d..0c38993487 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2015,21 +2015,26 @@ static nr_neighbour_cell_t *get_neighbour_by_pci(seq_arr_t *neighbour_cells, int /** @brief Parse and add neighbour cells for a given cell configuration * @param[in,out] cell Neighbour cell configuration to populate * @param[in] list Parameter list describing neighbour cells - * @param[in] n_path Base path for neighbour configuration in the config tree */ -static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, const paramlist_def_t *list, const char *n_path) + * @param[in] n_path Base path for neighbour configuration in the config tree + * @param[in] n_cell_params Number of neighbour-cell parameters */ +static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, + const paramlist_def_t *list, + const char *n_path, + int n_cell_params) { seq_arr_init(&cell->neighbour_cells, sizeof(nr_neighbour_cell_t)); /* Parse each neighbour cell in the list */ for (int l = 0; l < list->numelt; ++l) { const paramdef_t *cell_params = list->paramarray[l]; - nr_neighbour_cell_t n = {.gNB_ID = *cell_params[GNB_CONFIG_N_CELL_GNB_ID_IDX].uptr, - .nrcell_id = (uint64_t)*cell_params[GNB_CONFIG_N_CELL_NR_CELLID_IDX].u64ptr, - .physicalCellId = *cell_params[GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX].uptr, - .subcarrierSpacing = *cell_params[GNB_CONFIG_N_CELL_SCS_IDX].uptr, - .band = *cell_params[GNB_CONFIG_N_CELL_BAND_IDX].uptr, - .absoluteFrequencySSB = *cell_params[GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX].i64ptr, - .tac = *cell_params[GNB_CONFIG_N_CELL_TAC_IDX].uptr}; + nr_neighbour_cell_t n = { + .gNB_ID = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_GNB_ID)->uptr, + .nrcell_id = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NRCELLID)->u64ptr, + .physicalCellId = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID)->uptr, + .subcarrierSpacing = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_SCS)->uptr, + .band = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND)->uptr, + .absoluteFrequencySSB = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB)->i64ptr, + .tac = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE)->uptr}; char p[CONFIG_MAXOPTLENGTH]; // plmn path snprintf(p, sizeof(p), "%s.%s.[%i].%s", n_path, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, l, GNB_CONFIG_STRING_NEIGHBOUR_PLMN); @@ -2091,7 +2096,7 @@ static void fill_neighbour_cell_configuration(const uint8_t gnb_idx, gNB_RRC_INS GET_PARAMS_LIST(ncell_list, ncell_params, GNBNEIGHBOURCELLPARAMS_DESC, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, path); if (ncell_list.numelt < 1) continue; - parse_neighbour_cells_list(&cell, &ncell_list, path); + parse_neighbour_cells_list(&cell, &ncell_list, path, sizeofArray(ncell_params)); seq_arr_push_back(rrc->neighbour_cell_configuration, &cell, sizeof(cell)); } diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 05714771b4..50184503ea 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -300,23 +300,29 @@ typedef enum { #define GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE "tracking_area_code" #define GNB_CONFIG_STRING_NEIGHBOUR_PLMN "plmn" -#define GNB_CONFIG_N_CELL_GNB_ID_IDX 0 -#define GNB_CONFIG_N_CELL_NR_CELLID_IDX 1 -#define GNB_CONFIG_N_CELL_PHYSICAL_ID_IDX 2 -#define GNB_CONFIG_N_CELL_ABS_FREQ_SSB_IDX 3 -#define GNB_CONFIG_N_CELL_SCS_IDX 4 -#define GNB_CONFIG_N_CELL_BAND_IDX 5 -#define GNB_CONFIG_N_CELL_TAC_IDX 6 // clang-format off -#define GNBNEIGHBOURCELLPARAMS_DESC { \ -/* optname helpstr paramflags XXXptr def val type numelt */ \ - {GNB_CONFIG_STRING_GNB_ID, "neighbour cell's gNB ID", PARAMFLAG_MANDATORY, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \ - {GNB_CONFIG_STRING_NRCELLID, "neighbour cell nrCell Id", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0}, \ - {GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID, "neighbour cell physical id", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ - {GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB, "neighbour cell abs freq ssb", PARAMFLAG_MANDATORY, .i64ptr=NULL, .defint64val=0, TYPE_INT64, 0}, \ - {GNB_CONFIG_STRING_NEIGHBOUR_CELL_SCS, "neighbour cell scs", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ - {GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND, "neighbour cell band", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=78, TYPE_UINT, 0}, \ - {GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE, "neighbour cell tracking area", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ +#define GNBNEIGHBOURCELLPARAMS_DESC { /* optname helpstr paramflags XXXptr def val type numelt */ \ + {GNB_CONFIG_STRING_GNB_ID, \ + "neighbour cell's gNB ID", PARAMFLAG_MANDATORY, .uptr=NULL, .defintval=0, TYPE_UINT, 0, \ + .chkPptr=NULL}, \ + {GNB_CONFIG_STRING_NRCELLID, \ + "neighbour cell nrCell Id", PARAMFLAG_MANDATORY, .u64ptr=NULL, .defint64val=0, TYPE_UINT64, 0, \ + .chkPptr=NULL}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_PHYSICAL_ID, \ + "neighbour cell physical id", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {0, 1007}}}}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB, \ + "neighbour cell absolute frequency SSB (0..3279165)", PARAMFLAG_MANDATORY, .i64ptr=NULL, .defint64val=0, TYPE_INT64, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 3279165}}}}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_SCS, \ + "neighbour cell scs (15/30/60/120 kHz)", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {0, 1, 2, 3}, 4}}}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND, \ + "neighbour cell band (1..1024)", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=78, TYPE_UINT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {1, 1024}}}}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE, \ + "neighbour cell tracking area (0..16777215)", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {0, 16777215}}}}, \ } // clang-format on From e27f8c24cf229769807b63b1081e113aa7b21d0c Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 5 Mar 2026 11:23:39 +0100 Subject: [PATCH 10/72] Refactor (F1AP/RRC): convert SIB containers to byte_array_t MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Unify SIB container handling by introducing a typed SIB enum and storing SIB payloads as byte_array_t across common NR types, F1AP helpers, MAC, and RRC DU setup code. Changes: - Include byte_array support in nr_common.h, add nr_sib_type_t (NR_SIB_1–NR_SIB_21), and change nr_SIBs_t to hold only nr_sib_type_t SIB_type. - Switch f1ap_sib_msg_t in f1ap_messages_types.h to use a byte_array_t SI_container instead of raw pointer and length fields. - Update F1AP encode/decode, equality, copy, and free helpers in f1ap_interface_management.c to work on SI_container.buf / SI_container.len and use eq_byte_array, copy_byte_array, and free_byte_array. - Adapt F1AP tests in f1ap_lib_test.c to build and inspect SIB containers via SI_container.buf and SI_container.len. - Replace magic SIB numbers with nr_sib_type_t values in gnb_config.c (get_sys_info and fill_du_sibs) to validate and configure DU SIBs. - Rework nr_mac_configure_other_sib in NR_MAC_gNB/config.c to decode SIB2 from a byte_array_t container, fix freeing by releasing the decoded sib2 on failure, and use NR_SIB_2 / NR_SIB_19 for CU/DU SIB selection. - Add an add_si_msg helper in rrc_gNB_du.c and refactor the SIB2 branch of rrc_gNB_process_f1_setup_req to populate cell->SI_msg from encoded local byte array while iterating SIBs with FOR_EACH_SEQ_ARR. --- common/utils/nr/nr_common.h | 29 ++++++++++++-- openair2/COMMON/f1ap_messages_types.h | 3 +- openair2/F1AP/lib/f1ap_interface_management.c | 39 +++++++------------ openair2/F1AP/tests/f1ap_lib_test.c | 18 ++++----- openair2/GNB_APP/gnb_config.c | 4 +- openair2/LAYER2/NR_MAC_gNB/config.c | 19 +++++---- openair2/RRC/NR/rrc_gNB_du.c | 24 ++++++++---- 7 files changed, 78 insertions(+), 58 deletions(-) diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h index 8580a0491b..228e51feb2 100644 --- a/common/utils/nr/nr_common.h +++ b/common/utils/nr/nr_common.h @@ -13,6 +13,7 @@ #include #include "assertions.h" #include "common/utils/utils.h" +#include "common/utils/ds/byte_array.h" #define MAX_SI_GROUPS 3 #define NR_MAX_PDSCH_TBS 3824 @@ -79,10 +80,32 @@ static inline const char *rnti_types(nr_rnti_type_t rr) // the total shift is 2 * 15, in dB scale thats 10log10(2^(15*2)) #define SQ15_SQUARED_NORM_FACTOR_DB 90.3089986992 +typedef enum { + NR_SIB_1 = 1, + NR_SIB_2, + NR_SIB_3, + NR_SIB_4, + NR_SIB_5, + NR_SIB_6, + NR_SIB_7, + NR_SIB_8, + NR_SIB_9, + NR_SIB_10, + NR_SIB_11, + NR_SIB_12, + NR_SIB_13, + NR_SIB_14, + NR_SIB_15, + NR_SIB_16, + NR_SIB_17, + NR_SIB_18, + NR_SIB_19, + NR_SIB_20, + NR_SIB_21, +} nr_sib_type_t; + typedef struct { - uint8_t *SIB_buffer; - int SIB_size; - int SIB_type; + nr_sib_type_t SIB_type; } nr_SIBs_t; typedef struct nr_bandentry_s { diff --git a/openair2/COMMON/f1ap_messages_types.h b/openair2/COMMON/f1ap_messages_types.h index 37c4a7ee83..2df2c783bf 100644 --- a/openair2/COMMON/f1ap_messages_types.h +++ b/openair2/COMMON/f1ap_messages_types.h @@ -161,8 +161,7 @@ typedef struct f1ap_du_register_req_s { typedef struct f1ap_sib_msg_s { /// RRC container with system information owned by gNB-CU - uint8_t *SI_container; - int SI_container_length; + byte_array_t SI_container; /// SIB block type, e.g. 2 for sibType2 int SI_type; } f1ap_sib_msg_t; diff --git a/openair2/F1AP/lib/f1ap_interface_management.c b/openair2/F1AP/lib/f1ap_interface_management.c index bb2015b33e..815a9f1b82 100644 --- a/openair2/F1AP/lib/f1ap_interface_management.c +++ b/openair2/F1AP/lib/f1ap_interface_management.c @@ -627,10 +627,10 @@ static void encode_cells_to_activate(const served_cells_to_activate_t *cell, F1A F1AP_GNB_CUSystemInformation_t *gNB_CUSystemInformation = &cells_to_be_activated_itemExtIEs->extensionValue.choice.GNB_CUSystemInformation; const f1ap_sib_msg_t *SI_msg = &cell->SI_msg[n]; - if (SI_msg->SI_container != NULL) { + if (SI_msg->SI_container.buf != NULL) { asn1cSequenceAdd(gNB_CUSystemInformation->sibtypetobeupdatedlist.list, F1AP_SibtypetobeupdatedListItem_t, sib_item); sib_item->sIBtype = SI_msg->SI_type; - OCTET_STRING_fromBuf(&sib_item->sIBmessage, (const char *)SI_msg->SI_container, SI_msg->SI_container_length); + OCTET_STRING_fromBuf(&sib_item->sIBmessage, (const char *)SI_msg->SI_container.buf, SI_msg->SI_container.len); } } } @@ -670,7 +670,7 @@ static bool decode_cells_to_activate(served_cells_to_activate_t *out, const F1AP F1AP_SibtypetobeupdatedListItem_t *sib_item = gNB_CUSystemInformation->sibtypetobeupdatedlist.list.array[s]; /* SI container */ f1ap_sib_msg_t *SI_msg = &out->SI_msg[s]; - SI_msg->SI_container = cp_octet_string(&sib_item->sIBmessage, &SI_msg->SI_container_length); + SI_msg->SI_container.buf = cp_octet_string(&sib_item->sIBmessage, (int *)&SI_msg->SI_container.len); /* SIB type */ SI_msg->SI_type = sib_item->sIBtype; } @@ -1163,8 +1163,8 @@ bool eq_f1ap_setup_response(const f1ap_setup_resp_t *a, const f1ap_setup_resp_t for (int j = 0; j < b->cells_to_activate[i].num_SI; j++) { const f1ap_sib_msg_t *a_SI_msg = &a->cells_to_activate[i].SI_msg[j]; const f1ap_sib_msg_t *b_SI_msg = &b->cells_to_activate[i].SI_msg[j]; - _EQ_CHECK_INT(*a_SI_msg->SI_container, *b_SI_msg->SI_container); - _EQ_CHECK_INT(a_SI_msg->SI_container_length, b_SI_msg->SI_container_length); + if (!eq_byte_array(&a_SI_msg->SI_container, &b_SI_msg->SI_container)) + return false; _EQ_CHECK_INT(a_SI_msg->SI_type, b_SI_msg->SI_type); } } @@ -1201,9 +1201,7 @@ f1ap_setup_resp_t cp_f1ap_setup_response(const f1ap_setup_resp_t *msg) for (int j = 0; j < cp_cell->num_SI; j++) { f1ap_sib_msg_t *cp_SI_msg = &cp_cell->SI_msg[j]; const f1ap_sib_msg_t *b_SI_msg = &msg_cell->SI_msg[j]; - cp_SI_msg->SI_container_length = b_SI_msg->SI_container_length; - cp_SI_msg->SI_container = malloc_or_fail(cp_SI_msg->SI_container_length); - memcpy(cp_SI_msg->SI_container, b_SI_msg->SI_container, b_SI_msg->SI_container_length); + cp_SI_msg->SI_container = copy_byte_array(b_SI_msg->SI_container); cp_SI_msg->SI_type = b_SI_msg->SI_type; } } @@ -1221,8 +1219,7 @@ void free_f1ap_setup_response(const f1ap_setup_resp_t *msg) free(msg->gNB_CU_name); for (int i = 0; i < msg->num_cells_to_activate; i++) for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++) - if (msg->cells_to_activate[i].SI_msg[j].SI_container_length > 0) - free(msg->cells_to_activate[i].SI_msg[j].SI_container); + free_byte_array(msg->cells_to_activate[i].SI_msg[j].SI_container); free(msg->cells_to_activate); } @@ -1798,7 +1795,7 @@ void free_f1ap_cu_configuration_update(const f1ap_gnb_cu_configuration_update_t { for (int i = 0; i < msg->num_cells_to_activate; i++) for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++) - free(msg->cells_to_activate[i].SI_msg[j].SI_container); + free_byte_array(msg->cells_to_activate[i].SI_msg[j].SI_container); free(msg->cells_to_activate); } @@ -1819,8 +1816,8 @@ bool eq_f1ap_cu_configuration_update(const f1ap_gnb_cu_configuration_update_t *a for (int s = 0; s < a->cells_to_activate[i].num_SI; s++) { f1ap_sib_msg_t *a_sib_msg = &a->cells_to_activate[i].SI_msg[s]; f1ap_sib_msg_t *b_sib_msg = &b->cells_to_activate[i].SI_msg[s]; - _EQ_CHECK_INT(*a_sib_msg->SI_container, *b_sib_msg->SI_container); - _EQ_CHECK_INT(a_sib_msg->SI_container_length, b_sib_msg->SI_container_length); + if (!eq_byte_array(&a_sib_msg->SI_container, &b_sib_msg->SI_container)) + return false; _EQ_CHECK_INT(a_sib_msg->SI_type, b_sib_msg->SI_type); } } @@ -1842,9 +1839,7 @@ f1ap_gnb_cu_configuration_update_t cp_f1ap_cu_configuration_update(const f1ap_gn for (int s = 0; s < cp.cells_to_activate[i].num_SI; s++) { cp.cells_to_activate[i].SI_msg[s] = msg->cells_to_activate[i].SI_msg[s]; f1ap_sib_msg_t *SI_msg = &cp.cells_to_activate[i].SI_msg[s]; - SI_msg->SI_container = calloc_or_fail(SI_msg->SI_container_length, sizeof(*SI_msg->SI_container)); - for (int j = 0; j < SI_msg->SI_container_length; j++) - SI_msg->SI_container[j] = msg->cells_to_activate[i].SI_msg[s].SI_container[j]; + SI_msg->SI_container = copy_byte_array(msg->cells_to_activate[i].SI_msg[s].SI_container); } } return cp; @@ -2153,8 +2148,8 @@ bool eq_f1ap_du_configuration_update_acknowledge(const f1ap_gnb_du_configuration return false; _EQ_CHECK_INT(a->cells_to_activate[i].num_SI, b->cells_to_activate[i].num_SI); for (int s = 0; s < a->cells_to_activate[i].num_SI; s++) { - _EQ_CHECK_INT(*a->cells_to_activate[i].SI_msg[s].SI_container, *b->cells_to_activate[i].SI_msg[s].SI_container); - _EQ_CHECK_INT(a->cells_to_activate[i].SI_msg[s].SI_container_length, b->cells_to_activate[i].SI_msg[s].SI_container_length); + if (!eq_byte_array(&a->cells_to_activate[i].SI_msg[s].SI_container, &b->cells_to_activate[i].SI_msg[s].SI_container)) + return false; _EQ_CHECK_INT(a->cells_to_activate[i].SI_msg[s].SI_type, b->cells_to_activate[i].SI_msg[s].SI_type); } } @@ -2180,9 +2175,7 @@ f1ap_gnb_du_configuration_update_acknowledge_t cp_f1ap_du_configuration_update_a f1ap_sib_msg_t *cp_sib = &cp.cells_to_activate[i].SI_msg[s]; const f1ap_sib_msg_t *msg_sib = &msg->cells_to_activate[i].SI_msg[s]; cp_sib->SI_type = msg_sib->SI_type; - cp_sib->SI_container_length = msg_sib->SI_container_length; - cp_sib->SI_container = calloc_or_fail(cp_sib->SI_container_length, sizeof(*cp_sib->SI_container)); - memcpy(cp_sib->SI_container, msg_sib->SI_container, cp_sib->SI_container_length); + cp_sib->SI_container = copy_byte_array(msg_sib->SI_container); } } return cp; @@ -2196,9 +2189,7 @@ void free_f1ap_du_configuration_update_acknowledge(const f1ap_gnb_du_configurati // SI_container for (int i = 0; i < msg->num_cells_to_activate; i++) { for (int j = 0; j < msg->cells_to_activate[i].num_SI; j++) { - if (msg->cells_to_activate[i].SI_msg[j].SI_container) { - free(msg->cells_to_activate[i].SI_msg[j].SI_container); - } + free_byte_array(msg->cells_to_activate[i].SI_msg[j].SI_container); } } free(msg->cells_to_activate); diff --git a/openair2/F1AP/tests/f1ap_lib_test.c b/openair2/F1AP/tests/f1ap_lib_test.c index 8f315ff8ae..1140e3c39b 100644 --- a/openair2/F1AP/tests/f1ap_lib_test.c +++ b/openair2/F1AP/tests/f1ap_lib_test.c @@ -313,9 +313,9 @@ static void test_f1ap_setup_response(void) int SI_container_length = strlen(s) + 1; orig.cells_to_activate[0].num_SI = 1; f1ap_sib_msg_t *SI_msg = &orig.cells_to_activate[0].SI_msg[0]; - SI_msg->SI_container = malloc_or_fail(SI_container_length); - memcpy(SI_msg->SI_container, (uint8_t *)s, SI_container_length); - SI_msg->SI_container_length = SI_container_length; + SI_msg->SI_container.buf = malloc_or_fail(SI_container_length); + memcpy(SI_msg->SI_container.buf, s, SI_container_length); + SI_msg->SI_container.len = SI_container_length; SI_msg->SI_type = 7; } F1AP_F1AP_PDU_t *f1enc = encode_f1ap_setup_response(&orig); @@ -609,10 +609,10 @@ static void test_f1ap_du_configuration_update_acknowledge(void) orig.cells_to_activate[0].plmn.mnc_digit_length = 2; orig.cells_to_activate[0].num_SI = 1; orig.cells_to_activate[0].SI_msg[0].SI_type = 7; - orig.cells_to_activate[0].SI_msg[0].SI_container_length = 10; - orig.cells_to_activate[0].SI_msg[0].SI_container = malloc(sizeof(uint8_t) * 10); - for (int i = 0; i < orig.cells_to_activate[0].SI_msg[0].SI_container_length; i++) { - orig.cells_to_activate[0].SI_msg[0].SI_container[i] = i; + orig.cells_to_activate[0].SI_msg[0].SI_container.len = 10; + orig.cells_to_activate[0].SI_msg[0].SI_container.buf = malloc(sizeof(uint8_t) * 10); + for (int i = 0; i < (int)orig.cells_to_activate[0].SI_msg[0].SI_container.len; i++) { + orig.cells_to_activate[0].SI_msg[0].SI_container.buf[i] = i; } // ASN.1 enc/dec F1AP_F1AP_PDU_t *f1enc = encode_f1ap_du_configuration_update_acknowledge(&orig); @@ -655,8 +655,8 @@ static void test_f1ap_cu_configuration_update(void) orig.cells_to_activate[0].plmn.mnc_digit_length = 2; orig.cells_to_activate[0].num_SI = 1; orig.cells_to_activate[0].SI_msg[0].SI_type = 7; - orig.cells_to_activate[0].SI_msg[0].SI_container_length = 10; - orig.cells_to_activate[0].SI_msg[0].SI_container = malloc(sizeof(uint8_t) * 10); + orig.cells_to_activate[0].SI_msg[0].SI_container.len = 10; + orig.cells_to_activate[0].SI_msg[0].SI_container.buf = malloc(sizeof(uint8_t) * 10); F1AP_F1AP_PDU_t *f1enc = encode_f1ap_cu_configuration_update(&orig); F1AP_F1AP_PDU_t *f1dec = f1ap_encode_decode(f1enc); f1ap_msg_free(f1enc); diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 0c38993487..edbaae1735 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1083,7 +1083,7 @@ f1ap_gnb_du_system_info_t *get_sys_info(NR_BCCH_BCH_Message_t *mib, const NR_BCC nr_SIBs_t *si = (nr_SIBs_t *)seq_arr_at(du_SIBs, i); // other SIB in gNB-DU System Information not implemented yet // only DU SIB not included in this message is SIB19 - AssertFatal(si->SIB_type == 19, "Cannot handle SIB%d in gNB-DU System Information\n", si->SIB_type); + AssertFatal(si->SIB_type == NR_SIB_19, "Cannot handle SIB%d in gNB-DU System Information\n", (int)si->SIB_type); } } return sys_info; @@ -1243,7 +1243,7 @@ static seq_arr_t *fill_du_sibs(paramdef_t *GNBparamarray) int sib_value = GNBparamarray[GNB_DU_SIBS_IDX].iptr[i]; // SIB10, SIB12, SIB13, SIB14, SIB15, SIB17, SIB18, SIB19 and SIB20 are generated by the DU // all the rest in the DU - AssertFatal((sib_value > 9 && sib_value < 21) && sib_value != 11 && sib_value != 16, + AssertFatal((sib_value > NR_SIB_9 && sib_value < NR_SIB_21) && sib_value != NR_SIB_11 && sib_value != NR_SIB_16, "SIB%d is not a DU SIB, to be configured in DU section\n", sib_value); nr_SIBs_t *du_sib = calloc_or_fail(1, sizeof(nr_SIBs_t)); diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 202590f9fc..18d5bbb4a8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -988,22 +988,21 @@ bool nr_mac_configure_other_sib(gNB_MAC_INST *nrmac, int num_cu_sib, const f1ap_ NR_SystemInformation_IEs_t *sysInfov17 = calloc(1, sizeof(*sysInfov17)); // for othersibs for (int i = 0; i < num_cu_sib; i++) { config_sibs[i] = cu_sib[i].SI_type; + const byte_array_t *container = &cu_sib[i].SI_container; + if (!container->buf || container->len == 0) { + LOG_W(NR_MAC, "SIB%d container is invalid (ptr=%p len=%zu), skipping\n", config_sibs[i], container->buf, container->len); + continue; + } switch (config_sibs[i]) { - case 2: { + case NR_SIB_2: { struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc(1, sizeof(*type)); type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib2; // SIB2 coming from CU need to decode it NR_SIB2_t *sib2 = NULL; - asn_dec_rval_t dec_rval = uper_decode(NULL, - &asn_DEF_NR_SIB2, - (void **)&sib2, - cu_sib[i].SI_container, - cu_sib[i].SI_container_length, - 0, - 0); + asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_SIB2, (void **)&sib2, container->buf, container->len, 0, 0); if (dec_rval.code != RC_OK) { LOG_E(NR_MAC, "cannot decode SIB%d from CU\n", config_sibs[i]); - ASN_STRUCT_FREE(asn_DEF_NR_SIB2, cu_sib[i].SI_container); + ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2); } type->choice.sib2 = sib2; add_sib_to_systeminformation(sysInfo, type); @@ -1019,7 +1018,7 @@ bool nr_mac_configure_other_sib(gNB_MAC_INST *nrmac, int num_cu_sib, const f1ap_ int sib_idx = i + num_cu_sib; config_sibs[sib_idx] = si->SIB_type; switch (config_sibs[sib_idx]) { - case 19: { + case NR_SIB_19: { struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type_du = calloc(1, sizeof(*type_du)); type_du->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib19_v1700; NR_SIB19_r17_t *sib19 = get_SIB19_NR(cc->ServingCellConfigCommon); diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 71da3f3d1b..495ad92c82 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -297,6 +297,17 @@ static void cp_f1_served_cell_info_to_cell(nr_rrc_cell_container_t *dst, const f } } +static void add_si_msg(served_cells_to_activate_t *cell, int sib_type, const byte_array_t *enc) +{ + AssertFatal(cell->num_SI < F1AP_MAX_NO_SIB_TYPES, + "Too many SI messages (%u), max supported is %u\n", + cell->num_SI, + F1AP_MAX_NO_SIB_TYPES); + cell->SI_msg[cell->num_SI].SI_container = copy_byte_array(*enc); + cell->SI_msg[cell->num_SI].SI_type = sib_type; + cell->num_SI++; +} + void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) { AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n"); @@ -425,16 +436,13 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) // Encode CU SIBs and configure setup response with sysinfo seq_arr_t *sibs = rrc->SIBs; if (sibs) { - for (int i = 0; i < sibs->size; i++) { - nr_SIBs_t *sib = (nr_SIBs_t *)seq_arr_at(sibs, i); + FOR_EACH_SEQ_ARR (nr_SIBs_t *, sib, sibs) { switch (sib->SIB_type) { - case 2: { + case NR_SIB_2: { NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(new->mtc); - sib->SIB_size = do_SIB2_NR(&sib->SIB_buffer, ssbmtc); - cell.SI_msg[cell.num_SI].SI_container = sib->SIB_buffer; - cell.SI_msg[cell.num_SI].SI_container_length = sib->SIB_size; - cell.SI_msg[cell.num_SI].SI_type = sib->SIB_type; - cell.num_SI++; + byte_array_t ba = {0}; + ba.len = do_SIB2_NR(&ba.buf, ssbmtc); + add_si_msg(&cell, sib->SIB_type, &ba); } break; default: AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type); From 52d5705f473c2ea8d2a4cf1cb5bc5d339bb92a72 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 11 Feb 2026 15:41:10 +0100 Subject: [PATCH 11/72] Neighbour config: log neighbour presence and counts during parsing Add info logs for neighbour cell configuration parsing without changing functional behaviour. --- openair2/GNB_APP/gnb_config.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index edbaae1735..6c5d9f4781 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2083,6 +2083,7 @@ static void fill_neighbour_cell_configuration(const uint8_t gnb_idx, gNB_RRC_INS snprintf(path, sizeof(path), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx); GET_PARAMS_LIST(nlist, nparams, GNB_NEIGHBOUR_LIST_PARAM_LIST, GNB_CONFIG_STRING_NEIGHBOUR_LIST, path); + LOG_I(GNB_APP, "gNB %d: neighbour_list has %d serving cell(s)\n", gnb_idx, nlist.numelt); if (nlist.numelt < 1) return; @@ -2094,6 +2095,7 @@ static void fill_neighbour_cell_configuration(const uint8_t gnb_idx, gNB_RRC_INS cell.nr_cell_id = *gpd(nlist.paramarray[elm], sizeofArray(nparams), GNB_CONFIG_STRING_NRCELLID)->u64ptr; snprintf(path, sizeof(path), "%s.[%i].%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_NEIGHBOUR_LIST, elm); GET_PARAMS_LIST(ncell_list, ncell_params, GNBNEIGHBOURCELLPARAMS_DESC, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, path); + LOG_I(GNB_APP, "Cell %ld has %d neighbours configured\n", cell.nr_cell_id, ncell_list.numelt); if (ncell_list.numelt < 1) continue; parse_neighbour_cells_list(&cell, &ncell_list, path, sizeofArray(ncell_params)); From 9afa94040dc98acaa2d8b376d4b1a71f902c36d8 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 11 Feb 2026 10:33:56 +0100 Subject: [PATCH 12/72] RRC/SIB2: load cell reselection config from gNB Introduce a typed SIB2 cell reselection configuration, load it from the gNB configuration into the RRC gNB instance, and use it to build and encode SIB2 instead of relying on a fixed SIB2 definition. Validate SIB2 parameters against explicit bounds and make the SIB2 encoder consume a pre-built ASN.1 structure with safer error handling. Changes: - Add SIB2 mobility timer and q-HystSF enums plus SIB2 config structs (`sib2_speed_state_reselection_pars_t`, `sib2_config_t`) and a `sib2_config` field on `gNB_RRC_INST` in `nr_rrc_defs.h`. - Define SIB2 configuration option names, defaults (`GNB_CONFIG_STRING_SIB2_*`, `GNBSIB2PARAMS_DESC`) in `gnb_paramdef.h` for q_Hyst, thresholds, mobility timers, scaling factors and deriveSSB_IndexFromCell. - Add `sib2_config` parameter keys, defaults, and range/value checks in `gnb_paramdef.h` (`GNBSIB2PARAMS_DESC`). - Add SIB2 `fill_sib2_configuration()` in `gnb_config.c` to read SIB2 parameters from the config file log the resulting configuration and store it in `rrc->sib2_config` via `RCconfig_NRRRC()`. - Add `get_q_hyst_asn1()` and `get_sib2_from_cfg()` in `rrc_gNB_du.c`, and update `rrc_gNB_process_f1_setup_req()` to encode/add SIB2 from config with explicit encode-failure handling and logging. - Change `do_SIB2_NR` signature in `asn1_msg.h/.c` to `byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2)`, remove internal default SIB2 construction, and add ASN.1 constraint/encode error logging. --- openair2/GNB_APP/gnb_config.c | 61 +++++++++++++ openair2/GNB_APP/gnb_paramdef.h | 90 +++++++++++++++++++ openair2/RRC/NR/MESSAGES/asn1_msg.c | 48 ++++------ openair2/RRC/NR/MESSAGES/asn1_msg.h | 3 +- openair2/RRC/NR/nr_rrc_defs.h | 97 ++++++++++++++++++++ openair2/RRC/NR/rrc_gNB_du.c | 131 +++++++++++++++++++++++++++- 6 files changed, 395 insertions(+), 35 deletions(-) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 6c5d9f4781..5980666cff 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2171,6 +2171,64 @@ static void fill_measurement_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc) } } +/** @brief Parse and populate SIB2 configuration from config file + * @param[in] gnb_idx gNB index + * @return populated SIB2 configuration */ +static sib2_config_t fill_sib2_configuration(uint8_t gnb_idx) +{ + sib2_config_t sib2 = {0}; + + char sib2_path[MAX_OPTNAME_SIZE * 2 + 8]; + snprintf(sib2_path, sizeof(sib2_path), "%s.[%i].%s", GNB_CONFIG_STRING_GNB_LIST, gnb_idx, GNB_CONFIG_STRING_SIB2_CONFIG); + + GET_PARAMS(SIB2Params, GNBSIB2PARAMS_DESC, sib2_path); + const int n_sib2_params = sizeofArray(SIB2Params); + + cell_reselection_info_common_t *common = &sib2.cell_reselection_info_common; + common->q_Hyst = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_HYST)->iptr; + /* speedStateReselectionPars */ + common->speedStateReselectionPars = calloc_or_fail(1, sizeof(*common->speedStateReselectionPars)); + sib2_speed_state_reselection_pars_t *speed = common->speedStateReselectionPars; + + /* Cell reselection serving frequency information */ + cell_reselection_serving_freq_info_t *serv = &sib2.cell_reselection_serving_freq_info; + serv->cellReselectionPriority = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY)->iptr; + serv->threshServingLowP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P)->iptr; + serv->threshServingLowQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q)->iptr; + serv->s_NonIntraSearchP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P)->iptr; + serv->s_NonIntraSearchQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q)->iptr; + + /* Intra-frequency cell reselection parameters */ + intra_freq_cell_reselection_info_t *intra = &sib2.intra_freq_cell_reselection_info; + intra->q_RxLevMin = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN)->iptr; + intra->q_QualMin = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_Q_QUALMIN)->iptr; + intra->s_IntraSearchP = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P)->iptr; + intra->s_IntraSearchQ = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q)->iptr; + intra->t_ReselectionNR = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_T_RESEL_NR)->iptr; + + /* deriveSSB_IndexFromCell (0/1) */ + int derive_ssb = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX)->iptr; + sib2.deriveSSB_IndexFromCell = (derive_ssb != 0); + + /* Speed-state pars: optional parameters are omitted from the config file (default applied), not set to -1. */ + speed->t_Evaluation = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL)->iptr; + speed->t_HystNormal = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL)->iptr; + speed->n_CellChangeMedium = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED)->iptr; + speed->n_CellChangeHigh = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH)->iptr; + speed->sf_Medium = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM)->iptr; + speed->sf_High = *gpd(SIB2Params, n_sib2_params, GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH)->iptr; + + LOG_I(GNB_APP, + "gNB %d SIB2 config: q_Hyst=%d dB, cellReselPrio=%d, q_RxLevMin=%d dBm, t_ReselectionNR=%d s\n", + gnb_idx, + common->q_Hyst, + serv->cellReselectionPriority, + intra->q_RxLevMin, + intra->t_ReselectionNR); + + return sib2; +} + /** * @brief Allocates and initializes RRC instances * Currently assuming 1 instance @@ -2270,6 +2328,9 @@ gNB_RRC_INST *RCconfig_NRRRC() }// }//End for (k=0; k sib2_config = fill_sib2_configuration(i); }//End if (num_gnbs>0) if (!NODE_IS_DU(rrc->node_type)) diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 50184503ea..98e53d83e2 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -10,6 +10,7 @@ #define __GNB_APP_GNB_PARAMDEF__H__ #include "common/config/config_paramdesc.h" +#include "common/config/config_userapi.h" #include "common/ngran_types.h" #include "RRC_nr_paramsvalues.h" @@ -374,6 +375,95 @@ typedef enum { #define MEASUREMENT_EVENTS_INCLUDE_BEAM_MEAS_IDX 1 #define MEASUREMENT_EVENTS_MAX_RS_INDEX_TO_REPORT 2 +/*-------------------------------------------------------------------------------------------------------------------------------------*/ +/* SIB2 cell reselection configuration parameters */ +/*-------------------------------------------------------------------------------------------------------------------------------------*/ + +#define GNB_CONFIG_STRING_SIB2_CONFIG "sib2_config" + +/* clang-format off */ + +/* SIB2 top-level parameters (per cell) */ +#define GNB_CONFIG_STRING_SIB2_Q_HYST "q_Hyst" +#define GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY "cellReselectionPriority" +#define GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P "threshServingLowP" +#define GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q "threshServingLowQ" +#define GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P "s_NonIntraSearchP" +#define GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q "s_NonIntraSearchQ" +#define GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN "q_RxLevMin" +#define GNB_CONFIG_STRING_SIB2_Q_QUALMIN "q_QualMin" +#define GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P "s_IntraSearchP" +#define GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q "s_IntraSearchQ" +#define GNB_CONFIG_STRING_SIB2_T_RESEL_NR "t_ReselectionNR" +#define GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX "deriveSSB_IndexFromCell" + +/* SIB2 speedStateReselectionPars parameters */ +#define GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL "speed_t_Evaluation" +#define GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL "speed_t_HystNormal" +#define GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED "speed_n_CellChangeMedium" +#define GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH "speed_n_CellChangeHigh" +#define GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM "speed_sf_Medium" +#define GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH "speed_sf_High" + +#define GNBSIB2PARAMS_DESC { /* optname helpstr paramflags XXXptr def val type numelt */ \ + {GNB_CONFIG_STRING_SIB2_Q_HYST, \ + "SIB2 q-Hyst (0,1,2,3,4,5,6,8,10..24 dB)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}, 16}}}, \ + {GNB_CONFIG_STRING_SIB2_CELLRESEL_PRIORITY, \ + "SIB2 cellReselectionPriority (0..7)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 7}}}}, \ + {GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_P, \ + "SIB2 threshServingLowP (0..31)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_THRESH_SERVING_LOW_Q, \ + "SIB2 threshServingLowQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_P, \ + "SIB2 s-NonIntraSearchP (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_S_NONINTRASEARCH_Q, \ + "SIB2 s-NonIntraSearchQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_Q_RXLEVMIN, \ + "SIB2 q-RxLevMin (-70..-22)", 0, .iptr=NULL, .defintval=-56, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-70, -22}}}}, \ + {GNB_CONFIG_STRING_SIB2_Q_QUALMIN, \ + "SIB2 q-QualMin (-43..-12 or -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, \ + {-1, -43, -42, -41, -40, -39, -38, -37, -36, -35, -34, -33, -32, -31, -30, -29, -28, \ + -27, -26, -25, -24, -23, -22, -21, -20, -19, -18, -17, -16, -15, -14, -13, -12}, 33}}}, \ + {GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_P, \ + "SIB2 s-IntraSearchP (0..31)", 0, .iptr=NULL, .defintval=22, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_S_INTRASEARCH_Q, \ + "SIB2 s-IntraSearchQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_SIB2_T_RESEL_NR, \ + "SIB2 t-ReselectionNR (0..7)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 7}}}}, \ + {GNB_CONFIG_STRING_SIB2_DERIVE_SSB_IDX, \ + "SIB2 deriveSSB-IndexFromCell (0/1)", PARAMFLAG_BOOL, .iptr=NULL, .defintval=1, TYPE_INT, 0}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_T_EVAL, \ + "SIB2 speed t-Evaluation (0=s30,1=s60,2=s120,3=s180,4=s240)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 4}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_T_HYST_NORMAL, \ + "SIB2 speed t-HystNormal (0=s30,1=s60,2=s120,3=s180,4=s240)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 4}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_MED, \ + "SIB2 speed n-CellChangeMedium (1..16)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {1, 16}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_N_CELL_CHG_HIGH, \ + "SIB2 speed n-CellChangeHigh (1..16)", 0, .iptr=NULL, .defintval=2, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {1, 16}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_SF_MEDIUM, \ + "SIB2 speed sf-Medium (0=dB-6,1=dB-4,2=dB-2,3=dB0)", 0, .iptr=NULL, .defintval=1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 3}}}}, \ + {GNB_CONFIG_STRING_SIB2_SPEED_SF_HIGH, \ + "SIB2 speed sf-High (0=dB-6,1=dB-4,2=dB-2,3=dB0)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 3}}}}, \ +} +/* clang-format on */ + /* PLMN ID configuration */ #define GNB_CONFIG_STRING_PLMN_LIST "plmn_list" diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 8d5d00c975..b8cd1ab284 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -223,39 +223,25 @@ NR_UE_NR_Capability_t *decode_nr_ue_capability(int rnti, const NR_UE_CapabilityR return NULL; } -//------------------------------------------------------------------------------ - -int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc) +byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2) { - NR_SIB2_t *sib2 = calloc(1, sizeof(*sib2)); - sib2->cellReselectionInfoCommon.q_Hyst = NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB0; - struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars *speed = calloc(1, sizeof(*speed)); - NR_MobilityStateParameters_t mobilityStateParameters = {0}; - mobilityStateParameters.t_Evaluation = NR_MobilityStateParameters__t_Evaluation_s30; - mobilityStateParameters.t_HystNormal = NR_MobilityStateParameters__t_HystNormal_s30; - mobilityStateParameters.n_CellChangeMedium = 1; - mobilityStateParameters.n_CellChangeHigh = 2; - speed->mobilityStateParameters = mobilityStateParameters; - struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF qhyst = {0}; - qhyst.sf_Medium = NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF__sf_Medium_dB_4; - qhyst.sf_High = NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF__sf_High_dB_6; - speed->q_HystSF = qhyst; - sib2->cellReselectionInfoCommon.speedStateReselectionPars = speed; - sib2->cellReselectionServingFreqInfo.cellReselectionPriority = 0; // INTEGER (0..7) - sib2->cellReselectionServingFreqInfo.threshServingLowP = 0; - NR_ReselectionThresholdQ_t *threshServingLowQ = calloc(1, sizeof(*threshServingLowQ)); - *threshServingLowQ = 0; - sib2->cellReselectionServingFreqInfo.threshServingLowQ = threshServingLowQ; - sib2->intraFreqCellReselectionInfo.q_RxLevMin = -56; // INTEGER (-70..-22) - sib2->intraFreqCellReselectionInfo.s_IntraSearchP = 22; // INTEGER (0..31) - sib2->intraFreqCellReselectionInfo.t_ReselectionNR = 1; // INTEGER (0..7) - sib2->intraFreqCellReselectionInfo.deriveSSB_IndexFromCell = true; - sib2->intraFreqCellReselectionInfo.smtc = ssbmtc; + byte_array_t msg = {.buf = NULL, .len = 0}; + char errbuf[256] = {0}; + size_t errlen = sizeof(errbuf); + int ret = asn_check_constraints(&asn_DEF_NR_SIB2, sib2, errbuf, &errlen); + if (ret != 0) { + LOG_E(NR_RRC, "SIB2 constraint check failed: %s\n", errbuf); + return msg; + } - ssize_t size = uper_encode_to_new_buffer(&asn_DEF_NR_SIB2, NULL, (void *)sib2, (void **)msg_SIB2); - AssertFatal (size > 0, "ASN1 message encoding failed (encoded %lu bytes)!\n", size); - ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2); - return size; + int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB2, NULL, (void *)sib2, (void **)&msg.buf); + if (val <= 0) { + LOG_E(NR_RRC, "Failed to encode SIB2\n"); + return msg; + } + + msg.len = val; + return msg; } int do_RRCReject(uint8_t *const buffer) diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 7aeb2b4ed0..43f4e7dcef 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -33,6 +33,7 @@ #include "NR_MeasurementTimingConfiguration.h" #include "NR_UE-NR-Capability.h" #include "NR_UE-CapabilityRAT-ContainerList.h" +#include "NR_SIB2.h" #include "ds/seq_arr.h" #include "ds/byte_array.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h" @@ -65,7 +66,7 @@ typedef struct { */ int xer_sprint_NR(char *string, size_t string_size, struct asn_TYPE_descriptor_s *td, void *sptr); -int do_SIB2_NR(uint8_t **msg_SIB2, NR_SSB_MTC_t *ssbmtc); +byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2); int do_RRCReject(uint8_t *const buffer); diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 3b07ec6a6c..68104ba203 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -418,6 +418,101 @@ typedef struct nr_rrc_cuup_container_t { sctp_assoc_t assoc_id; } nr_rrc_cuup_container_t; +/**Timers for SIB2 MobilityStateParameters (TS 38.331) + * Value set shared by T-Evaluation and T-HystNormal: (s30, s60, s120, s180, s240). */ +typedef enum sib2_mobility_state_timer_e { + SIB2_MOBILITY_STATE_TIMER_S30, + SIB2_MOBILITY_STATE_TIMER_S60, + SIB2_MOBILITY_STATE_TIMER_S120, + SIB2_MOBILITY_STATE_TIMER_S180, + SIB2_MOBILITY_STATE_TIMER_S240, +} sib2_mobility_state_timer_t; + +/** Q-HystSF scaling factors for Q_hyst (TS 38.331) + * Speed-dependent offsets (-6, -4, -2, 0 dB) applied to the base Q_hyst + * when the UE is in medium or high mobility state. */ +typedef enum sib2_q_hystsf_e { + SIB2_Q_HYSTSF_DB_6, + SIB2_Q_HYSTSF_DB_4, + SIB2_Q_HYSTSF_DB_2, + SIB2_Q_HYSTSF_DB0, +} sib2_q_hystsf_t; + +/** SIB2 speedStateReselectionPars configuration + * Mirrors TS 38.331 MobilityStateParameters and Q-hystSF. */ +typedef struct sib2_speed_state_reselection_pars_s { + /* t-Evaluation: duration for evaluating allowed reselections + * to enter mobility states (s30, s60, s120, s180, s240) */ + sib2_mobility_state_timer_t t_Evaluation; + /* t-HystNormal: additional time period to evaluate reselection criteria + * before returning to normal mobility (s30, s60, s120, s180, s240) */ + sib2_mobility_state_timer_t t_HystNormal; + /* n-CellChangeMedium: max number of cell reselections to enter medium mobility state (1..16) */ + int n_CellChangeMedium; + /* n-CellChangeHigh: max number of cell reselections to enter high mobility state (1..16) */ + int n_CellChangeHigh; + /* sf-Medium: speed-dependent scaling factor for Qhyst in medium state (-6, -4, -2, 0 dB) */ + sib2_q_hystsf_t sf_Medium; + /* sf-High: speed-dependent scaling factor for Qhyst in high state (-6, -4, -2, 0 dB) */ + sib2_q_hystsf_t sf_High; +} sib2_speed_state_reselection_pars_t; + +/** SIB2 cellReselectionServingFreqInfo (TS 38.331) */ +typedef struct { + /* s-NonIntraSearchP: Srxlev threshold to trigger NR inter-freq / inter-RAT measurements (0..31)*2 dB. + * Set to -1 to omit (optional ASN.1 field). */ + int s_NonIntraSearchP; + /* s-NonIntraSearchQ: Squal threshold to trigger NR inter-freq / inter-RAT measurements (0..31)*2 dB */ + int s_NonIntraSearchQ; + /* threshServingLowP: Srxlev threshold (dB) used by the UE on the serving cell + * when reselecting towards a lower-priority RAT/frequency (0..31)*2 dB */ + int threshServingLowP; + /* threshServingLowQ: Squal threshold (dB) used by the UE on the serving cell + * when reselecting towards a lower-priority RAT/frequency (0..31)*2 dB. + * Set to -1 to omit (optional ASN.1 field). */ + int threshServingLowQ; + /* cellReselectionPriority: absolute priority of serving NR frequency (0..7) */ + int cellReselectionPriority; +} cell_reselection_serving_freq_info_t; + +/** SIB2 cellReselectionInfoCommon (TS 38.331) */ +typedef struct { + /* q-Hyst: hysteresis added to serving-cell ranking R_s (0..24 dB) */ + int q_Hyst; + /* speedStateReselectionPars: MobilityStateParameters + q-HystSF */ + sib2_speed_state_reselection_pars_t *speedStateReselectionPars; +} cell_reselection_info_common_t; + +/** SIB2 intraFreqCellReselectionInfo (TS 38.331) */ +typedef struct { + /* q-RxLevMin: minimum required RX level in the cell (dBm) */ + int q_RxLevMin; + /* q-QualMin: minimum required quality level in the cell (dB); */ + int q_QualMin; + /* s-IntraSearchP: Srxlev threshold for intra-freq meas (0..31) */ + int s_IntraSearchP; + /* s-IntraSearchQ: Squal threshold for intra-freq meas (0..31) */ + int s_IntraSearchQ; + /* t-ReselectionNR: NR cell reselection timer (0..7) */ + int t_ReselectionNR; +} intra_freq_cell_reselection_info_t; + +/** SIB2 configuration for idle/inactive cell reselection + * (maps to SIB2 reselection parameters in TS 38.304/38.331, per cell). + * @note Squal = Cell selection quality value (dB) + * Srxlev = Cell selection RX level value (dB) */ +typedef struct sib2_config_s { + // cellReselectionInfoCommon + cell_reselection_info_common_t cell_reselection_info_common; + // cellReselectionServingFreqInfo + cell_reselection_serving_freq_info_t cell_reselection_serving_freq_info; + // intraFreqCellReselectionInfo + intra_freq_cell_reselection_info_t intra_freq_cell_reselection_info; + /* deriveSSB_IndexFromCell: SIB2 deriveSSB-IndexFromCell - whether UE may + * assume SFN/SSB alignment across cells on serving freq (per TS 38.304/38.133) */ + bool deriveSSB_IndexFromCell; +} sib2_config_t; + //---NR---(completely change)--------------------- typedef struct gNB_RRC_INST_s { @@ -432,6 +527,8 @@ typedef struct gNB_RRC_INST_s { // RRC configuration nr_rrc_config_t configuration; seq_arr_t *SIBs; + // SIB2 configuration for cell reselection parameters + sib2_config_t sib2_config; // gNB N3 GTPU instance instance_t e1_inst; diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 495ad92c82..d5d0f27be9 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -23,6 +23,8 @@ #include "f1ap_messages_types.h" #include "ngap_messages_types.h" #include "nr_rrc_defs.h" +#include "NR_SIB2.h" +#include "openair2/RRC/NR/MESSAGES/asn1_msg.h" #include "rrc_gNB_mobility.h" #include "openair2/F1AP/f1ap_common.h" #include "openair2/F1AP/f1ap_ids.h" @@ -55,6 +57,121 @@ static NR_SSB_MTC_t *get_ssb_mtc(const NR_MeasurementTimingConfiguration_t *mtc) return ssb_mtc; } +/** @brief Map config q_Hyst (dB: 0,1,2,3,4,5,6,8,10..24) to ASN.1 q-Hyst enum index (0..15). + * ASN.1 NR_SIB2 cellReselectionInfoCommon.q-Hyst is encoded with 16 values. */ +static long get_q_hyst_asn1(int q_hyst_db) +{ + switch (q_hyst_db) { + case 0: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB0; + case 1: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB1; + case 2: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB2; + case 3: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB3; + case 4: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB4; + case 5: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB5; + case 6: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB6; + case 8: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB8; + case 10: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB10; + case 12: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB12; + case 14: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB14; + case 16: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB16; + case 18: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB18; + case 20: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB20; + case 22: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB22; + case 24: + return NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB24; + default: + AssertFatal(false, "unexpected q-Hyst value %d (configuration should have been validated)\n", q_hyst_db); + } +} + +/** @brief Build NR_SIB2 from validated SIB2 configuration. + * @param cfg Validated SIB2 configuration from config file + * @param ssbmtc SSB-MTC instance to attach to intraFreqCellReselectionInfo.smtc. */ +static NR_SIB2_t *get_sib2_from_cfg(const sib2_config_t *cfg, NR_SSB_MTC_t *ssbmtc) +{ + NR_SIB2_t *sib2 = calloc_or_fail(1, sizeof(*sib2)); + typeof(sib2->cellReselectionInfoCommon) *common = &sib2->cellReselectionInfoCommon; + const cell_reselection_info_common_t *cfg_common = &cfg->cell_reselection_info_common; + + /* q-Hyst: ASN.1 enum (0..15) converted from config dB (0,1,2,3,4,5,6,8,10..24). */ + common->q_Hyst = get_q_hyst_asn1(cfg_common->q_Hyst); + + /* speedStateReselectionPars */ + if (cfg_common->speedStateReselectionPars != NULL) { + struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars *speed = calloc_or_fail(1, sizeof(*speed)); + NR_MobilityStateParameters_t mobilityStateParameters = {0}; + const sib2_speed_state_reselection_pars_t *pars = cfg_common->speedStateReselectionPars; + /* t-Evaluation / t-HystNormal: (0..7) */ + mobilityStateParameters.t_Evaluation = pars->t_Evaluation; + mobilityStateParameters.t_HystNormal = pars->t_HystNormal; + /* n-CellChangeMedium / n-CellChangeHigh: (1..16) */ + mobilityStateParameters.n_CellChangeMedium = pars->n_CellChangeMedium; + mobilityStateParameters.n_CellChangeHigh = pars->n_CellChangeHigh; + speed->mobilityStateParameters = mobilityStateParameters; + + /* q-HystSF: sf-Medium/sf-High (0..3) */ + struct NR_SIB2__cellReselectionInfoCommon__speedStateReselectionPars__q_HystSF qhyst = {0}; + qhyst.sf_Medium = pars->sf_Medium; + qhyst.sf_High = pars->sf_High; + speed->q_HystSF = qhyst; + + common->speedStateReselectionPars = speed; + } + + /* cellReselectionServingFreqInfo */ + typeof(sib2->cellReselectionServingFreqInfo) *serv = &sib2->cellReselectionServingFreqInfo; + const cell_reselection_serving_freq_info_t *cfg_serv = &cfg->cell_reselection_serving_freq_info; + /* cellReselectionPriority: CellReselectionPriority (0..7) */ + serv->cellReselectionPriority = cfg_serv->cellReselectionPriority; + /* threshServingLowP: ReselectionThreshold (0..31) */ + serv->threshServingLowP = cfg_serv->threshServingLowP; + /* threshServingLowQ: ReselectionThresholdQ (0..31) */ + if (cfg_serv->threshServingLowQ != -1) + asn1cCallocOne(serv->threshServingLowQ, cfg_serv->threshServingLowQ); + /* s-NonIntraSearchP/Q: ReselectionThreshold/ReselectionThresholdQ (0..31) */ + if (cfg_serv->s_NonIntraSearchP != -1) + asn1cCallocOne(serv->s_NonIntraSearchP, cfg_serv->s_NonIntraSearchP); + if (cfg_serv->s_NonIntraSearchQ != -1) + asn1cCallocOne(serv->s_NonIntraSearchQ, cfg_serv->s_NonIntraSearchQ); + + /* intraFreqCellReselectionInfo */ + typeof(sib2->intraFreqCellReselectionInfo) *intra = &sib2->intraFreqCellReselectionInfo; + const intra_freq_cell_reselection_info_t *cfg_intra = &cfg->intra_freq_cell_reselection_info; + /* q-RxLevMin: Q-RxLevMin (-70..-22) */ + intra->q_RxLevMin = cfg_intra->q_RxLevMin; + /* q-QualMin: Q-QualMin (-43..-12) */ + if (cfg_intra->q_QualMin != -1) + asn1cCallocOne(intra->q_QualMin, cfg_intra->q_QualMin); + /* s-IntraSearchP: ReselectionThreshold (0..31) */ + intra->s_IntraSearchP = cfg_intra->s_IntraSearchP; + /* s-IntraSearchQ: ReselectionThresholdQ (0..31) */ + if (cfg_intra->s_IntraSearchQ != -1) + asn1cCallocOne(intra->s_IntraSearchQ, cfg_intra->s_IntraSearchQ); + /* t-ReselectionNR: (0..7) */ + intra->t_ReselectionNR = cfg_intra->t_ReselectionNR; + /* deriveSSB-IndexFromCell (bool) */ + intra->deriveSSB_IndexFromCell = cfg->deriveSSB_IndexFromCell; + /* smtc */ + intra->smtc = ssbmtc; + + return sib2; +} + /** @brief Return the frequency of the SS block of the cell for which this message is included, * or of other SS blocks within the same carrier. MeasurementTimingConfiguration message is * used to convey assistance information for measurement timing between nodes (e.g F1 setup) */ @@ -440,9 +557,17 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) switch (sib->SIB_type) { case NR_SIB_2: { NR_SSB_MTC_t *ssbmtc = get_ssb_mtc(new->mtc); - byte_array_t ba = {0}; - ba.len = do_SIB2_NR(&ba.buf, ssbmtc); - add_si_msg(&cell, sib->SIB_type, &ba); + NR_SIB2_t *sib2 = get_sib2_from_cfg(&rrc->sib2_config, ssbmtc); + byte_array_t enc = do_SIB2_NR(sib2); + ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2); + if (!enc.buf || enc.len == 0) { + free_byte_array(enc); + LOG_E(NR_RRC, "SIB2 encoding failed\n"); + break; + } + add_si_msg(&cell, sib->SIB_type, &enc); + free_byte_array(enc); + LOG_I(NR_RRC, "DU %ld: added SIB2 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id); } break; default: AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type); From c53b28f51dcd30cf8e501c84e7df2d225cfdfc35 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 3 Mar 2026 14:10:24 +0100 Subject: [PATCH 13/72] RRC: add SIB3/SIB4 neighbour offsets and inter-frequency SIB4 config Introduce typed SIB3/SIB4 neighbour and inter-frequency carrier configuration structures, wire them into the RRC neighbour and gNB instances, and centralise parsing and bounds checking of per-frequency SIB4 parameters from the gNB frequency list. Changes: - Define SIB3/SIB4 reselection and offset bound macros in gnb_config.c and apply them when parsing SIB4 frequency_config and per-carrier q_RxLevMin/t_ReselectionNR. - Add add_inter_freq and parse_inter_freq_list helpers in gnb_config.c to populate a shared inter_freqs seq_arr_t from gNB_CONFIG_STRING_GNB_LIST frequency_list entries, enforcing unique (ARFCN,SCS) combinations and logging duplicates. - Extend neighbour cell parameters in gnb_paramdef.h with SIB3 per- neighbour offset fields and define frequency_list/frequency_config option names, descriptor tables, and index constants for SIB4 inter- frequency configuration. - Introduce nr_neighbour_cell_neighbor_offset_t, nr_neighbour_cell_sib3_t, nr_neighbour_cell_sib4_freq_t, nr_neighbour_cell_sib4_t, and nr_inter_freq_cfg_t in nr_rrc_defs.h, and extend nr_neighbour_cell_t and gNB_RRC_INST with sib3/sib4 and inter_freqs members respectively. - udpated fill_neighbour_cell_configuration to use GET_PARAMS_LIST with params bound check --- openair2/GNB_APP/gnb_config.c | 110 ++++++++++++++++++++++++++++++++ openair2/GNB_APP/gnb_paramdef.h | 78 ++++++++++++++++++++++ openair2/RRC/NR/nr_rrc_defs.h | 73 +++++++++++++++++++++ 3 files changed, 261 insertions(+) diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 5980666cff..856078e6f7 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -2012,6 +2012,108 @@ static nr_neighbour_cell_t *get_neighbour_by_pci(seq_arr_t *neighbour_cells, int return NULL; } +/** @brief Parse gNB-level inter-frequency list (frequency_list) into a shared array. + * This parses gNBs.[gnb_idx].frequency_list once and validates per-frequency SIB4 + * parameters. The resulting list can then be reused for all serving cells. + * @param[out] inter_freqs Sequence array of nr_inter_freq_cfg_t to populate + * @param[in] gnb_idx gNB index */ +static bool eq_inter_freq_arfcn_scs(const void *vval, const void *vit) +{ + const nr_inter_freq_cfg_t *key = (const nr_inter_freq_cfg_t *)vval; + const nr_inter_freq_cfg_t *cur = (const nr_inter_freq_cfg_t *)vit; + return key->arfcn == cur->arfcn && key->scs == cur->scs; +} + +/** @brief Insert an inter-frequency config if (ARFCN,SCS) is unique in the list. + * Fails fast if a duplicate is found. + * @param[in,out] inter_freqs Existing inter-frequency list + * @param[in] f Candidate entry to insert (by value) + * @param[in] gnb_idx gNB index for logging + */ +static void add_inter_freq(seq_arr_t *inter_freqs, const nr_inter_freq_cfg_t *f, uint8_t gnb_idx) +{ + const nr_inter_freq_cfg_t key = {.arfcn = f->arfcn, .scs = f->scs}; + elm_arr_t dup = find_if(inter_freqs, (void *)&key, eq_inter_freq_arfcn_scs); + if (dup.found) { + AssertFatal(false, "gNB %d: duplicate inter-freq carrier ARFCN %d, SCS %d in frequency_list\n", gnb_idx, f->arfcn, f->scs); + } + + seq_arr_push_back(inter_freqs, (void *)f, sizeof(*f)); +} + +static void parse_inter_freq_list(seq_arr_t *inter_freqs, const uint8_t gnb_idx) +{ + seq_arr_init(inter_freqs, sizeof(nr_inter_freq_cfg_t)); + + char freq_path[MAX_OPTNAME_SIZE + 8]; + snprintf(freq_path, sizeof(freq_path), "%s.[%i]", GNB_CONFIG_STRING_GNB_LIST, gnb_idx); + GET_PARAMS_LIST(freq_list, freq_params, GNBFREQUENCYPARAMS_DESC, GNB_CONFIG_STRING_FREQUENCY_LIST, freq_path); + + for (int i = 0; i < freq_list.numelt; ++i) { + const paramdef_t *freq_item = freq_list.paramarray[i]; + nr_inter_freq_cfg_t f = {0}; + f.arfcn = *gpd(freq_item, sizeofArray(freq_params), GNB_CONFIG_STRING_FREQUENCY_ABS_FREQ_SSB)->i64ptr; + f.scs = *gpd(freq_item, sizeofArray(freq_params), GNB_CONFIG_STRING_FREQUENCY_SCS)->uptr; + + char cfg_path[MAX_OPTNAME_SIZE + 16]; + snprintf(cfg_path, + sizeof(cfg_path), + "%s.[%i].%s.[%i]", + GNB_CONFIG_STRING_GNB_LIST, + gnb_idx, + GNB_CONFIG_STRING_FREQUENCY_LIST, + i); + GET_PARAMS(freq_cfg, GNBFREQUENCYCONFIGPARAMS_DESC, cfg_path); + + nr_neighbour_cell_sib4_freq_t *p = &f.freq_cfg; + + p->cellReselectionPriority = *freq_cfg[GNB_CONFIG_FREQUENCY_CELL_RESEL_PRIO_IDX].iptr; + p->threshX_HighP = *freq_cfg[GNB_CONFIG_FREQUENCY_THRESH_X_HIGH_P_IDX].iptr; + p->threshX_LowP = *freq_cfg[GNB_CONFIG_FREQUENCY_THRESH_X_LOW_P_IDX].iptr; + p->threshX_HighQ = *freq_cfg[GNB_CONFIG_FREQUENCY_THRESH_X_HIGH_Q_IDX].iptr; + p->threshX_LowQ = *freq_cfg[GNB_CONFIG_FREQUENCY_THRESH_X_LOW_Q_IDX].iptr; + p->q_OffsetFreq = *freq_cfg[GNB_CONFIG_FREQUENCY_Q_OFFSET_FREQ_IDX].iptr; + f.q_RxLevMin = *freq_cfg[GNB_CONFIG_FREQUENCY_Q_RXLEVMIN_IDX].iptr; + f.t_ReselectionNR = *freq_cfg[GNB_CONFIG_FREQUENCY_T_RESEL_NR_IDX].uptr; + + /* Add inter-frequency configuration to the list. */ + add_inter_freq(inter_freqs, &f, gnb_idx); + + LOG_I(GNB_APP, + "gNB %d: SIB4 inter-freq ARFCN %d, SCS %d, prio=%d, ThreshX_HighP=%d, ThreshX_LowP=%d, ThreshX_HighQ=%d, " + "ThreshX_LowQ=%d, q_OffsetFreq=%d\n", + gnb_idx, + f.arfcn, + f.scs, + f.freq_cfg.cellReselectionPriority, + f.freq_cfg.threshX_HighP, + f.freq_cfg.threshX_LowP, + f.freq_cfg.threshX_HighQ, + f.freq_cfg.threshX_LowQ, + f.freq_cfg.q_OffsetFreq); + } +} + +static void set_sib3_offsets(nr_neighbour_cell_t *n, const paramdef_t *cell_params, int n_cell_params) +{ + nr_neighbour_cell_neighbor_offset_t *sib3_off = &n->sib3.offset; + + /* SIB3 per-neighbour offsets */ + sib3_off->q_OffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_OFFSET_CELL)->iptr; + sib3_off->q_RxLevMinOffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_RXLEVMIN_OFFSET_CELL)->iptr; + sib3_off->q_QualMinOffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_QUALMIN_OFFSET_CELL)->iptr; +} + +static void set_sib4_offsets(nr_neighbour_cell_t *n, const paramdef_t *cell_params, int n_cell_params) +{ + nr_neighbour_cell_neighbor_offset_t *sib4_off = &n->sib4.offset; + + /* SIB4 per-neighbour offsets */ + sib4_off->q_OffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_OFFSET_CELL)->iptr; + sib4_off->q_RxLevMinOffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_RXLEVMIN_OFFSET_CELL)->iptr; + sib4_off->q_QualMinOffsetCell = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_QUALMIN_OFFSET_CELL)->iptr; +} + /** @brief Parse and add neighbour cells for a given cell configuration * @param[in,out] cell Neighbour cell configuration to populate * @param[in] list Parameter list describing neighbour cells @@ -2036,6 +2138,11 @@ static void parse_neighbour_cells_list(neighbour_cell_configuration_t *cell, .absoluteFrequencySSB = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_CELL_ABS_FREQ_SSB)->i64ptr, .tac = *gpd(cell_params, n_cell_params, GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE)->uptr}; + set_sib3_offsets(&n, cell_params, n_cell_params); + set_sib4_offsets(&n, cell_params, n_cell_params); + /* SIB4 per-frequency reselection parameters (q_OffsetFreq, thresholds, priority) + * are populated in parse_inter_freq_list() per-frequency, not per neighbour here. */ + char p[CONFIG_MAXOPTLENGTH]; // plmn path snprintf(p, sizeof(p), "%s.%s.[%i].%s", n_path, GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, l, GNB_CONFIG_STRING_NEIGHBOUR_PLMN); GET_PARAMS(NeighbourPlmn, GNBPLMNPARAMS_DESC, p); @@ -2087,6 +2194,9 @@ static void fill_neighbour_cell_configuration(const uint8_t gnb_idx, gNB_RRC_INS if (nlist.numelt < 1) return; + /* Parse gNB-level inter-frequency configuration once and reuse it for all cells. */ + parse_inter_freq_list(&rrc->inter_freqs, gnb_idx); + rrc->neighbour_cell_configuration = malloc_or_fail(sizeof(seq_arr_t)); seq_arr_init(rrc->neighbour_cell_configuration, sizeof(neighbour_cell_configuration_t)); diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index 98e53d83e2..35a1c096d1 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -300,6 +300,11 @@ typedef enum { #define GNB_CONFIG_STRING_NEIGHBOUR_CELL_BAND "band" #define GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE "tracking_area_code" #define GNB_CONFIG_STRING_NEIGHBOUR_PLMN "plmn" +/* SIB3 fields (intra-frequency) - per-neighbor */ +#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_OFFSET_CELL "q_OffsetCell" +#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_RXLEVMIN_OFFSET_CELL "q_RxLevMinOffsetCell" +#define GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_QUALMIN_OFFSET_CELL "q_QualMinOffsetCell" +/* SIB4 per-frequency fields are configured in frequency_list.frequency_config */ // clang-format off #define GNBNEIGHBOURCELLPARAMS_DESC { /* optname helpstr paramflags XXXptr def val type numelt */ \ @@ -324,9 +329,82 @@ typedef enum { {GNB_CONFIG_STRING_NEIGHBOUR_TRACKING_ARE_CODE, \ "neighbour cell tracking area (0..16777215)", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \ .chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {0, 16777215}}}}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_OFFSET_CELL, \ + "Q-OffsetCell for SIB3/SIB4 (Q-OffsetRange, default 0)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, \ + {-24, -22, -20, -18, -16, -14, -12, -10, -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}, 31}}}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_RXLEVMIN_OFFSET_CELL, \ + "Q-RxLevMinOffsetCell for SIB3/SIB4 (1..8 or -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {-1, 1, 2, 3, 4, 5, 6, 7, 8}, 9}}}, \ + {GNB_CONFIG_STRING_NEIGHBOUR_CELL_Q_QUALMIN_OFFSET_CELL, \ + "Q-QualMinOffsetCell for SIB3/SIB4 (1..8 or -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {-1, 1, 2, 3, 4, 5, 6, 7, 8}, 9}}}, \ } // clang-format on +/* Per-frequency neighbour configuration (frequency_list) */ +#define GNB_CONFIG_STRING_FREQUENCY_LIST "frequency_list" +#define GNB_CONFIG_STRING_FREQUENCY_ABS_FREQ_SSB "absoluteFrequencySSB" +#define GNB_CONFIG_STRING_FREQUENCY_SCS "subcarrierSpacing" +#define GNB_CONFIG_STRING_FREQUENCY_BAND "band" +#define GNB_CONFIG_STRING_FREQUENCY_CONFIG "frequency_config" + +#define GNB_CONFIG_STRING_FREQUENCY_CELL_RESEL_PRIO "cellReselectionPriority" +#define GNB_CONFIG_STRING_FREQUENCY_THRESH_X_HIGH_P "threshX_HighP" +#define GNB_CONFIG_STRING_FREQUENCY_THRESH_X_LOW_P "threshX_LowP" +#define GNB_CONFIG_STRING_FREQUENCY_THRESH_X_HIGH_Q "threshX_HighQ" +#define GNB_CONFIG_STRING_FREQUENCY_THRESH_X_LOW_Q "threshX_LowQ" +#define GNB_CONFIG_STRING_FREQUENCY_Q_OFFSET_FREQ "q_OffsetFreq" +#define GNB_CONFIG_STRING_FREQUENCY_Q_RXLEVMIN "q_RxLevMin" +#define GNB_CONFIG_STRING_FREQUENCY_T_RESEL_NR "t_ReselectionNR" + +// clang-format off +#define GNBFREQUENCYPARAMS_DESC { \ +/* optname helpstr paramflags XXXptr def val type numelt */ \ + {GNB_CONFIG_STRING_FREQUENCY_ABS_FREQ_SSB, "inter-frequency abs freq ssb", PARAMFLAG_MANDATORY, .i64ptr=NULL, .defint64val=0, TYPE_INT64, 0}, \ + {GNB_CONFIG_STRING_FREQUENCY_SCS, "inter-frequency scs", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=0, TYPE_UINT, 0}, \ + {GNB_CONFIG_STRING_FREQUENCY_BAND, "inter-frequency band", PARAMFLAG_MANDATORY, .uptr=NULL, .defuintval=78, TYPE_UINT, 0}, \ +} + +#define GNBFREQUENCYCONFIGPARAMS_DESC { \ + {GNB_CONFIG_STRING_FREQUENCY_CELL_RESEL_PRIO, \ + "SIB4 cellReselectionPriority (0..7, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 7}}}}, \ + {GNB_CONFIG_STRING_FREQUENCY_THRESH_X_HIGH_P, \ + "SIB4 threshX-HighP (0..31)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \ + {GNB_CONFIG_STRING_FREQUENCY_THRESH_X_LOW_P, \ + "SIB4 threshX-LowP (0..31)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {0, 31}}}}, \ + {GNB_CONFIG_STRING_FREQUENCY_THRESH_X_HIGH_Q, \ + "SIB4 threshX-HighQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_FREQUENCY_THRESH_X_LOW_Q, \ + "SIB4 threshX-LowQ (0..31, -1=disabled)", 0, .iptr=NULL, .defintval=-1, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-1, 31}}}}, \ + {GNB_CONFIG_STRING_FREQUENCY_Q_OFFSET_FREQ, \ + "SIB4 q-OffsetFreq (Q-OffsetRange values, default 0)", 0, .iptr=NULL, .defintval=0, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s1 = {config_check_intval, {-24, -22, -20, -18, -16, -14, -12, -10, \ + -8, -6, -5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24}, 31}}}, \ + {GNB_CONFIG_STRING_FREQUENCY_Q_RXLEVMIN, \ + "SIB4 per-carrier Q-RxLevMin (-70..-22, default -22)", 0, .iptr=NULL, .defintval=-22, TYPE_INT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_intrange, {-70, -22}}}}, \ + {GNB_CONFIG_STRING_FREQUENCY_T_RESEL_NR, \ + "SIB4 per-carrier t-ReselectionNR (0..7, default 0)", 0, .uptr=NULL, .defuintval=0, TYPE_UINT, 0, \ + .chkPptr = &(checkedparam_t){.s2 = {config_check_uintrange, {0, 7}}}}, \ +} +// clang-format on + +/* Indexes into GNBFREQUENCYCONFIGPARAMS_DESC */ +#define GNB_CONFIG_FREQUENCY_CELL_RESEL_PRIO_IDX 0 +#define GNB_CONFIG_FREQUENCY_THRESH_X_HIGH_P_IDX 1 +#define GNB_CONFIG_FREQUENCY_THRESH_X_LOW_P_IDX 2 +#define GNB_CONFIG_FREQUENCY_THRESH_X_HIGH_Q_IDX 3 +#define GNB_CONFIG_FREQUENCY_THRESH_X_LOW_Q_IDX 4 +#define GNB_CONFIG_FREQUENCY_Q_OFFSET_FREQ_IDX 5 +#define GNB_CONFIG_FREQUENCY_Q_RXLEVMIN_IDX 6 +#define GNB_CONFIG_FREQUENCY_T_RESEL_NR_IDX 7 + /* New Measurement Configurations*/ #define GNB_CONFIG_STRING_MEASUREMENT_CONFIGURATION "nr_measurement_configuration" diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 68104ba203..f0e212c79f 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -312,6 +312,72 @@ typedef struct { bool is_default_a3_configuration_exists; } nr_measurement_configuration_t; +/** @brief Per-neighbor cell-specific offsets (TS 38.331), shared by SIB3 and SIB4 + * Maps to ASN.1 IntraFreqNeighCellInfo and InterFreqNeighCellInfo + * (SIB3.IntraFreqNeighCellList, SIB4.InterFreqCarrierFreqInfo.neighCellList) */ +typedef struct { + // q-OffsetCell: (Q-OffsetRange values -24,-22,-20,-18,-16,-14,-12,-10,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8,10,12,14,16,18,20,22,24 dB) + // per-neighbor cell ranking + int q_OffsetCell; + // q-RxLevMinOffsetCell: Q-OffsetCellSmall (1..8): Per-neighbor RSRP minimum offset + int q_RxLevMinOffsetCell; + // q-QualMinOffsetCell: Q-OffsetCellSmall (1..8): Per-neighbor RSRQ minimum offset + int q_QualMinOffsetCell; +} nr_neighbour_cell_neighbor_offset_t; + +/** @brief SIB3 (intra-frequency) neighbor list parameters (TS 38.331) + * Per-neighbor fields for SIB3.IntraFreqNeighCellList.IntraFreqNeighCellInfo. + * SIB3 carries intra-freq neighbor offsets for ranking. */ +typedef struct { + nr_neighbour_cell_neighbor_offset_t offset; +} nr_neighbour_cell_sib3_t; + +/** @brief SIB4 inter-frequency carrier parameters (TS 38.331) + * Per-frequency fields for SIB4.InterFreqCarrierFreqList.InterFreqCarrierFreqInfo. + * Stored once per ARFCN; all neighbors on that frequency share this config. */ +typedef struct { + // cellReselectionPriority (0..7): Absolute priority of this inter-freq carrier + int cellReselectionPriority; + // threshX-HighP (0..31): RSRP threshold for reselection to a higher-priority inter-freq + int threshX_HighP; + // threshX-LowP (0..31): RSRP threshold for reselection to a lower-priority inter-freq + int threshX_LowP; + // threshX-HighQ (0..31): RSRQ threshold for higher-priority inter-freq reselection + int threshX_HighQ; + // threshX-LowQ (0..31): RSRQ threshold for lower-priority inter-freq reselection + int threshX_LowQ; + // q-OffsetFreq (Q-OffsetRange values: -24,-22,-20,-18,-16,-14,-12,-10,-8,-6,-5,-4,-3,-2,-1,0,1,2,3,4,5,6,8,10,12,14,16,18,20,22,24 dB): + // Frequency-specific offset in inter-freq cell ranking formula + int q_OffsetFreq; +} nr_neighbour_cell_sib4_freq_t; + +/** @brief SIB4 (inter-frequency) parameters per neighbor (TS 38.331) + * Combines per-frequency carrier info (InterFreqCarrierFreqInfo) and per-neighbor + * offsets (InterFreqNeighCellInfo). */ +typedef struct { + // Per-frequency carrier (InterFreqCarrierFreqInfo) + nr_neighbour_cell_sib4_freq_t sib4_freq; + // Per-neighbor offsets (InterFreqNeighCellInfo) + nr_neighbour_cell_neighbor_offset_t offset; +} nr_neighbour_cell_sib4_t; + +/** @brief Per-frequency SIB4 configuration (TS 38.331), keyed by ARFCN + * One entry per inter-freq carrier (absoluteFrequencySSB + subcarrierSpacing). + * freq_cfg holds InterFreqCarrierFreqInfo parameters. Analogous to one “carrier” slice of SIB4 + * interFreqCarrierFreqList. */ +typedef struct { + // absoluteFrequencySSB (ARFCN) + int arfcn; + // ssbSubcarrierSpacing + int scs; + // q-RxLevMin (-70..-22 dBm) + int q_RxLevMin; + // t-ReselectionNR (0..7) + int t_ReselectionNR; + // Inter-frequency carrier parameters (InterFreqCarrierFreqInfo) + nr_neighbour_cell_sib4_freq_t freq_cfg; +} nr_inter_freq_cfg_t; + typedef struct { uint32_t gNB_ID; uint64_t nrcell_id; @@ -322,6 +388,10 @@ typedef struct { plmn_id_t plmn; uint32_t tac; bool isIntraFrequencyNeighbour; + // SIB3 (intra-frequency neighbor cell-specific offsets) + nr_neighbour_cell_sib3_t sib3; + // SIB4 (inter-frequency neighbor cell-specific parameters) + nr_neighbour_cell_sib4_t sib4; } nr_neighbour_cell_t; typedef struct neighbour_cell_configuration_s { @@ -540,6 +610,9 @@ typedef struct gNB_RRC_INST_s { nr_mac_rrc_dl_if_t mac_rrc; cucp_cuup_if_t cucp_cuup; + // Per-frequency SIB4 configurations, indexed by ARFCN + seq_arr_t inter_freqs; /* array of nr_inter_freq_cfg_t */ + // Per-cell neighbour configurations, indexed by cell_id seq_arr_t *neighbour_cell_configuration; nr_measurement_configuration_t measurementConfiguration; From 817d1a6cb5c15bf2cc747e13ffee748ccddc8dd7 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 11 Feb 2026 10:51:18 +0100 Subject: [PATCH 14/72] RRC/MAC: add SIB3 intra-freqeuncy neighbour support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce NR SIB3 encoding in RRC and wire SIB3 neighbour information into CU–DU F1 Setup and DU SystemInformation. Extend MAC to decode NR SIB3 from CU-provided SIB containers and attach the decoded SIB3 to the DU SystemInformation structure. Changes: - Add NR_SIB3 ASN.1 include and a do_SIB3_NR() encoder in asn1_msg so SIB3 can be serialized into a byte_array_t buffer. - Introduce get_q_offset_asn1() and get_sib3_intra_freq_neighbors() in rrc_gNB_du.c to build an intra-frequency neighbour cell list for SIB3, mapping q_OffsetCell dB values to NR_Q_OffsetRange and optionally filling q_RxLevMinOffsetCell and q_QualMinOffsetCell. - Limit the number of intra-frequency neighbours in SIB3 to NR_maxCellIntra, logging a warning and skipping additional cells when the limit is reached. - Hook SIB3 construction and encoding into rrc_gNB_process_f1_setup_req() so SIB type 3 entries are added to the F1 Setup Response when neighbour configuration and matching neighbours are available. - Handle NR_SIB_3 in nr_mac_configure_other_sib() by validating the CU SIB3 container buffer, decoding it with uper_decode into an NR_SIB3_t, and attaching the resulting SIB3 to the MAC SystemInformation via add_sib_to_systeminformation(). --- openair2/LAYER2/NR_MAC_gNB/config.c | 15 +++ openair2/RRC/NR/MESSAGES/asn1_msg.c | 21 ++++ openair2/RRC/NR/MESSAGES/asn1_msg.h | 2 + openair2/RRC/NR/rrc_gNB_du.c | 156 ++++++++++++++++++++++++++++ 4 files changed, 194 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index 18d5bbb4a8..f38eebd326 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -1008,6 +1008,21 @@ bool nr_mac_configure_other_sib(gNB_MAC_INST *nrmac, int num_cu_sib, const f1ap_ add_sib_to_systeminformation(sysInfo, type); break; } + case NR_SIB_3: { + struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc_or_fail(1, sizeof(*type)); + type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib3; + NR_SIB3_t *sib3 = NULL; + asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_SIB3, (void **)&sib3, container->buf, container->len, 0, 0); + if (dec_rval.code != RC_OK) { + LOG_E(NR_MAC, "cannot decode SIB%d from CU\n", config_sibs[i]); + ASN_STRUCT_FREE(asn_DEF_NR_SIB3, sib3); + free(type); + break; + } + type->choice.sib3 = sib3; + add_sib_to_systeminformation(sysInfo, type); + break; + } default : AssertFatal(false, "Invalid or not supported SIB%d\n", config_sibs[i]); } diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index b8cd1ab284..80235ebdb7 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -244,6 +244,27 @@ byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2) return msg; } +byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3) +{ + byte_array_t msg = {.buf = NULL, .len = 0}; + char errbuf[256] = {0}; + size_t errlen = sizeof(errbuf); + int ret = asn_check_constraints(&asn_DEF_NR_SIB3, sib3, errbuf, &errlen); + if (ret != 0) { + LOG_E(NR_RRC, "SIB3 constraint check failed: %s\n", errbuf); + return msg; + } + + int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB3, NULL, (void *)sib3, (void **)&msg.buf); + if (val <= 0) { + LOG_E(NR_RRC, "Failed to encode SIB3\n"); + return msg; + } + + msg.len = val; + return msg; +} + int do_RRCReject(uint8_t *const buffer) { asn_enc_rval_t enc_rval; diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 43f4e7dcef..11963780f1 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -34,6 +34,7 @@ #include "NR_UE-NR-Capability.h" #include "NR_UE-CapabilityRAT-ContainerList.h" #include "NR_SIB2.h" +#include "NR_SIB3.h" #include "ds/seq_arr.h" #include "ds/byte_array.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h" @@ -67,6 +68,7 @@ typedef struct { int xer_sprint_NR(char *string, size_t string_size, struct asn_TYPE_descriptor_s *td, void *sptr); byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2); +byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3); int do_RRCReject(uint8_t *const buffer); diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index d5d0f27be9..43692ec742 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -24,6 +24,8 @@ #include "ngap_messages_types.h" #include "nr_rrc_defs.h" #include "NR_SIB2.h" +#include "NR_SIB3.h" +#include "NR_Q-OffsetRange.h" #include "openair2/RRC/NR/MESSAGES/asn1_msg.h" #include "rrc_gNB_mobility.h" #include "openair2/F1AP/f1ap_common.h" @@ -35,6 +37,7 @@ #include "s1ap_messages_types.h" #include "tree.h" #include "uper_decoder.h" +#include "common/utils/oai_asn1.h" #include "utils.h" #include "xer_encoder.h" @@ -172,6 +175,136 @@ static NR_SIB2_t *get_sib2_from_cfg(const sib2_config_t *cfg, NR_SSB_MTC_t *ssbm return sib2; } +/** @brief Map internal int enum to ASN.1 Q-OffsetRange value. */ +static long get_q_offset_asn1(int q_offset_db) +{ + switch (q_offset_db) { + case -24: + return NR_Q_OffsetRange_dB_24; + case -22: + return NR_Q_OffsetRange_dB_22; + case -20: + return NR_Q_OffsetRange_dB_20; + case -18: + return NR_Q_OffsetRange_dB_18; + case -16: + return NR_Q_OffsetRange_dB_16; + case -14: + return NR_Q_OffsetRange_dB_14; + case -12: + return NR_Q_OffsetRange_dB_12; + case -10: + return NR_Q_OffsetRange_dB_10; + case -8: + return NR_Q_OffsetRange_dB_8; + case -6: + return NR_Q_OffsetRange_dB_6; + case -5: + return NR_Q_OffsetRange_dB_5; + case -4: + return NR_Q_OffsetRange_dB_4; + case -3: + return NR_Q_OffsetRange_dB_3; + case -2: + return NR_Q_OffsetRange_dB_2; + case -1: + return NR_Q_OffsetRange_dB_1; + case 0: + return NR_Q_OffsetRange_dB0; + case 1: + return NR_Q_OffsetRange_dB1; + case 2: + return NR_Q_OffsetRange_dB2; + case 3: + return NR_Q_OffsetRange_dB3; + case 4: + return NR_Q_OffsetRange_dB4; + case 5: + return NR_Q_OffsetRange_dB5; + case 6: + return NR_Q_OffsetRange_dB6; + case 8: + return NR_Q_OffsetRange_dB8; + case 10: + return NR_Q_OffsetRange_dB10; + case 12: + return NR_Q_OffsetRange_dB12; + case 14: + return NR_Q_OffsetRange_dB14; + case 16: + return NR_Q_OffsetRange_dB16; + case 18: + return NR_Q_OffsetRange_dB18; + case 20: + return NR_Q_OffsetRange_dB20; + case 22: + return NR_Q_OffsetRange_dB22; + case 24: + return NR_Q_OffsetRange_dB24; + default: + AssertFatal(false, "Invalid q_offset_db value %d\n", q_offset_db); + } +} + +/** @brief Build a SIB3 (intra-frequency neighbour cell list) from the configured neighbour cells. + * Only neighbours whose SSB ARFCN matches @param serving_ssb_arfcn are included. + * @param neighbours list of configured neighbour cells + * @param serving_ssb_arfcn SSB ARFCN of the serving cell, used to filter intra-frequency neighbours + * @returns allocated NR_SIB3_t on success, NULL if no matching neighbours were found */ +static NR_SIB3_t *get_sib3_intra_freq_neighbors(const seq_arr_t *neighbours, uint32_t serving_ssb_arfcn) +{ + DevAssert(neighbours); + + NR_SIB3_t *sib3 = calloc_or_fail(1, sizeof(*sib3)); + sib3->intraFreqNeighCellList = calloc_or_fail(1, sizeof(*sib3->intraFreqNeighCellList)); + + int intra_count = 0; + FOR_EACH_SEQ_ARR (const nr_neighbour_cell_t *, neigh, neighbours) { + if (neigh->absoluteFrequencySSB != serving_ssb_arfcn) + continue; + + LOG_D(NR_RRC, + "SIB3: intra-frequency neighbour candidate: cell ID %lu, PCI %d (SSB ARFCN %d)\n", + neigh->nrcell_id, + neigh->physicalCellId, + neigh->absoluteFrequencySSB); + + NR_IntraFreqNeighCellInfo_t *cell = calloc_or_fail(1, sizeof(*cell)); + cell->physCellId = neigh->physicalCellId; + + const nr_neighbour_cell_neighbor_offset_t *off = &neigh->sib3.offset; + /* q-OffsetCell: NR_Q-OffsetRange (-24..24 dB), 0 dB default */ + cell->q_OffsetCell = get_q_offset_asn1(off->q_OffsetCell); + + /* q-RxLevMinOffsetCell / q-QualMinOffsetCell: (1..8) */ + if (off->q_RxLevMinOffsetCell != -1) + asn1cCallocOne(cell->q_RxLevMinOffsetCell, off->q_RxLevMinOffsetCell); + if (off->q_QualMinOffsetCell != -1) + asn1cCallocOne(cell->q_QualMinOffsetCell, off->q_QualMinOffsetCell); + + /* Add to SIB3 intra-frequency neighbour list */ + if (sib3->intraFreqNeighCellList->list.count >= NR_maxCellIntra) { + LOG_W(NR_RRC, "SIB3: too many intra-frequency neighbors (max %d), skipping cell %ld\n", NR_maxCellIntra, neigh->nrcell_id); + asn1cFreeStruc(asn_DEF_NR_IntraFreqNeighCellInfo, cell); + break; + } + asn1cSeqAdd(&sib3->intraFreqNeighCellList->list, cell); + ++intra_count; + } + + if (intra_count == 0) { + LOG_W(NR_RRC, + "SIB3: no intra-frequency neighbours found for serving SSB ARFCN %u " + "(configured neighbours %u), not building SIB3\n", + serving_ssb_arfcn, + (unsigned)seq_arr_size(neighbours)); + asn1cFreeStruc(asn_DEF_NR_SIB3, sib3); + return NULL; + } + + return sib3; +} + /** @brief Return the frequency of the SS block of the cell for which this message is included, * or of other SS blocks within the same carrier. MeasurementTimingConfiguration message is * used to convey assistance information for measurement timing between nodes (e.g F1 setup) */ @@ -569,6 +702,29 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) free_byte_array(enc); LOG_I(NR_RRC, "DU %ld: added SIB2 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id); } break; + case NR_SIB_3: { + if (!rrc->neighbour_cell_configuration) + break; + const neighbour_cell_configuration_t *neighbour_config = get_cell_neighbour_list(rrc, new); + if (!neighbour_config) + break; + const seq_arr_t *neigh_cells = &neighbour_config->neighbour_cells; + + NR_SIB3_t *sib3 = get_sib3_intra_freq_neighbors(neigh_cells, get_ssb_arfcn(new)); + if (!sib3) + break; + + byte_array_t enc = do_SIB3_NR(sib3); + ASN_STRUCT_FREE(asn_DEF_NR_SIB3, sib3); + if (!enc.buf || enc.len == 0) { + free_byte_array(enc); + LOG_E(NR_RRC, "SIB3 encoding failed\n"); + break; + } + add_si_msg(&cell, sib->SIB_type, &enc); + free_byte_array(enc); + LOG_I(NR_RRC, "DU %ld: added SIB3 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id); + } break; default: AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type); } From 6f9a716eb9eb854d9ac693b00dae38dbd8a3f6bd Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Wed, 11 Feb 2026 10:56:24 +0100 Subject: [PATCH 15/72] RRC/MAC: add SIB4 inter-frequency neighbour support Add SIB4 encode/decode helpers and populate SIB4 from configured inter-frequency carriers and neighbour cells, wiring it into DU F1 Setup handling and MAC SystemInformation. Changes: - Add do_SIB4_NR() and NR_SIB4.h inclusion in asn1_msg.c/asn1_msg.h to UPER-encode NR_SIB4_t into a byte_array_t. - Extend nr_mac_configure_other_sib() in NR_MAC_gNB/config.c with an NR_SIB_4 case that validates the SIB container, decodes NR_SIB4_t via uper_decode, logs failures, and appends SIB4 to SystemInformation. - Introduce build_inter_freq_carrier_from_cfg() and get_sib4_inter_freq_neighbors() in rrc_gNB_du.c to build NR_InterFreqCarrierFreqInfo_t and NR_SIB4_t from inter-frequency and neighbour cell configuration, skipping invalid or empty carriers. - Handle NR_SIB_4 in rrc_gNB_process_f1_setup_req() by deriving SIB4 from neighbour/inter-frequency configuration, encoding it with do_SIB4_NR(), validating the resulting byte_array_t and adding the SI message for the DU cell when encoding succeeds. --- openair2/LAYER2/NR_MAC_gNB/config.c | 15 +++ openair2/RRC/NR/MESSAGES/asn1_msg.c | 21 ++++ openair2/RRC/NR/MESSAGES/asn1_msg.h | 2 + openair2/RRC/NR/rrc_gNB_du.c | 149 ++++++++++++++++++++++++++++ 4 files changed, 187 insertions(+) diff --git a/openair2/LAYER2/NR_MAC_gNB/config.c b/openair2/LAYER2/NR_MAC_gNB/config.c index f38eebd326..1d217b7c72 100644 --- a/openair2/LAYER2/NR_MAC_gNB/config.c +++ b/openair2/LAYER2/NR_MAC_gNB/config.c @@ -1023,6 +1023,21 @@ bool nr_mac_configure_other_sib(gNB_MAC_INST *nrmac, int num_cu_sib, const f1ap_ add_sib_to_systeminformation(sysInfo, type); break; } + case NR_SIB_4: { + struct NR_SystemInformation_IEs__sib_TypeAndInfo__Member *type = calloc_or_fail(1, sizeof(*type)); + type->present = NR_SystemInformation_IEs__sib_TypeAndInfo__Member_PR_sib4; + NR_SIB4_t *sib4 = NULL; + asn_dec_rval_t dec_rval = uper_decode(NULL, &asn_DEF_NR_SIB4, (void **)&sib4, container->buf, container->len, 0, 0); + if (dec_rval.code != RC_OK) { + LOG_E(NR_MAC, "cannot decode SIB%d from CU\n", config_sibs[i]); + ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4); + free(type); + break; + } + type->choice.sib4 = sib4; + add_sib_to_systeminformation(sysInfo, type); + break; + } default : AssertFatal(false, "Invalid or not supported SIB%d\n", config_sibs[i]); } diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 80235ebdb7..d818229054 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -265,6 +265,27 @@ byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3) return msg; } +byte_array_t do_SIB4_NR(NR_SIB4_t *sib4) +{ + byte_array_t msg = {.buf = NULL, .len = 0}; + char errbuf[256] = {0}; + size_t errlen = sizeof(errbuf); + int ret = asn_check_constraints(&asn_DEF_NR_SIB4, sib4, errbuf, &errlen); + if (ret != 0) { + LOG_E(NR_RRC, "SIB4 constraint check failed: %s\n", errbuf); + return msg; + } + + int val = uper_encode_to_new_buffer(&asn_DEF_NR_SIB4, NULL, (void *)sib4, (void **)&msg.buf); + if (val <= 0) { + LOG_E(NR_RRC, "Failed to encode SIB4\n"); + return msg; + } + + msg.len = val; + return msg; +} + int do_RRCReject(uint8_t *const buffer) { asn_enc_rval_t enc_rval; diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 11963780f1..384096ee16 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -35,6 +35,7 @@ #include "NR_UE-CapabilityRAT-ContainerList.h" #include "NR_SIB2.h" #include "NR_SIB3.h" +#include "NR_SIB4.h" #include "ds/seq_arr.h" #include "ds/byte_array.h" #include "openair2/LAYER2/nr_pdcp/nr_pdcp_configuration.h" @@ -69,6 +70,7 @@ int xer_sprint_NR(char *string, size_t string_size, struct asn_TYPE_descriptor_s byte_array_t do_SIB2_NR(const NR_SIB2_t *sib2); byte_array_t do_SIB3_NR(const NR_SIB3_t *sib3); +byte_array_t do_SIB4_NR(NR_SIB4_t *sib4); int do_RRCReject(uint8_t *const buffer); diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 43692ec742..9843d02b83 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -25,6 +25,7 @@ #include "nr_rrc_defs.h" #include "NR_SIB2.h" #include "NR_SIB3.h" +#include "NR_SIB4.h" #include "NR_Q-OffsetRange.h" #include "openair2/RRC/NR/MESSAGES/asn1_msg.h" #include "rrc_gNB_mobility.h" @@ -305,6 +306,130 @@ static NR_SIB3_t *get_sib3_intra_freq_neighbors(const seq_arr_t *neighbours, uin return sib3; } +/** @brief Build an NR_InterFreqCarrierFreqInfo structure from a nr_inter_freq_cfg_t configuration. + * @param cfg Configuration to build the carrier from + * @return Allocated NR_InterFreqCarrierFreqInfo_t on success, NULL if the configuration is invalid */ +static NR_InterFreqCarrierFreqInfo_t *build_inter_freq_carrier_from_cfg(const nr_inter_freq_cfg_t *cfg) +{ + DevAssert(cfg); + + const nr_neighbour_cell_sib4_freq_t *f = &cfg->freq_cfg; + + NR_InterFreqCarrierFreqInfo_t *carrier = calloc_or_fail(1, sizeof(*carrier)); + /* dl_CarrierFreq (ARFCN) */ + carrier->dl_CarrierFreq = cfg->arfcn; + /* ssbSubcarrierSpacing: (15/30/60/120 kHz) */ + carrier->ssbSubcarrierSpacing = cfg->scs; + carrier->deriveSSB_IndexFromCell = true; + /* Q-RxLevMin (-70..-22 dBm) */ + carrier->q_RxLevMin = cfg->q_RxLevMin; + /* T-Reselection (0..7) */ + carrier->t_ReselectionNR = cfg->t_ReselectionNR; + /* threshX_HighP/LowP: Reselection threshold (0..31) */ + carrier->threshX_HighP = f->threshX_HighP; + carrier->threshX_LowP = f->threshX_LowP; + /* threshX-Q: Optional RSRQ thresholds; emit only when both are provided. */ + if (f->threshX_HighQ != -1 && f->threshX_LowQ != -1) { + carrier->threshX_Q = calloc_or_fail(1, sizeof(*carrier->threshX_Q)); + carrier->threshX_Q->threshX_HighQ = f->threshX_HighQ; + carrier->threshX_Q->threshX_LowQ = f->threshX_LowQ; + } else if (f->threshX_HighQ != -1 || f->threshX_LowQ != -1) { + LOG_W(NR_RRC, + "SIB4: partial threshX-Q for ARFCN %d (HighQ=%d LowQ=%d), omitting threshX-Q\n", + cfg->arfcn, + f->threshX_HighQ, + f->threshX_LowQ); + } + /* CellReselectionPriority (0..7) */ + if (f->cellReselectionPriority != -1) + asn1cCallocOne(carrier->cellReselectionPriority, f->cellReselectionPriority); + /* q-OffsetFreq (-24..24 dB), 0 dB default */ + asn1cCallocOne(carrier->q_OffsetFreq, get_q_offset_asn1(f->q_OffsetFreq)); + /* InterFreqNeighCellList */ + carrier->interFreqNeighCellList = calloc_or_fail(1, sizeof(*carrier->interFreqNeighCellList)); + return carrier; +} + +/** + * @brief Build an NR_SIB4 structure populated with inter-frequency neighbour cell entries. + * + * Iterates over all configured inter-frequency carriers (excluding the serving cell ARFCN) + * and, for each carrier, adds the matching neighbour cells from @p neighbour_config. + * Carriers with no matching neighbours are discarded. If no valid carrier is found, + * the function returns NULL. + * + * @param neighbour_config Neighbour cell configuration (cells only). + * @param inter_freqs gNB-level inter-frequency configuration. + * @param serving_ssb_arfcn SSB ARFCN of the serving cell, used to skip the serving carrier. + * @return Allocated NR_SIB4_t on success, NULL if no inter-frequency neighbours are available. + */ +static NR_SIB4_t *get_sib4_inter_freq_neighbors(const neighbour_cell_configuration_t *neighbour_config, + const seq_arr_t *inter_freqs, + uint32_t serving_ssb_arfcn) +{ + DevAssert(neighbour_config); + + NR_SIB4_t *sib4 = calloc_or_fail(1, sizeof(*sib4)); + + const seq_arr_t *neigh = &neighbour_config->neighbour_cells; + + FOR_EACH_SEQ_ARR (nr_inter_freq_cfg_t *, cfg, inter_freqs) { + if (cfg->arfcn == serving_ssb_arfcn) + continue; + + NR_InterFreqCarrierFreqInfo_t *carrier = build_inter_freq_carrier_from_cfg(cfg); + if (!carrier) + continue; + + NR_InterFreqNeighCellList_t *neigh_list = carrier->interFreqNeighCellList; + + FOR_EACH_SEQ_ARR (nr_neighbour_cell_t *, nc, neigh) { + if (nc->absoluteFrequencySSB != cfg->arfcn || nc->subcarrierSpacing != cfg->scs) + continue; + + if (neigh_list->list.count >= NR_maxCellInter) { + LOG_W(NR_RRC, + "SIB4: too many inter-frequency neighbors for ARFCN %ld (max %d), skipping neighbour\n", + carrier->dl_CarrierFreq, + NR_maxCellInter); + continue; + } + + const nr_neighbour_cell_neighbor_offset_t *offset = &nc->sib4.offset; + LOG_I(NR_RRC, + "SIB4: added inter-frequency neighbour cell: cellId %ld (PCI %d), ARFCN %d, SCS %d\n", + nc->nrcell_id, + nc->physicalCellId, + cfg->arfcn, + cfg->scs); + asn1cSequenceAdd(neigh_list->list, NR_InterFreqNeighCellInfo_t, cell); + cell->physCellId = nc->physicalCellId; + + /* q_OffsetCell: 0 dB is default */ + cell->q_OffsetCell = get_q_offset_asn1(offset->q_OffsetCell); + + /* q_RxLevMinOffsetCell / q_QualMinOffsetCell: range 1..8 */ + if (offset->q_RxLevMinOffsetCell != -1) + asn1cCallocOne(cell->q_RxLevMinOffsetCell, offset->q_RxLevMinOffsetCell); + if (offset->q_QualMinOffsetCell != -1) + asn1cCallocOne(cell->q_QualMinOffsetCell, offset->q_QualMinOffsetCell); + } + + if (neigh_list->list.count > 0) { + asn1cSeqAdd(&sib4->interFreqCarrierFreqList.list, carrier); + } else { + ASN_STRUCT_FREE(asn_DEF_NR_InterFreqCarrierFreqInfo, carrier); + } + } + + if (sib4->interFreqCarrierFreqList.list.count == 0) { + ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4); + return NULL; + } + + return sib4; +} + /** @brief Return the frequency of the SS block of the cell for which this message is included, * or of other SS blocks within the same carrier. MeasurementTimingConfiguration message is * used to convey assistance information for measurement timing between nodes (e.g F1 setup) */ @@ -725,6 +850,30 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) free_byte_array(enc); LOG_I(NR_RRC, "DU %ld: added SIB3 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id); } break; + case NR_SIB_4: { + if (!rrc->neighbour_cell_configuration) + break; + const neighbour_cell_configuration_t *neighbour_cfg = get_cell_neighbour_list(rrc, new); + if (!neighbour_cfg) + break; + + NR_SIB4_t *sib4 = get_sib4_inter_freq_neighbors(neighbour_cfg, &rrc->inter_freqs, get_ssb_arfcn(new)); + if (!sib4) { + LOG_W(NR_RRC, "SIB4: could not build from neighbours, skipping\n"); + break; + } + + byte_array_t enc = do_SIB4_NR(sib4); + ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4); + if (!enc.buf || enc.len == 0) { + free_byte_array(enc); + LOG_E(NR_RRC, "SIB4 encoding failed\n"); + break; + } + add_si_msg(&cell, sib->SIB_type, &enc); + free_byte_array(enc); + LOG_I(NR_RRC, "DU %ld: added SIB4 to F1 Setup Response (cell %ld)\n", du->gNB_DU_id, new->info.cell_id); + } break; default: AssertFatal(false, "SIB%d not handled yet\n", sib->SIB_type); } From 4569ef199d2cee6c803530f61b6afefbb3905e0e Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 15 Jan 2026 23:49:40 +0100 Subject: [PATCH 16/72] Refactor (RRC): drop isIntraFrequencyNeighbour flag and labelling Remove the isIntraFrequencyNeighbour field and all code that sets or uses it, simplifying neighbour cell handling and avoiding redundant state in the RRC gNB code. The flag duplicated information that can be derived from the neighbour absoluteFrequencySSB and the serving cell SSB ARFCN, and intra-frequency neighbours are logged in the SIB3 preparation flow. Changes: - Remove isIntraFrequencyNeighbour from nr_neighbour_cell_t in openair2/RRC/NR/nr_rrc_defs.h - Delete is_intra_frequency_neighbour() and label_intra_frequency_neighbours() helpers from openair2/RRC/NR/rrc_gNB_du.c - Remove calls to label_intra_frequency_neighbours() from rrc_gNB_process_f1_setup_req() and rrc_gNB_process_f1_du_configuration_update() in rrc_gNB_du.c - Stop setting .isIntraFrequencyNeighbour in the local nr_neighbour_cell_t neighbourConfig initializer in openair2/RRC/NR/rrc_gNB_mobility.c --- openair2/RRC/NR/nr_rrc_defs.h | 1 - openair2/RRC/NR/rrc_gNB_du.c | 42 ------------------------------ openair2/RRC/NR/rrc_gNB_mobility.c | 1 - 3 files changed, 44 deletions(-) diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index f0e212c79f..113fbe2f94 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -387,7 +387,6 @@ typedef struct { int band; plmn_id_t plmn; uint32_t tac; - bool isIntraFrequencyNeighbour; // SIB3 (intra-frequency neighbor cell-specific offsets) nr_neighbour_cell_sib3_t sib3; // SIB4 (inter-frequency neighbor cell-specific parameters) diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index 9843d02b83..6b60e5b6b9 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -538,41 +538,6 @@ static const neighbour_cell_configuration_t *get_cell_neighbour_list(const gNB_R return (const neighbour_cell_configuration_t *)it; } -static void is_intra_frequency_neighbour(void *ssb_arfcn, void *neighbour_cell) -{ - uint32_t *ssb_arfcn_ptr = (uint32_t *)ssb_arfcn; - nr_neighbour_cell_t *neighbour_cell_ptr = (nr_neighbour_cell_t *)neighbour_cell; - - if (*ssb_arfcn_ptr == neighbour_cell_ptr->absoluteFrequencySSB) { - LOG_D(NR_RRC, "HO LOG: found intra frequency neighbour %lu!\n", neighbour_cell_ptr->nrcell_id); - neighbour_cell_ptr->isIntraFrequencyNeighbour = true; - } -} -/** - * @brief Labels neighbour cells if they are intra frequency to prepare meas config only for intra frequency ho - * @param[in] rrc Pointer to RRC instance - * @param[in] cell Pointer to cell container - */ -static void label_intra_frequency_neighbours(gNB_RRC_INST *rrc, const nr_rrc_cell_container_t *cell) -{ - if (!rrc->neighbour_cell_configuration) - return; - - const neighbour_cell_configuration_t *neighbour_cell_config = get_cell_neighbour_list(rrc, cell); - if (!neighbour_cell_config) - return; - - uint32_t ssb_arfcn = get_ssb_arfcn(cell); - LOG_D(NR_RRC, - "Cell %lu (PCI %d, SSB ARFCN %u) has neighbour cell configuration, labeling intra-frequency neighbours\n", - cell->info.cell_id, - cell->info.pci, - ssb_arfcn); - - const seq_arr_t *cell_neighbour_list = &neighbour_cell_config->neighbour_cells; - for_each((seq_arr_t *)cell_neighbour_list, (void *)&ssb_arfcn, is_intra_frequency_neighbour); -} - static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_configuration, const f1ap_served_cell_info_t *cell) { // MTC is mandatory, but some DUs don't send it in the F1 Setup Request, so @@ -880,9 +845,6 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) } } - if (new->mib != NULL &&new->sib1 != NULL) - label_intra_frequency_neighbours(rrc, new); - resp.cells_to_activate[i] = cell; } @@ -1098,10 +1060,6 @@ void rrc_gNB_process_f1_du_configuration_update(f1ap_gnb_du_configuration_update LOG_I(NR_RRC, "update system information of DU %ld\n", du->gNB_DU_id); } } - - if (cell && cell->mib != NULL && cell->sib1 != NULL && cell->assoc_id == du->assoc_id) { - label_intra_frequency_neighbours(rrc, cell); - } } if (conf_up->num_cells_to_delete > 0) { diff --git a/openair2/RRC/NR/rrc_gNB_mobility.c b/openair2/RRC/NR/rrc_gNB_mobility.c index e36e6ee684..fdbc2d66af 100644 --- a/openair2/RRC/NR/rrc_gNB_mobility.c +++ b/openair2/RRC/NR/rrc_gNB_mobility.c @@ -607,7 +607,6 @@ void nr_HO_N2_trigger_telnet(gNB_RRC_INST *rrc, uint32_t neighbour_pci, uint32_t if (neighbour_pci == scell_pci) { LOG_I(NR_RRC, "UE %d: trigger handover on the same cell PCI=%d\n", rrc_ue_id, neighbour_pci); nr_neighbour_cell_t neighbourConfig = { - .isIntraFrequencyNeighbour = true, .gNB_ID = du->gNB_DU_id, .nrcell_id = pcell->info.cell_id, .physicalCellId = pcell->info.pci, From da7054092a1a10985fa540895d06e2a520e43933 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Thu, 15 Jan 2026 23:44:26 +0100 Subject: [PATCH 17/72] Reorganize and document nr_neighbour_cell_t structure - Add comprehensive documentation header explaining the structure's role as single source of truth for neighbor cell information across multiple protocols (NGAP/XnAP handover, measurement config, DU validation, SIB3/SIB4) - Add detailed field comments documenting protocol-specific mappings: * Core identification fields (gNB_ID, nrcell_id, physicalCellId) * Frequency/RF parameters (absoluteFrequencySSB, subcarrierSpacing, band) * Handover target identification (plmn, tac) - Document NGAP/XnAP IE mappings for plmn and tac fields --- openair2/RRC/NR/nr_rrc_defs.h | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index 113fbe2f94..5a4063c3f2 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -378,14 +378,31 @@ typedef struct { nr_neighbour_cell_sib4_freq_t freq_cfg; } nr_inter_freq_cfg_t; +/** @brief Neighbor cell configuration structure + * Single source of truth for neighbor cell information, used across multiple protocols/scopes: + * - Handover (NGAP/XnAP): for target gNB (ID, plmn, tac, nrcell_id, physicalCellId) + * - Measurement (MeasConfig): physicalCellId, absoluteFrequencySSB, band + * - DU validation: all fields validated against actual cell information + * - SIB3/SIB4 generation: physicalCellId, absoluteFrequencySSB, subcarrierSpacing, band + * References: + * - 3GPP TS 38.331 (RRC) for SIB3/SIB4 and MeasConfig + * - 3GPP TS 38.413 (NGAP) / TS 38.423 (XnAP) for target cell identification */ typedef struct { + // gNB identifier (for target node in NGAP/XnAP handover) uint32_t gNB_ID; + // NR Cell Global Identifier (for NGAP/XnAP handover, DU validation) uint64_t nrcell_id; + // Physical Cell ID (PCI) (used in HandoverPreparationInformation and MeasObjectNR) int physicalCellId; + // SSB absolute frequency (ARFCN) (for MeasObjectNR, intra/inter-frequency determination) int absoluteFrequencySSB; + // SSB subcarrier spacing (for MeasObjectNR/SIB4) int subcarrierSpacing; + // Frequency band indicator (for MeasObjectNR/SIB4) int band; + // PLMN identity (for target node in NGAP/XnAP handover) plmn_id_t plmn; + // Tracking Area Code (for target node in NGAP/XnAP handover) uint32_t tac; // SIB3 (intra-frequency neighbor cell-specific offsets) nr_neighbour_cell_sib3_t sib3; From 3e515d9c9c2de1b5e0aef115944e92779b1fd6f2 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 3 Mar 2026 13:19:40 +0100 Subject: [PATCH 18/72] Tests: add SIB2/SIB3/SIB4 ASN.1 round-trip coverage Add encode/decode helpers and basic round-trip tests for NR SIB2, SIB3 and SIB4 in test_asn1_msg.cpp to ensure we produce decodable messages with expected fields. Extend SIB4 coverage with an inter_freq_carrier_freq_info_ranges test that exercises boundary and out-of-range values for carrier reselection parameters and checks encoder failures for invalid configurations. Changes: - Introduce encode_and_decode_sib2, encode_and_decode_sib3 and encode_and_decode_sib4 helpers that call do_SIB2_NR, do_SIB3_NR, do_SIB4_NR and uper_decode and assert non-empty, non-null byte arrays and successful decoding. - Add sib2_basic_encode_decode, sib3_basic_encode_decode and sib4_basic_encode_decode tests that populate minimal but representative NR_SIB2_t, NR_SIB3_t and NR_SIB4_t instances and verify selected decoded fields match the originals. - Add inter_freq_carrier_freq_info_ranges test that builds an NR_SIB4_t with NR_InterFreqCarrierFreqInfo_t and varies different offsets and thresholds to confirm valid combinations encode and invalid ones are rejected. --- .../RRC/NR/MESSAGES/tests/test_asn1_msg.cpp | 210 ++++++++++++++++++ 1 file changed, 210 insertions(+) diff --git a/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp b/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp index 4867d7f8ab..c14e9bdba0 100644 --- a/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp +++ b/openair2/RRC/NR/MESSAGES/tests/test_asn1_msg.cpp @@ -14,12 +14,61 @@ extern "C" { #include "NR_DRB-ToAddMod.h" #include "NR_DRB-ToAddModList.h" #include "NR_SRB-ToAddModList.h" +#include "NR_InterFreqCarrierFreqInfo.h" #include "ds/byte_array.h" +#include "NR_SIB2.h" +#include "NR_SIB3.h" +#include "NR_SIB4.h" +#include "NR_IntraFreqNeighCellInfo.h" +#include "NR_IntraFreqNeighCellList.h" +#include "NR_InterFreqCarrierFreqInfo.h" +#include "NR_InterFreqNeighCellInfo.h" +#include "NR_InterFreqNeighCellList.h" +#include "asn_application.h" RAN_CONTEXT_t RC; #ifdef __cplusplus } #endif +static NR_SIB2_t *encode_and_decode_sib2(const NR_SIB2_t *sib2, byte_array_t *ba_out) +{ + *ba_out = do_SIB2_NR(sib2); + EXPECT_GT(ba_out->len, 0); + EXPECT_NE(ba_out->buf, nullptr); + + NR_SIB2_t *decoded = nullptr; + asn_dec_rval_t dec = uper_decode(nullptr, &asn_DEF_NR_SIB2, (void **)&decoded, ba_out->buf, ba_out->len, 0, 0); + EXPECT_EQ(dec.code, RC_OK); + EXPECT_NE(decoded, nullptr); + return decoded; +} + +static NR_SIB3_t *encode_and_decode_sib3(const NR_SIB3_t *sib3, byte_array_t *ba_out) +{ + *ba_out = do_SIB3_NR(sib3); + EXPECT_GT(ba_out->len, 0); + EXPECT_NE(ba_out->buf, nullptr); + + NR_SIB3_t *decoded = nullptr; + asn_dec_rval_t dec = uper_decode(nullptr, &asn_DEF_NR_SIB3, (void **)&decoded, ba_out->buf, ba_out->len, 0, 0); + EXPECT_EQ(dec.code, RC_OK); + EXPECT_NE(decoded, nullptr); + return decoded; +} + +static NR_SIB4_t *encode_and_decode_sib4(NR_SIB4_t *sib4, byte_array_t *ba_out) +{ + *ba_out = do_SIB4_NR(sib4); + EXPECT_GT(ba_out->len, 0); + EXPECT_NE(ba_out->buf, nullptr); + + NR_SIB4_t *decoded = nullptr; + asn_dec_rval_t dec = uper_decode(nullptr, &asn_DEF_NR_SIB4, (void **)&decoded, ba_out->buf, ba_out->len, 0, 0); + EXPECT_EQ(dec.code, RC_OK); + EXPECT_NE(decoded, nullptr); + return decoded; +} + TEST(nr_asn1, rrc_reject) { unsigned char buf[1000]; @@ -138,6 +187,167 @@ TEST(nr_asn1, rrc_reconfiguration) free_RRCReconfiguration_params(params); } +TEST(nr_asn1, sib2_basic_encode_decode) +{ + NR_SIB2_t *sib2 = (NR_SIB2_t *)calloc_or_fail(1, sizeof(*sib2)); + sib2->cellReselectionInfoCommon.q_Hyst = NR_SIB2__cellReselectionInfoCommon__q_Hyst_dB0; + sib2->cellReselectionServingFreqInfo.cellReselectionPriority = 3; + sib2->cellReselectionServingFreqInfo.threshServingLowP = 10; + sib2->intraFreqCellReselectionInfo.q_RxLevMin = -56; + sib2->intraFreqCellReselectionInfo.s_IntraSearchP = 22; + sib2->intraFreqCellReselectionInfo.t_ReselectionNR = 1; + sib2->intraFreqCellReselectionInfo.deriveSSB_IndexFromCell = true; + + byte_array_t ba = {}; + NR_SIB2_t *decoded = encode_and_decode_sib2(sib2, &ba); + + EXPECT_EQ(decoded->cellReselectionServingFreqInfo.cellReselectionPriority, + sib2->cellReselectionServingFreqInfo.cellReselectionPriority); + EXPECT_EQ(decoded->cellReselectionServingFreqInfo.threshServingLowP, sib2->cellReselectionServingFreqInfo.threshServingLowP); + + ASN_STRUCT_FREE(asn_DEF_NR_SIB2, sib2); + ASN_STRUCT_FREE(asn_DEF_NR_SIB2, decoded); + free_byte_array(ba); +} + +TEST(nr_asn1, sib3_basic_encode_decode) +{ + NR_SIB3_t *sib3 = (NR_SIB3_t *)calloc_or_fail(1, sizeof(*sib3)); + sib3->intraFreqNeighCellList = (struct NR_IntraFreqNeighCellList *)calloc_or_fail(1, sizeof(*sib3->intraFreqNeighCellList)); + + NR_IntraFreqNeighCellInfo_t *cell = (NR_IntraFreqNeighCellInfo_t *)calloc_or_fail(1, sizeof(*cell)); + cell->physCellId = 100; + cell->q_OffsetCell = NR_Q_OffsetRange_dB0; + ASN_SEQUENCE_ADD(&sib3->intraFreqNeighCellList->list, cell); + + byte_array_t ba = {}; + NR_SIB3_t *decoded = encode_and_decode_sib3(sib3, &ba); + + ASSERT_NE(decoded->intraFreqNeighCellList, nullptr); + EXPECT_EQ(decoded->intraFreqNeighCellList->list.count, 1); + EXPECT_EQ(decoded->intraFreqNeighCellList->list.array[0]->physCellId, cell->physCellId); + + ASN_STRUCT_FREE(asn_DEF_NR_SIB3, sib3); + ASN_STRUCT_FREE(asn_DEF_NR_SIB3, decoded); + free_byte_array(ba); +} + +TEST(nr_asn1, sib4_basic_encode_decode) +{ + NR_SIB4_t *sib4 = (NR_SIB4_t *)calloc_or_fail(1, sizeof(*sib4)); + + NR_InterFreqCarrierFreqInfo_t *carrier = (NR_InterFreqCarrierFreqInfo_t *)calloc_or_fail(1, sizeof(*carrier)); + carrier->dl_CarrierFreq = 2000; + carrier->ssbSubcarrierSpacing = NR_SubcarrierSpacing_kHz15; + carrier->deriveSSB_IndexFromCell = true; + carrier->q_RxLevMin = -56; + carrier->t_ReselectionNR = 1; + carrier->threshX_HighP = 5; + carrier->threshX_LowP = 3; + carrier->interFreqNeighCellList = (struct NR_InterFreqNeighCellList *)calloc_or_fail(1, sizeof(*carrier->interFreqNeighCellList)); + + NR_InterFreqNeighCellInfo_t *ncell = (NR_InterFreqNeighCellInfo_t *)calloc_or_fail(1, sizeof(*ncell)); + ncell->physCellId = 200; + ncell->q_OffsetCell = NR_Q_OffsetRange_dB0; + ASN_SEQUENCE_ADD(&carrier->interFreqNeighCellList->list, ncell); + ASN_SEQUENCE_ADD(&sib4->interFreqCarrierFreqList.list, carrier); + + byte_array_t ba = {}; + NR_SIB4_t *decoded = encode_and_decode_sib4(sib4, &ba); + + ASSERT_GT(decoded->interFreqCarrierFreqList.list.count, 0); + EXPECT_EQ(decoded->interFreqCarrierFreqList.list.array[0]->dl_CarrierFreq, carrier->dl_CarrierFreq); + + ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4); + ASN_STRUCT_FREE(asn_DEF_NR_SIB4, decoded); + free_byte_array(ba); +} + +TEST(nr_asn1, inter_freq_carrier_freq_info_ranges) +{ + NR_SIB4_t *sib4 = (NR_SIB4_t *)calloc_or_fail(1, sizeof(*sib4)); + ASN_SEQUENCE_ADD(&sib4->interFreqCarrierFreqList.list, + (NR_InterFreqCarrierFreqInfo_t *)calloc_or_fail(1, sizeof(NR_InterFreqCarrierFreqInfo_t))); + + NR_InterFreqCarrierFreqInfo_t *carrier = sib4->interFreqCarrierFreqList.list.array[0]; + carrier->dl_CarrierFreq = 640000; + carrier->ssbSubcarrierSpacing = NR_SubcarrierSpacing_kHz30; + carrier->deriveSSB_IndexFromCell = 1; + carrier->threshX_HighP = 0; + carrier->threshX_LowP = 0; + + NR_Q_OffsetRange_t *qoff = (NR_Q_OffsetRange_t *)calloc_or_fail(1, sizeof(*qoff)); + *qoff = 15; // arbitrary valid Q-OffsetRange enum + carrier->q_OffsetFreq = qoff; + + // Optional threshX_Q struct + carrier->threshX_Q = (decltype(carrier->threshX_Q))calloc_or_fail(1, sizeof(*carrier->threshX_Q)); + carrier->threshX_Q->threshX_HighQ = 0; + carrier->threshX_Q->threshX_LowQ = 0; + + auto try_encode = [](NR_SIB4_t *s, bool expect_success) { + byte_array_t ba = do_SIB4_NR(s); + int len = (int)ba.len; + if (expect_success) { + EXPECT_GT(len, 0); + EXPECT_NE(ba.buf, nullptr); + } else { + EXPECT_LE(len, 0); + } + free_byte_array(ba); + }; + + // Valid lower bounds for all constrained scalars + carrier->q_RxLevMin = -70; + carrier->t_ReselectionNR = 0; + carrier->threshX_HighP = 0; + carrier->threshX_LowP = 0; + carrier->threshX_Q->threshX_HighQ = 0; + carrier->threshX_Q->threshX_LowQ = 0; + *qoff = 15; + try_encode(sib4, true); + + // Invalid q_RxLevMin (below ASN.1 range) + carrier->q_RxLevMin = -80; + carrier->t_ReselectionNR = 0; + try_encode(sib4, false); + + // Invalid t_ReselectionNR (above ASN.1 range) + carrier->q_RxLevMin = -70; + carrier->t_ReselectionNR = 8; + try_encode(sib4, false); + + // Restore valid q_RxLevMin / t_ReselectionNR + carrier->q_RxLevMin = -70; + carrier->t_ReselectionNR = 0; + + // Invalid threshX_HighP (below 0) + carrier->threshX_HighP = -1; + try_encode(sib4, false); + carrier->threshX_HighP = 0; + + // Invalid threshX_LowP (below 0) + carrier->threshX_LowP = -1; + try_encode(sib4, false); + carrier->threshX_LowP = 0; + + // Invalid threshX_HighQ (below 0) + carrier->threshX_Q->threshX_HighQ = -1; + try_encode(sib4, false); + carrier->threshX_Q->threshX_HighQ = 0; + + // Invalid threshX_LowQ (below 0) + carrier->threshX_Q->threshX_LowQ = -1; + try_encode(sib4, false); + carrier->threshX_Q->threshX_LowQ = 0; + + // Invalid q_OffsetFreq (outside [-24,24] enum range) + *qoff = 99; + try_encode(sib4, false); + + ASN_STRUCT_FREE(asn_DEF_NR_SIB4, sib4); +} + int main(int argc, char **argv) { logInit(); From c866078b2168f7e7c248b3c620d8a602ae4ae530 Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 7 Apr 2026 14:19:09 +0200 Subject: [PATCH 19/72] Tests: enable SIB2/SIB3/SIB4 example config in F1 rfsim CI Update the shared neighbour config used by the F1 rfsim pipeline to enable CU-provided SIB2 and neighbour-derived SIB3/SIB4 inputs. In `ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf` - Add `cu_sibs = (2, 3, 4) In `ci-scripts/conf_files/neighbour-config.conf`: - Add a complete `sib2_config` block - Add `frequency_list` / `frequency_config` for inter-frequency SIB4 fields - Extend both neighbour entries with per-cell offsets --- .../conf_files/gnb-cu.sa.band78.106prb.conf | 1 + ci-scripts/conf_files/neighbour-config.conf | 57 +++++++++++++++++++ 2 files changed, 58 insertions(+) diff --git a/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf b/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf index 015d77cacc..8460983d2b 100644 --- a/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf +++ b/ci-scripts/conf_files/gnb-cu.sa.band78.106prb.conf @@ -17,6 +17,7 @@ gNBs = // Tracking area code, 0x0000 and 0xfffe are reserved values tracking_area_code = 1; plmn_list = ({ mcc = 208; mnc = 99; mnc_length = 2; snssaiList = ({ sst = 1, sd = 0xffffff }) }); + cu_sibs = ( 2, 3, 4 ); @include "neighbour-config.conf" nr_cellid = 12345678L; diff --git a/ci-scripts/conf_files/neighbour-config.conf b/ci-scripts/conf_files/neighbour-config.conf index fe91ff2f08..57aa55f772 100644 --- a/ci-scripts/conf_files/neighbour-config.conf +++ b/ci-scripts/conf_files/neighbour-config.conf @@ -5,6 +5,57 @@ # for the 2-cell rfsim setup (gNB_ID 0xe00 & 0xb00) # ############################################################ +############################################################ +# SIB2 # +############################################################ + +sib2_config : { + q_Hyst = 0; + cellReselectionPriority = 0; + threshServingLowP = 0; + threshServingLowQ = 0; + s_NonIntraSearchP = -1; + s_NonIntraSearchQ = -1; + q_RxLevMin = -56; + q_QualMin = -1; + s_IntraSearchP = 22; + s_IntraSearchQ = -1; + t_ReselectionNR = 1; + deriveSSB_IndexFromCell = 1; + + speed_t_Evaluation = 0; + speed_t_HystNormal = 0; + speed_n_CellChangeMedium = 1; + speed_n_CellChangeHigh = 2; + speed_sf_Medium = 1; + speed_sf_High = 0; +}; + +############################################################ +# Per-frequency SIB4 inter-frequency configuration # +############################################################ + +frequency_list = ( + { + absoluteFrequencySSB = 621312; + subcarrierSpacing = 1; # 30 kHz + band = 78; + + frequency_config = ( + { + cellReselectionPriority = 5; + threshX_HighP = 10; + threshX_LowP = 6; + threshX_HighQ = -1; + threshX_LowQ = -1; + q_OffsetFreq = 0; + q_RxLevMin = -56; + t_ReselectionNR = 1; + } + ); + } +); + neighbour_list = ( ########################################################## # Entry USED BY gNB_ID = 0xe00 (nr_cellid = 1) # @@ -21,6 +72,9 @@ neighbour_list = ( band = 78; plmn = { mcc = 208; mnc = 99; mnc_length = 2 }; tracking_area_code = 1; + q_OffsetCell = 2; + q_RxLevMinOffsetCell= 2; + q_QualMinOffsetCell = 1; } ); }, @@ -40,6 +94,9 @@ neighbour_list = ( band = 78; plmn = { mcc = 208; mnc = 99; mnc_length = 2 }; tracking_area_code = 1; + q_OffsetCell = 2; + q_RxLevMinOffsetCell= 2; + q_QualMinOffsetCell = 1; } ); } From 10516db5ddbf21dc4126284264eb9487d0c3fe26 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 2 Apr 2026 17:36:05 +0200 Subject: [PATCH 20/72] fix yaml file Co-Authored-By: @Abdo-Gaber --- .../CONF/gnb.sa.band78.fr1.106PRB.usrpb210.redcap.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.redcap.yaml b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.redcap.yaml index b5b5708811..735c15ca1c 100644 --- a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.redcap.yaml +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.redcap.yaml @@ -21,7 +21,7 @@ gNBs: # Physical parameters: do_CSIRS: 1 do_SRS: 1 - first_active_bwp: 1; + first_active_bwp: 1 bwp_list: - scs: 1 From f414ae474cdf4c04075c7df303a9ab9cf802b667 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 2 Apr 2026 17:41:24 +0200 Subject: [PATCH 21/72] some cleanup in scheduler primitives file --- .../NR_MAC_gNB/gNB_scheduler_primitives.c | 78 ++++++++----------- 1 file changed, 34 insertions(+), 44 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..8a02933663 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -16,16 +16,12 @@ #include "UTIL/OPT/opt.h" #include "openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h" -#include "F1AP_CauseRadioNetwork.h" - #include "intertask_interface.h" #include "openair2/F1AP/f1ap_ids.h" #include "F1AP_CauseRadioNetwork.h" #include "T.h" - #include "uper_encoder.h" -#include "uper_decoder.h" #include "SIMULATION/TOOLS/sim.h" // for taus @@ -96,11 +92,6 @@ static const uint16_t cqi_table3[16][2] = {{0, 0}, {6, 6660}, {6, 7720}}; -static void determine_aggregation_level_search_order(int agg_level_search_order[NUM_PDCCH_AGG_LEVELS], - float pdcch_cl_adjust); - -static int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots, int slots_per_frame); - int get_ssbidx_from_beam(gNB_MAC_INST *mac, int beam_idx) { for (int i = 0; i < MAX_NUM_OF_SSB; i++) @@ -570,6 +561,21 @@ int find_pdcch_candidate(const gNB_MAC_INST *mac, return -1; } +/// @brief Orders PDCCH aggregation levels so that we first check desired aggregation level according to +/// pdcch_cl_adjust +/// @param agg_level_search_order in/out 5-element array of aggregation levels from 0 to 4 +/// @param pdcch_cl_adjust value from 0 to 1 indication channel impariments (0 - good channel, 1 - bad channel) +static void determine_aggregation_level_search_order(int agg_level_search_order[NUM_PDCCH_AGG_LEVELS], float pdcch_cl_adjust) +{ + int desired_agg_level_index = round(4 * pdcch_cl_adjust); + int agg_level_search_index = 0; + for (int i = desired_agg_level_index; i < NUM_PDCCH_AGG_LEVELS; i++) { + agg_level_search_order[agg_level_search_index++] = i; + } + for (int i = desired_agg_level_index - 1; i >= 0; i--) { + agg_level_search_order[agg_level_search_index++] = i; + } +} int get_cce_index(const gNB_MAC_INST *nrmac, const int CC_id, @@ -3448,6 +3454,25 @@ void nr_csirs_scheduling(int Mod_idP, frame_t frame, slot_t slot, nfapi_nr_dl_tt } } +static void nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots, int slots_per_frame) +{ + DevAssert(mac != NULL); + DevAssert(UE != NULL); + NR_SCHED_ENSURE_LOCKED(&mac->sched_lock); + + nr_timer_setup(&UE->UE_sched_ctrl.transm_interrupt, slots, 1); + nr_timer_start(&UE->UE_sched_ctrl.transm_interrupt); + + // it might happen that timing advance command should be sent during the UE inactivity time. + // To prevent this, delay next TA command just after the UE inactivity time. + const int inactive_frames = slots / slots_per_frame + 1; + if ((UE->UE_sched_ctrl.ta_frame - mac->frame + MAX_FRAME_NUMBER) % MAX_FRAME_NUMBER < inactive_frames) + UE->UE_sched_ctrl.ta_frame = (mac->frame + inactive_frames) % MAX_FRAME_NUMBER; + + LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots)\n", UE->rnti, slots); +} + + void nr_measgap_scheduling(gNB_MAC_INST *nr_mac, frame_t frame, sub_frame_t slot) { NR_SCHED_ENSURE_LOCKED(&nr_mac->sched_lock); @@ -3517,25 +3542,6 @@ int nr_mac_get_reconfig_delay_slots(NR_SubcarrierSpacing_t scs) return (delay_ms << scs) + sl_ahead; } -static int nr_mac_interrupt_ue_transmission(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots, int slots_per_frame) -{ - DevAssert(mac != NULL); - DevAssert(UE != NULL); - NR_SCHED_ENSURE_LOCKED(&mac->sched_lock); - - nr_timer_setup(&UE->UE_sched_ctrl.transm_interrupt, slots, 1); - nr_timer_start(&UE->UE_sched_ctrl.transm_interrupt); - - // it might happen that timing advance command should be sent during the UE inactivity time. - // To prevent this, delay next TA command just after the UE inactivity time. - const int inactive_frames = slots / slots_per_frame + 1; - if ((UE->UE_sched_ctrl.ta_frame - mac->frame + MAX_FRAME_NUMBER) % MAX_FRAME_NUMBER < inactive_frames) - UE->UE_sched_ctrl.ta_frame = (mac->frame + inactive_frames) % MAX_FRAME_NUMBER; - - LOG_D(NR_MAC, "UE %04x: Interrupt UE transmission (%d slots)\n", UE->rnti, slots); - return 0; -} - static void nr_mac_ue_transmission_timeout(gNB_MAC_INST *mac, NR_UE_info_t *UE, int slots) { DevAssert(mac != NULL); @@ -4120,22 +4126,6 @@ bool nr_mac_get_new_rnti(NR_UEs_t *UEs, rnti_t *rnti) return loop < 100; // nothing found: loop count 100 } -/// @brief Orders PDCCH aggregation levels so that we first check desired aggregation level according to -/// pdcch_cl_adjust -/// @param agg_level_search_order in/out 5-element array of aggregation levels from 0 to 4 -/// @param pdcch_cl_adjust value from 0 to 1 indication channel impariments (0 - good channel, 1 - bad channel) -static void determine_aggregation_level_search_order(int agg_level_search_order[NUM_PDCCH_AGG_LEVELS], float pdcch_cl_adjust) -{ - int desired_agg_level_index = round(4 * pdcch_cl_adjust); - int agg_level_search_index = 0; - for (int i = desired_agg_level_index; i < NUM_PDCCH_AGG_LEVELS; i++) { - agg_level_search_order[agg_level_search_index++] = i; - } - for (int i = desired_agg_level_index - 1; i >= 0; i--) { - agg_level_search_order[agg_level_search_index++] = i; - } -} - /// @brief Update PDCCH closed loop adjust for UE depending on detection of feedback. /// @param sched_ctrl UE scheduling control info /// @param feedback_not_detected Whether feedback (PUSCH or HARQ) was detected From 00c1976078b1f3234ef45a09be391ee84429eb62 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 2 Apr 2026 17:47:42 +0200 Subject: [PATCH 22/72] some cleanup in scheduler RA file --- openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c | 243 +++++++++--------- 1 file changed, 120 insertions(+), 123 deletions(-) diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index 6b2c9e23ac..3341455e45 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -7,7 +7,6 @@ */ #include "common/platform_types.h" -#include "uper_decoder.h" /* MAC */ #include "nr_mac_gNB.h" @@ -24,8 +23,6 @@ #include -static void nr_fill_rar(NR_UE_info_t *UE, uint8_t *dlsch_buffer, nfapi_nr_pusch_pdu_t *pusch_pdu); - static const float ssb_per_rach_occasion[8] = {0.125, 0.25, 0.5, 1, 2, 4, 8}; static int16_t ssb_index_from_prach(module_id_t module_idP, @@ -1027,6 +1024,126 @@ static bool get_feasible_msg3_tda(const NR_ServingCellConfigCommon_t *scc, return false; } +///////////////////////////////////// +// Random Access Response PDU // +// TS 38.213 ch 8.2 // +// TS 38.321 ch 6.2.3 // +///////////////////////////////////// +//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise +//| E | T | R A P I D |// +//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// +//| R | T A |// +//| T A | UL grant |// +//| UL grant |// +//| UL grant |// +//| UL grant |// +//| T C - R N T I |// +//| T C - R N T I |// +///////////////////////////////////// +// UL grant (27 bits) // +///////////////////////////////////// +//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise +//|-------------------|FHF|F_alloc|// +//| Freq allocation |// +//| F_alloc |Time allocation|// +//| MCS | TPC |CSI|// +///////////////////////////////////// +// WIP +// todo: +// - handle MAC RAR BI subheader +// - sending only 1 RAR subPDU +// - UL Grant: hardcoded CSI, TPC, time alloc +// - padding +static void nr_fill_rar(NR_UE_info_t *UE, uint8_t *dlsch_buffer, nfapi_nr_pusch_pdu_t *pusch_pdu) +{ + NR_RA_t *ra = UE->ra; + LOG_D(NR_MAC, + "[gNB] Generate RAR MAC PDU frame %d slot %d preamble index %u TA command %d \n", + ra->Msg2_frame, + ra->Msg2_slot, + ra->preamble_index, + ra->timing_offset); + NR_RA_HEADER_BI *rarbi = (NR_RA_HEADER_BI *) dlsch_buffer; + NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) (dlsch_buffer + 1); + NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 2); + unsigned char csi_req = 0; + + /// E/T/R/R/BI subheader /// + // E = 1, MAC PDU includes another MAC sub-PDU (RAPID) + // T = 0, Back-off indicator subheader + // R = 2, Reserved + // BI = 0, 5ms + rarbi->E = 1; + rarbi->T = 0; + rarbi->R = 0; + rarbi->BI = 0; + + /// E/T/RAPID subheader /// + // E = 0, one only RAR, first and last + // T = 1, RAPID + rarh->E = 0; + rarh->T = 1; + rarh->RAPID = ra->preamble_index; + + /// RAR MAC payload /// + rar->R = 0; + + // TA command + rar->TA1 = (uint8_t) (ra->timing_offset >> 5); // 7 MSBs of timing advance + rar->TA2 = (uint8_t) (ra->timing_offset & 0x1f); // 5 LSBs of timing advance + + // TC-RNTI + rar->TCRNTI_1 = (uint8_t) (UE->rnti >> 8); // 8 MSBs of rnti + rar->TCRNTI_2 = (uint8_t) (UE->rnti & 0xff); // 8 LSBs of rnti + + // UL grant + + if (pusch_pdu->frequency_hopping) + AssertFatal(1==0,"PUSCH with frequency hopping currently not supported"); + + int bwp_size = pusch_pdu->bwp_size; + int prb_alloc = PRBalloc_to_locationandbandwidth0(ra->msg3_nb_rb, ra->msg3_first_rb, bwp_size); + int valid_bits = 14; + int f_alloc = prb_alloc & ((1 << valid_bits) - 1); + + uint32_t ul_grant = csi_req | (ra->msg3_TPC << 1) | (pusch_pdu->mcs_index << 4) | (ra->Msg3_tda_id << 8) | (f_alloc << 12) | (pusch_pdu->frequency_hopping << 26); + + rar->UL_GRANT_1 = (uint8_t) (ul_grant >> 24) & 0x07; + rar->UL_GRANT_2 = (uint8_t) (ul_grant >> 16) & 0xff; + rar->UL_GRANT_3 = (uint8_t) (ul_grant >> 8) & 0xff; + rar->UL_GRANT_4 = (uint8_t) ul_grant & 0xff; + +#ifdef DEBUG_RAR + LOG_I(NR_MAC, "rarh->E = 0x%x\n", rarh->E); + LOG_I(NR_MAC, "rarh->T = 0x%x\n", rarh->T); + LOG_I(NR_MAC, "rarh->RAPID = 0x%x (%i)\n", rarh->RAPID, rarh->RAPID); + LOG_I(NR_MAC, "rar->R = 0x%x\n", rar->R); + LOG_I(NR_MAC, "rar->TA1 = 0x%x\n", rar->TA1); + LOG_I(NR_MAC, "rar->TA2 = 0x%x\n", rar->TA2); + LOG_I(NR_MAC, "rar->UL_GRANT_1 = 0x%x\n", rar->UL_GRANT_1); + LOG_I(NR_MAC, "rar->UL_GRANT_2 = 0x%x\n", rar->UL_GRANT_2); + LOG_I(NR_MAC, "rar->UL_GRANT_3 = 0x%x\n", rar->UL_GRANT_3); + LOG_I(NR_MAC, "rar->UL_GRANT_4 = 0x%x\n", rar->UL_GRANT_4); + LOG_I(NR_MAC, "rar->TCRNTI_1 = 0x%x\n", rar->TCRNTI_1); + LOG_I(NR_MAC, "rar->TCRNTI_2 = 0x%x\n", rar->TCRNTI_2); +#endif + LOG_D(NR_MAC, + "In %s: Transmitted RAR with t_alloc %d f_alloc %d ta_command %d mcs %d freq_hopping %d tpc_command %d csi_req %d t_crnti " + "%x \n", + __FUNCTION__, + rar->UL_GRANT_3 & 0x0f, + (rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12), + rar->TA2 + (rar->TA1 << 5), + rar->UL_GRANT_4 >> 4, + rar->UL_GRANT_1 >> 2, + ra->msg3_TPC, + csi_req, + rar->TCRNTI_2 + (rar->TCRNTI_1 << 8)); + + // resetting msg3 TPC to 0dB for possible retransmissions + ra->msg3_TPC = 1; +} + static bool nr_get_Msg3alloc(gNB_MAC_INST *mac, int CC_id, int current_slot, frame_t current_frame, NR_UE_info_t *UE) { NR_RA_t *ra = UE->ra; @@ -1964,126 +2081,6 @@ bool nr_check_Msg4_MsgB_Ack(module_id_t module_id, frame_t frame, slot_t slot, N return false; } -///////////////////////////////////// -// Random Access Response PDU // -// TS 38.213 ch 8.2 // -// TS 38.321 ch 6.2.3 // -///////////////////////////////////// -//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise -//| E | T | R A P I D |// -//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// -//| R | T A |// -//| T A | UL grant |// -//| UL grant |// -//| UL grant |// -//| UL grant |// -//| T C - R N T I |// -//| T C - R N T I |// -///////////////////////////////////// -// UL grant (27 bits) // -///////////////////////////////////// -//| 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 |// bit-wise -//|-------------------|FHF|F_alloc|// -//| Freq allocation |// -//| F_alloc |Time allocation|// -//| MCS | TPC |CSI|// -///////////////////////////////////// -// WIP -// todo: -// - handle MAC RAR BI subheader -// - sending only 1 RAR subPDU -// - UL Grant: hardcoded CSI, TPC, time alloc -// - padding -static void nr_fill_rar(NR_UE_info_t *UE, uint8_t *dlsch_buffer, nfapi_nr_pusch_pdu_t *pusch_pdu) -{ - NR_RA_t *ra = UE->ra; - LOG_D(NR_MAC, - "[gNB] Generate RAR MAC PDU frame %d slot %d preamble index %u TA command %d \n", - ra->Msg2_frame, - ra->Msg2_slot, - ra->preamble_index, - ra->timing_offset); - NR_RA_HEADER_BI *rarbi = (NR_RA_HEADER_BI *) dlsch_buffer; - NR_RA_HEADER_RAPID *rarh = (NR_RA_HEADER_RAPID *) (dlsch_buffer + 1); - NR_MAC_RAR *rar = (NR_MAC_RAR *) (dlsch_buffer + 2); - unsigned char csi_req = 0; - - /// E/T/R/R/BI subheader /// - // E = 1, MAC PDU includes another MAC sub-PDU (RAPID) - // T = 0, Back-off indicator subheader - // R = 2, Reserved - // BI = 0, 5ms - rarbi->E = 1; - rarbi->T = 0; - rarbi->R = 0; - rarbi->BI = 0; - - /// E/T/RAPID subheader /// - // E = 0, one only RAR, first and last - // T = 1, RAPID - rarh->E = 0; - rarh->T = 1; - rarh->RAPID = ra->preamble_index; - - /// RAR MAC payload /// - rar->R = 0; - - // TA command - rar->TA1 = (uint8_t) (ra->timing_offset >> 5); // 7 MSBs of timing advance - rar->TA2 = (uint8_t) (ra->timing_offset & 0x1f); // 5 LSBs of timing advance - - // TC-RNTI - rar->TCRNTI_1 = (uint8_t) (UE->rnti >> 8); // 8 MSBs of rnti - rar->TCRNTI_2 = (uint8_t) (UE->rnti & 0xff); // 8 LSBs of rnti - - // UL grant - - if (pusch_pdu->frequency_hopping) - AssertFatal(1==0,"PUSCH with frequency hopping currently not supported"); - - int bwp_size = pusch_pdu->bwp_size; - int prb_alloc = PRBalloc_to_locationandbandwidth0(ra->msg3_nb_rb, ra->msg3_first_rb, bwp_size); - int valid_bits = 14; - int f_alloc = prb_alloc & ((1 << valid_bits) - 1); - - uint32_t ul_grant = csi_req | (ra->msg3_TPC << 1) | (pusch_pdu->mcs_index << 4) | (ra->Msg3_tda_id << 8) | (f_alloc << 12) | (pusch_pdu->frequency_hopping << 26); - - rar->UL_GRANT_1 = (uint8_t) (ul_grant >> 24) & 0x07; - rar->UL_GRANT_2 = (uint8_t) (ul_grant >> 16) & 0xff; - rar->UL_GRANT_3 = (uint8_t) (ul_grant >> 8) & 0xff; - rar->UL_GRANT_4 = (uint8_t) ul_grant & 0xff; - -#ifdef DEBUG_RAR - LOG_I(NR_MAC, "rarh->E = 0x%x\n", rarh->E); - LOG_I(NR_MAC, "rarh->T = 0x%x\n", rarh->T); - LOG_I(NR_MAC, "rarh->RAPID = 0x%x (%i)\n", rarh->RAPID, rarh->RAPID); - LOG_I(NR_MAC, "rar->R = 0x%x\n", rar->R); - LOG_I(NR_MAC, "rar->TA1 = 0x%x\n", rar->TA1); - LOG_I(NR_MAC, "rar->TA2 = 0x%x\n", rar->TA2); - LOG_I(NR_MAC, "rar->UL_GRANT_1 = 0x%x\n", rar->UL_GRANT_1); - LOG_I(NR_MAC, "rar->UL_GRANT_2 = 0x%x\n", rar->UL_GRANT_2); - LOG_I(NR_MAC, "rar->UL_GRANT_3 = 0x%x\n", rar->UL_GRANT_3); - LOG_I(NR_MAC, "rar->UL_GRANT_4 = 0x%x\n", rar->UL_GRANT_4); - LOG_I(NR_MAC, "rar->TCRNTI_1 = 0x%x\n", rar->TCRNTI_1); - LOG_I(NR_MAC, "rar->TCRNTI_2 = 0x%x\n", rar->TCRNTI_2); -#endif - LOG_D(NR_MAC, - "In %s: Transmitted RAR with t_alloc %d f_alloc %d ta_command %d mcs %d freq_hopping %d tpc_command %d csi_req %d t_crnti " - "%x \n", - __FUNCTION__, - rar->UL_GRANT_3 & 0x0f, - (rar->UL_GRANT_3 >> 4) | (rar->UL_GRANT_2 << 4) | ((rar->UL_GRANT_1 & 0x03) << 12), - rar->TA2 + (rar->TA1 << 5), - rar->UL_GRANT_4 >> 4, - rar->UL_GRANT_1 >> 2, - ra->msg3_TPC, - csi_req, - rar->TCRNTI_2 + (rar->TCRNTI_1 << 8)); - - // resetting msg3 TPC to 0dB for possible retransmissions - ra->msg3_TPC = 1; -} - /** @brief remove the UE with RNTI rnti from list of UEs doing RA. * * The corresponding function to add is add_new_UE_RA(). */ From a1ab7321110a9c5338d17032286fed89164d6584 Mon Sep 17 00:00:00 2001 From: francescomani Date: Thu, 2 Apr 2026 19:01:07 +0200 Subject: [PATCH 23/72] adding separated NR_PHY_RU library split from LTE one --- CMakeLists.txt | 32 ++++++++++++------- common/utils/actor/actor.h | 2 +- doc/Doxyfile | 2 +- executables/lte-enb.c | 2 +- executables/lte-ru.c | 2 +- executables/nr-ru.c | 2 +- executables/ru_control.c | 2 +- openair1/PHY/INIT/nr_init_ru.c | 1 - openair1/PHY/LTE_TRANSPORT/prach.c | 4 +-- openair1/PHY/LTE_UE_TRANSPORT/prach_ue.c | 2 +- openair1/PHY/MODULATION/beamforming.c | 23 ------------- openair1/PHY/MODULATION/nr_beamforming.c | 26 +++++++++++++++ .../PHY/NR_ESTIMATION/nr_freq_equalization.c | 1 - openair1/PHY/defs_nr_UE.h | 4 +-- openair1/PHY/{LTE_TRANSPORT => }/if4_tools.c | 1 - openair1/PHY/{LTE_TRANSPORT => }/if4_tools.h | 2 +- openair1/SCHED/ru_procedures.c | 2 +- radio/ETHERNET/if_defs.h | 2 +- 18 files changed, 60 insertions(+), 52 deletions(-) create mode 100644 openair1/PHY/MODULATION/nr_beamforming.c rename openair1/PHY/{LTE_TRANSPORT => }/if4_tools.c (99%) rename openair1/PHY/{LTE_TRANSPORT => }/if4_tools.h (96%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e8758cea3..40aef3aa4f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -877,6 +877,10 @@ set(PHY_SRC ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/pilots_mbsfn.c ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/dlsch_coding.c ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/dlsch_modulation.c + ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/ulsch_demodulation.c + ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c + ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/freq_equalization.c + ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/lte_adjust_sync_eNB.c ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/dci_tools.c ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/pbch.c ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/dci.c @@ -893,22 +897,22 @@ set(PHY_SRC ) set(PHY_SRC_RU - ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/if4_tools.c + ${OPENAIR1_DIR}/PHY/if4_tools.c ${OPENAIR1_DIR}/PHY/LTE_UE_TRANSPORT/drs_modulation.c - ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/ulsch_demodulation.c - ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c - ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/lte_adjust_sync_eNB.c - ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/freq_equalization.c ${OPENAIR1_DIR}/PHY/MODULATION/slot_fep_ul.c - ${OPENAIR1_DIR}/PHY/MODULATION/slot_fep_nr.c ${OPENAIR1_DIR}/PHY/MODULATION/ul_7_5_kHz.c ${OPENAIR1_DIR}/PHY/MODULATION/gen_75KHz.cpp ${OPENAIR1_DIR}/PHY/MODULATION/beamforming.c ${OPENAIR1_DIR}/PHY/MODULATION/compute_bf_weights.c ${OPENAIR1_DIR}/PHY/INIT/lte_init_ru.c - ${OPENAIR1_DIR}/PHY/INIT/nr_init_ru.c ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/prach.c + ) +set(NR_PHY_SRC_RU + ${OPENAIR1_DIR}/PHY/MODULATION/nr_beamforming.c + ${OPENAIR1_DIR}/PHY/MODULATION/slot_fep_nr.c + ${OPENAIR1_DIR}/PHY/INIT/nr_init_ru.c + ${OPENAIR1_DIR}/PHY/if4_tools.c ) set(PHY_SRC_UE @@ -1080,6 +1084,8 @@ target_link_libraries(PHY_NR_UE PRIVATE asn1_nr_rrc_hdrs nr_phy_common nr_common add_library(PHY_RU ${PHY_SRC_RU}) target_link_libraries(PHY_RU PRIVATE asn1_lte_rrc_hdrs UTIL) +add_library(NR_PHY_RU ${NR_PHY_SRC_RU}) + #Layer 2 library ##################### set(MAC_DIR ${OPENAIR2_DIR}/LAYER2/MAC) @@ -1757,6 +1763,10 @@ add_executable(oairu ${OPENAIR_DIR}/executables/lte-ru.c ${OPENAIR_DIR}/executables/ru_control.c ${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c + ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/lte_ul_channel_estimation.c + ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/freq_equalization.c + ${OPENAIR1_DIR}/PHY/LTE_TRANSPORT/ulsch_demodulation.c + ${OPENAIR1_DIR}/PHY/LTE_ESTIMATION/lte_adjust_sync_eNB.c ${OPENAIR_DIR}/executables/main_ru.c ${OPENAIR_DIR}/common/utils/lte/prach_utils.c ) @@ -1812,7 +1822,7 @@ add_executable(nr-oru ${OPENAIR_DIR}/executables/main_nr_ru.c ) target_link_libraries(nr-oru PRIVATE - UTIL SCHED_RU_LIB PHY_COMMON PHY_RU PHY_NR shlib_loader z dl + UTIL SCHED_RU_LIB PHY_COMMON NR_PHY_RU PHY_NR shlib_loader z dl radio_common softmodem_common) target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils barrier actor) @@ -1836,7 +1846,7 @@ add_executable(nr-softmodem target_link_libraries(nr-softmodem PRIVATE -Wl,--start-group - UTIL SCTP_CLIENT SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON PHY_RU GTPV1U SECURITY + UTIL SCTP_CLIENT SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON NR_PHY_RU GTPV1U SECURITY ITTI ${NAS_UE_LIB} lte_rrc nr_rrc ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU x2ap f1ap m2ap m3ap e1ap radio_common @@ -2027,7 +2037,7 @@ target_link_libraries(nr_dlsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_prachsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/prachsim.c) target_link_libraries(nr_prachsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_RU PHY_NR_UE MAC_NR_COMMON SCHED_NR_LIB CONFIG_LIB -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR NR_PHY_RU PHY_NR_UE MAC_NR_COMMON SCHED_NR_LIB CONFIG_LIB -Wl,--end-group m pthread ${T_LIB} ITTI dl physim_common) target_link_libraries(nr_prachsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -2128,7 +2138,7 @@ if (${T_TRACER}) SECURITY SCHED_LIB SCHED_NR_LIB SCHED_RU_LIB SCHED_UE_LIB SCHED_NR_UE_LIB NFAPI_LIB NFAPI_PNF_LIB NFAPI_VNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB - PHY_COMMON PHY PHY_UE PHY_NR PHY_NR_COMMON PHY_NR_UE PHY_RU + PHY_COMMON PHY PHY_UE PHY_NR PHY_NR_COMMON PHY_NR_UE PHY_RU NR_PHY_RU L2 L2_LTE L2_NR L2_LTE_NR L2_UE NR_L2_UE MAC_NR_COMMON MAC_UE_NR ngap GTPV1U SCTP_CLIENT MME_APP LIB_NAS_UE SIMU dfts config_internals nr_common crc_byte) diff --git a/common/utils/actor/actor.h b/common/utils/actor/actor.h index 3d7760356c..d49bd88c65 100644 --- a/common/utils/actor/actor.h +++ b/common/utils/actor/actor.h @@ -4,7 +4,7 @@ #ifndef ACTOR_H #define ACTOR_H -#include "notified_fifo.h" +#include "common/utils/threadPool/notified_fifo.h" #define INIT_ACTOR(ptr, name, core_affinity) init_actor((Actor_t *)ptr, name, core_affinity); diff --git a/doc/Doxyfile b/doc/Doxyfile index fad5807544..7c09e6272e 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -1967,7 +1967,7 @@ INPUT = \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_TRANSPORT/dlsch_modulation.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_TRANSPORT/dci_NB_IoT.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_TRANSPORT/group_hopping.c \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_TRANSPORT/if4_tools.h \ +@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/if4_tools.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_TRANSPORT/uci_tools.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_TRANSPORT/prach.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/LTE_TRANSPORT/edci.c \ diff --git a/executables/lte-enb.c b/executables/lte-enb.c index b62815b70a..c735934c1a 100644 --- a/executables/lte-enb.c +++ b/executables/lte-enb.c @@ -20,7 +20,7 @@ #include "nfapi/oai_integration/vendor_ext.h" #include "radio/COMMON/common_lib.h" -#include "PHY/LTE_TRANSPORT/if4_tools.h" +#include "PHY/if4_tools.h" #include "PHY/LTE_ESTIMATION/lte_estimation.h" #include "PHY/phy_extern.h" diff --git a/executables/lte-ru.c b/executables/lte-ru.c index 91470d8f63..a0a82deabd 100644 --- a/executables/lte-ru.c +++ b/executables/lte-ru.c @@ -28,7 +28,7 @@ #include "PHY/phy_extern.h" #include "PHY/LTE_ESTIMATION/lte_estimation.h" #include "PHY/LTE_REFSIG/lte_refsig.h" -#include "PHY/LTE_TRANSPORT/if4_tools.h" +#include "PHY/if4_tools.h" #include "PHY/LTE_TRANSPORT/transport_proto.h" #include "SCHED/sched_common.h" #include "common/utils/LOG/log.h" diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 7554c8a9a6..ad2425f358 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -20,7 +20,7 @@ #include "radio/COMMON/common_lib.h" #include "radio/ETHERNET/ethernet_lib.h" -#include "PHY/LTE_TRANSPORT/if4_tools.h" +#include "PHY/if4_tools.h" #include "PHY/defs_nr_common.h" #include "PHY/phy_extern.h" diff --git a/executables/ru_control.c b/executables/ru_control.c index 11a3ef0f14..1852a67afb 100644 --- a/executables/ru_control.c +++ b/executables/ru_control.c @@ -27,7 +27,7 @@ #include "radio/COMMON/common_lib.h" #include "radio/ETHERNET/ethernet_lib.h" -#include "PHY/LTE_TRANSPORT/if4_tools.h" +#include "PHY/if4_tools.h" #include "PHY/LTE_TRANSPORT/transport_proto.h" #include "SCHED/sched_eNB.h" diff --git a/openair1/PHY/INIT/nr_init_ru.c b/openair1/PHY/INIT/nr_init_ru.c index 0cbb679f82..71ad18ab62 100644 --- a/openair1/PHY/INIT/nr_init_ru.c +++ b/openair1/PHY/INIT/nr_init_ru.c @@ -3,7 +3,6 @@ */ #include "PHY/phy_extern.h" -#include "SCHED/sched_common.h" #include "assertions.h" #include #include "openair1/PHY/defs_RU.h" diff --git a/openair1/PHY/LTE_TRANSPORT/prach.c b/openair1/PHY/LTE_TRANSPORT/prach.c index b821aa758e..6751f68da0 100644 --- a/openair1/PHY/LTE_TRANSPORT/prach.c +++ b/openair1/PHY/LTE_TRANSPORT/prach.c @@ -7,9 +7,7 @@ */ #include "PHY/sse_intrin.h" #include "PHY/defs_eNB.h" -//#include "PHY/phy_extern.h" -//#include "prach.h" -#include "PHY/LTE_TRANSPORT/if4_tools.h" +#include "PHY/if4_tools.h" #include "SCHED/sched_eNB.h" #include "common/utils/LOG/vcd_signal_dumper.h" diff --git a/openair1/PHY/LTE_UE_TRANSPORT/prach_ue.c b/openair1/PHY/LTE_UE_TRANSPORT/prach_ue.c index 62268ef930..b5abfdea92 100644 --- a/openair1/PHY/LTE_UE_TRANSPORT/prach_ue.c +++ b/openair1/PHY/LTE_UE_TRANSPORT/prach_ue.c @@ -10,7 +10,7 @@ #include "executables/lte-softmodem.h" #include "PHY/phy_extern_ue.h" //#include "prach.h" -#include "PHY/LTE_TRANSPORT/if4_tools.h" +#include "PHY/if4_tools.h" #include "SCHED_UE/sched_UE.h" #include "SCHED/sched_common_extern.h" diff --git a/openair1/PHY/MODULATION/beamforming.c b/openair1/PHY/MODULATION/beamforming.c index 56def37b41..f65a1bac48 100644 --- a/openair1/PHY/MODULATION/beamforming.c +++ b/openair1/PHY/MODULATION/beamforming.c @@ -9,7 +9,6 @@ #include "PHY/CODING/lte_interleaver_inline.h" #include "PHY/LTE_TRANSPORT/transport_eNB.h" #include "modulation_eNB.h" -#include "nr_modulation.h" #include "common/utils/LOG/vcd_signal_dumper.h" int beam_precoding(int32_t **txdataF, @@ -80,25 +79,3 @@ int beam_precoding_one_eNB(int32_t **txdataF, } return 0; } - - -void nr_beam_precoding(c16_t **txdataF, - c16_t **txdataF_BF, - NR_DL_FRAME_PARMS *frame_parms, - int32_t ***beam_weights, - int symbol, - int aa, - int nb_antenna_ports, - int offset) -{ - // clear txdata_BF[aa][re] for each call of ue_spec_beamforming - memset(&txdataF_BF[aa][symbol*frame_parms->ofdm_symbol_size], 0, sizeof(c16_t) *(frame_parms->ofdm_symbol_size)); - - for (int p = 0; p < nb_antenna_ports; p++) { - multadd_cpx_vector((c16_t *)&txdataF[p][(symbol * frame_parms->ofdm_symbol_size) + offset], - (c16_t *)beam_weights[p][aa], - (c16_t *)&txdataF_BF[aa][symbol * frame_parms->ofdm_symbol_size], - frame_parms->ofdm_symbol_size, - 15); - } -} diff --git a/openair1/PHY/MODULATION/nr_beamforming.c b/openair1/PHY/MODULATION/nr_beamforming.c new file mode 100644 index 0000000000..65084e401c --- /dev/null +++ b/openair1/PHY/MODULATION/nr_beamforming.c @@ -0,0 +1,26 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include "nr_modulation.h" + +void nr_beam_precoding(c16_t **txdataF, + c16_t **txdataF_BF, + NR_DL_FRAME_PARMS *frame_parms, + int32_t ***beam_weights, + int symbol, + int aa, + int nb_antenna_ports, + int offset) +{ + // clear txdata_BF[aa][re] for each call of ue_spec_beamforming + memset(&txdataF_BF[aa][symbol*frame_parms->ofdm_symbol_size], 0, sizeof(c16_t) *(frame_parms->ofdm_symbol_size)); + + for (int p = 0; p < nb_antenna_ports; p++) { + multadd_cpx_vector((c16_t *)&txdataF[p][(symbol * frame_parms->ofdm_symbol_size) + offset], + (c16_t *)beam_weights[p][aa], + (c16_t *)&txdataF_BF[aa][symbol * frame_parms->ofdm_symbol_size], + frame_parms->ofdm_symbol_size, + 15); + } +} diff --git a/openair1/PHY/NR_ESTIMATION/nr_freq_equalization.c b/openair1/PHY/NR_ESTIMATION/nr_freq_equalization.c index fd7530b0d9..75589c75f8 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_freq_equalization.c +++ b/openair1/PHY/NR_ESTIMATION/nr_freq_equalization.c @@ -2,7 +2,6 @@ * SPDX-License-Identifier: LicenseRef-CSSL-1.0 */ -#include "PHY/defs_eNB.h" #include "PHY/sse_intrin.h" #include "PHY/NR_ESTIMATION/nr_ul_estimation.h" diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 63771f7125..c0326693a2 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -28,8 +28,8 @@ #include "common_lib.h" #include "fapi_nr_ue_interface.h" #include "assertions.h" -#include "barrier.h" -#include "actor.h" +#include "common/utils/barrier/barrier.h" +#include "common/utils/actor/actor.h" //#include "openair1/SCHED_NR_UE/defs.h" #define msg(aRGS...) LOG_D(PHY, ##aRGS) diff --git a/openair1/PHY/LTE_TRANSPORT/if4_tools.c b/openair1/PHY/if4_tools.c similarity index 99% rename from openair1/PHY/LTE_TRANSPORT/if4_tools.c rename to openair1/PHY/if4_tools.c index 66097fc87f..5c5428db90 100644 --- a/openair1/PHY/LTE_TRANSPORT/if4_tools.c +++ b/openair1/PHY/if4_tools.c @@ -3,7 +3,6 @@ */ #include -//#include "PHY/defs_eNB.h" #include "PHY/defs_RU.h" #include "PHY/TOOLS/alaw_lut.h" //#include "PHY/phy_extern.h" diff --git a/openair1/PHY/LTE_TRANSPORT/if4_tools.h b/openair1/PHY/if4_tools.h similarity index 96% rename from openair1/PHY/LTE_TRANSPORT/if4_tools.h rename to openair1/PHY/if4_tools.h index 3cc30f7710..367ccfa859 100644 --- a/openair1/PHY/LTE_TRANSPORT/if4_tools.h +++ b/openair1/PHY/if4_tools.h @@ -4,7 +4,7 @@ #ifndef __IF4_TOOLS_H__ #define __IF4_TOOLS_H__ -#include "PHY/defs_eNB.h" +#include "openair1/PHY/defs_RU.h" /// Macro for IF4 packet type #define IF4p5_PACKET_TYPE 0x080A diff --git a/openair1/SCHED/ru_procedures.c b/openair1/SCHED/ru_procedures.c index 065c66d14c..8162d11699 100644 --- a/openair1/SCHED/ru_procedures.c +++ b/openair1/SCHED/ru_procedures.c @@ -10,7 +10,7 @@ #include "PHY/defs_eNB.h" #include "SCHED/sched_eNB.h" #include "PHY/MODULATION/modulation_eNB.h" -#include "PHY/LTE_TRANSPORT/if4_tools.h" +#include "PHY/if4_tools.h" #include "PHY/LTE_TRANSPORT/transport_common_proto.h" #include "PHY/LTE_TRANSPORT/transport_proto.h" #include "PHY/LTE_UE_TRANSPORT/transport_proto_ue.h" diff --git a/radio/ETHERNET/if_defs.h b/radio/ETHERNET/if_defs.h index e357858137..9e5ec6818e 100644 --- a/radio/ETHERNET/if_defs.h +++ b/radio/ETHERNET/if_defs.h @@ -9,7 +9,7 @@ #include #ifndef LITE_COMPILATION -#include "PHY/LTE_TRANSPORT/if4_tools.h" +#include "PHY/if4_tools.h" #endif // ETH transport preference modes From 4cda6ab959c361aa28d919fc68a11f10fd4c0431 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 3 Apr 2026 08:08:42 +0200 Subject: [PATCH 24/72] remove unnecessary PHY_COMMON library from NR targets --- CMakeLists.txt | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 40aef3aa4f..8a180c433a 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1822,7 +1822,7 @@ add_executable(nr-oru ${OPENAIR_DIR}/executables/main_nr_ru.c ) target_link_libraries(nr-oru PRIVATE - UTIL SCHED_RU_LIB PHY_COMMON NR_PHY_RU PHY_NR shlib_loader z dl + UTIL SCHED_RU_LIB NR_PHY_RU PHY_NR shlib_loader z dl radio_common softmodem_common) target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils barrier actor) @@ -1846,7 +1846,7 @@ add_executable(nr-softmodem target_link_libraries(nr-softmodem PRIVATE -Wl,--start-group - UTIL SCTP_CLIENT SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_COMMON PHY_NR_COMMON NR_PHY_RU GTPV1U SECURITY + UTIL SCTP_CLIENT SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_NR_COMMON NR_PHY_RU GTPV1U SECURITY ITTI ${NAS_UE_LIB} lte_rrc nr_rrc ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU x2ap f1ap m2ap m3ap e1ap radio_common @@ -1916,7 +1916,7 @@ add_executable(nr-uesoftmodem target_link_libraries(nr-uesoftmodem PRIVATE -Wl,--start-group nr_rrc SECURITY UTIL SCHED_RU_LIB SCHED_NR_UE_LIB - PHY_COMMON PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB + PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB ITTI SIMU radio_common time_management softmodem_common -Wl,--end-group z dl) @@ -1993,14 +1993,14 @@ target_link_libraries(physim_common PRIVATE UTIL) add_executable(nr_dlschsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/dlschsim.c) target_link_libraries(nr_dlschsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group m pthread ${T_LIB} ITTI dl physim_common ) target_link_libraries(nr_dlschsim PRIVATE asn1_nr_rrc_hdrs) add_executable(nr_pbchsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pbchsim.c) target_link_libraries(nr_pbchsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group m pthread ${T_LIB} ITTI dl physim_common ) target_link_libraries(nr_pbchsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -2010,14 +2010,14 @@ add_executable(nr_psbchsim ${NFAPI_USER_DIR}/nfapi.c ) target_link_libraries(nr_psbchsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB -Wl,--end-group m pthread ${T_LIB} ITTI dl physim_common softmodem_common ) target_link_libraries(nr_psbchsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_pucchsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/pucchsim.c) target_link_libraries(nr_pucchsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group m pthread ${T_LIB} ITTI dl physim_common ) target_link_libraries(nr_pucchsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -2029,7 +2029,7 @@ add_executable(nr_dlsim ${PHY_INTERFACE_DIR}/queue_t.c ) target_link_libraries(nr_dlsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common softmodem_common ) @@ -2037,7 +2037,7 @@ target_link_libraries(nr_dlsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) add_executable(nr_prachsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/prachsim.c) target_link_libraries(nr_prachsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR NR_PHY_RU PHY_NR_UE MAC_NR_COMMON SCHED_NR_LIB CONFIG_LIB -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR NR_PHY_RU PHY_NR_UE MAC_NR_COMMON SCHED_NR_LIB CONFIG_LIB -Wl,--end-group m pthread ${T_LIB} ITTI dl physim_common) target_link_libraries(nr_prachsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -2045,7 +2045,7 @@ add_executable(nr_ulschsim ${OPENAIR1_DIR}/SIMULATION/NR_PHY/ulschsim.c ) target_link_libraries(nr_ulschsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB CONFIG_LIB MAC_NR_COMMON -Wl,--end-group m pthread ${T_LIB} ITTI dl physim_common ) target_link_libraries(nr_ulschsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -2058,7 +2058,7 @@ add_executable(nr_ulsim ) target_link_libraries(nr_ulsim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common softmodem_common ) target_link_libraries(nr_ulsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -2069,7 +2069,7 @@ add_executable(nr_srssim ) target_link_libraries(nr_srssim PRIVATE - -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON -Wl,--end-group + -Wl,--start-group UTIL SIMU PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON -Wl,--end-group m pthread ITTI dl nr_ue_phy_meas physim_common softmodem_common ) From 08085af9432e4677240b001bacd2cff7de1f93dc Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 3 Apr 2026 08:26:21 +0200 Subject: [PATCH 25/72] move nr_ru_procedures to SCHED_NR_SRC to prevent including unnecessary SCHED_RU_LIB --- CMakeLists.txt | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8a180c433a..8af731fd75 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -709,6 +709,7 @@ target_link_libraries(SCHED_LIB PRIVATE asn1_lte_rrc_hdrs) set(SCHED_NR_SRC ${OPENAIR1_DIR}/SCHED_NR/phy_procedures_nr_gNB.c ${OPENAIR1_DIR}/SCHED_NR/nr_prach_procedures.c + ${OPENAIR1_DIR}/SCHED_NR/nr_ru_procedures.c ${OPENAIR1_DIR}/SCHED_NR/phy_frame_config_nr.c ) add_library(SCHED_NR_LIB ${SCHED_NR_SRC}) @@ -716,7 +717,6 @@ target_link_libraries(SCHED_NR_LIB PRIVATE asn1_nr_rrc_hdrs UTIL) set(SCHED_SRC_RU ${OPENAIR1_DIR}/SCHED/ru_procedures.c - ${OPENAIR1_DIR}/SCHED_NR/nr_ru_procedures.c ${OPENAIR1_DIR}/SCHED/prach_procedures.c ) add_library(SCHED_RU_LIB ${SCHED_SRC_RU}) @@ -1818,11 +1818,12 @@ add_executable(nr-oru ${OPENAIR_DIR}/openair1/PHY/INIT/nr_parms.c ${OPENAIR_DIR}/openair1/SCHED_NR/phy_frame_config_nr.c ${OPENAIR_DIR}/openair1/SCHED_NR/nr_prach_procedures.c + ${OPENAIR_DIR}/openair1/SCHED_NR/nr_ru_procedures.c ${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c ${OPENAIR_DIR}/executables/main_nr_ru.c ) target_link_libraries(nr-oru PRIVATE - UTIL SCHED_RU_LIB NR_PHY_RU PHY_NR shlib_loader z dl + UTIL NR_PHY_RU PHY_NR shlib_loader z dl radio_common softmodem_common) target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils barrier actor) @@ -1846,7 +1847,7 @@ add_executable(nr-softmodem target_link_libraries(nr-softmodem PRIVATE -Wl,--start-group - UTIL SCTP_CLIENT SCHED_LIB SCHED_RU_LIB SCHED_NR_LIB PHY_NR PHY PHY_NR_COMMON NR_PHY_RU GTPV1U SECURITY + UTIL SCTP_CLIENT SCHED_LIB SCHED_NR_LIB PHY_NR PHY PHY_NR_COMMON NR_PHY_RU GTPV1U SECURITY ITTI ${NAS_UE_LIB} lte_rrc nr_rrc ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU x2ap f1ap m2ap m3ap e1ap radio_common @@ -1915,7 +1916,7 @@ add_executable(nr-uesoftmodem target_link_libraries(nr-uesoftmodem PRIVATE -Wl,--start-group - nr_rrc SECURITY UTIL SCHED_RU_LIB SCHED_NR_UE_LIB + nr_rrc SECURITY UTIL SCHED_NR_UE_LIB PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB ITTI SIMU radio_common time_management softmodem_common From 850f03d32851271d20554f9a730033485bcaabcf Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 3 Apr 2026 08:30:50 +0200 Subject: [PATCH 26/72] remove lte_rrc from nr-softmodem --- CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 8af731fd75..2fa870ed69 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1848,7 +1848,7 @@ add_executable(nr-softmodem target_link_libraries(nr-softmodem PRIVATE -Wl,--start-group UTIL SCTP_CLIENT SCHED_LIB SCHED_NR_LIB PHY_NR PHY PHY_NR_COMMON NR_PHY_RU GTPV1U SECURITY - ITTI ${NAS_UE_LIB} lte_rrc nr_rrc + ITTI ${NAS_UE_LIB} nr_rrc ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU x2ap f1ap m2ap m3ap e1ap radio_common time_management softmodem_common From d45a1e209ba99978be8059f3e5e3b98826b85213 Mon Sep 17 00:00:00 2001 From: francescomani Date: Wed, 8 Apr 2026 09:36:16 +0200 Subject: [PATCH 27/72] verify consistency of CSI report in L2 instead of RRC at RRC we don't store CSI-MeasConfig information so in some scenarios (see #1063) we may not have the full pictures to understand if what RRC received is a valid configuration we verify at L2 instead after updating CSI-MeasConfig and if it fails we send a message to RRC --- openair2/COMMON/mac_messages_def.h | 1 + openair2/COMMON/mac_messages_types.h | 5 +++++ openair2/LAYER2/NR_MAC_UE/config_ue.c | 10 ++++++++-- openair2/RRC/NR_UE/L2_interface_ue.c | 7 +++++++ openair2/RRC/NR_UE/L2_interface_ue.h | 1 + openair2/RRC/NR_UE/rrc_UE.c | 6 ++++++ openair2/RRC/NR_UE/rrc_proto.h | 1 - openair2/RRC/NR_UE/verify_RRC.c | 4 +--- openair2/RRC/NR_UE/verify_RRC.h | 16 ++++++++++++++++ 9 files changed, 45 insertions(+), 6 deletions(-) create mode 100644 openair2/RRC/NR_UE/verify_RRC.h diff --git a/openair2/COMMON/mac_messages_def.h b/openair2/COMMON/mac_messages_def.h index cca53d38e6..bed2ad4243 100644 --- a/openair2/COMMON/mac_messages_def.h +++ b/openair2/COMMON/mac_messages_def.h @@ -31,6 +31,7 @@ MESSAGE_DEF(RRC_MAC_PCCH_DATA_REQ, MESSAGE_PRIORITY_MED_PLUS, RrcMacPcchDat MESSAGE_DEF(NR_RRC_MAC_RA_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacRaInd, nr_rrc_mac_ra_ind) MESSAGE_DEF(NR_RRC_MAC_MSG3_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacMsg3Ind, nr_rrc_mac_msg3_ind) MESSAGE_DEF(NR_RRC_MAC_INAC_IND, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacInacInd, nr_rrc_mac_inac_ind) +MESSAGE_DEF(NR_RRC_MAC_VERIFY, MESSAGE_PRIORITY_MED_PLUS, NRRrcMacVerify, nr_rrc_mac_verify) /* RRC configures DRX context (MAC timers) of a UE */ MESSAGE_DEF(RRC_MAC_DRX_CONFIG_REQ, MESSAGE_PRIORITY_MED, rrc_mac_drx_config_req_t, rrc_mac_drx_config_req) diff --git a/openair2/COMMON/mac_messages_types.h b/openair2/COMMON/mac_messages_types.h index c447379640..dc6770948f 100644 --- a/openair2/COMMON/mac_messages_types.h +++ b/openair2/COMMON/mac_messages_types.h @@ -41,6 +41,7 @@ #define NR_RRC_MAC_RA_IND(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_mac_ra_ind #define NR_RRC_MAC_MSG3_IND(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_mac_msg3_ind #define NR_RRC_MAC_INAC_IND(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_mac_inac_ind +#define NR_RRC_MAC_VERIFY(mSGpTR) (mSGpTR)->ittiMsg.nr_rrc_mac_verify #define RRC_MAC_DRX_CONFIG_REQ(mSGpTR) (mSGpTR)->ittiMsg.rrc_mac_drx_config_req @@ -67,6 +68,10 @@ typedef struct NRRrcMacMsg3Ind_s { bool prepare_payload; } NRRrcMacMsg3Ind; +typedef struct NRRrcMacVerify_s { + bool L2_RRC_verification_failed; // not to leave the struct empty +} NRRrcMacVerify; + typedef struct NRRrcMacInacInd_s { bool inactivity_timer_expired; // not to leave the struct empty } NRRrcMacInacInd; diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 6ecba2e923..d56b921b48 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -15,6 +15,8 @@ #include "common/utils/nr/nr_common.h" #include "executables/softmodem-common.h" #include "SCHED_NR/phy_frame_config_nr.h" +#include "RRC/NR_UE/verify_RRC.h" +#include "RRC/NR_UE/L2_interface_ue.h" #include "oai_asn1.h" #define ASIGN_P_VAL(dst, src) \ @@ -2552,7 +2554,9 @@ static void modify_csi_measconfig(NR_CSI_MeasConfig_t *source, NR_CSI_MeasConfig } } -static void configure_csiconfig(NR_UE_ServingCell_Info_t *sc_info, struct NR_SetupRelease_CSI_MeasConfig *csi_MeasConfig_sr) +static void configure_csiconfig(NR_UE_ServingCell_Info_t *sc_info, + struct NR_SetupRelease_CSI_MeasConfig *csi_MeasConfig_sr, + module_id_t ue_id) { switch (csi_MeasConfig_sr->present) { case NR_SetupRelease_CSI_MeasConfig_PR_NOTHING: @@ -2574,6 +2578,8 @@ static void configure_csiconfig(NR_UE_ServingCell_Info_t *sc_info, struct NR_Set } else { // modification modify_csi_measconfig(csi_MeasConfig_sr->choice.setup, sc_info->csi_MeasConfig); } + if (!check_csi_report_consistency(sc_info->csi_MeasConfig)) + nr_mac_rrc_verification_failed(ue_id); break; } default: @@ -2585,7 +2591,7 @@ static void configure_servingcell_info(NR_UE_MAC_INST_t *mac, NR_ServingCellConf { NR_UE_ServingCell_Info_t *sc_info = &mac->sc_info; if (scd->csi_MeasConfig) { - configure_csiconfig(sc_info, scd->csi_MeasConfig); + configure_csiconfig(sc_info, scd->csi_MeasConfig, mac->ue_id); compute_csi_bitlen(sc_info->csi_MeasConfig, mac->csi_report_template); } diff --git a/openair2/RRC/NR_UE/L2_interface_ue.c b/openair2/RRC/NR_UE/L2_interface_ue.c index b0b3b501cd..0133f81e03 100644 --- a/openair2/RRC/NR_UE/L2_interface_ue.c +++ b/openair2/RRC/NR_UE/L2_interface_ue.c @@ -155,6 +155,13 @@ void process_msg_rcc_to_mac(nr_mac_rrc_message_t *msg, int instance_id) } } +void nr_mac_rrc_verification_failed(const module_id_t mod_id) +{ + MessageDef *message_p = itti_alloc_new_message(TASK_MAC_UE, 0, NR_RRC_MAC_VERIFY); + NR_RRC_MAC_VERIFY (message_p).L2_RRC_verification_failed = true; + itti_send_msg_to_task(TASK_RRC_NRUE, GNB_MODULE_ID_TO_INSTANCE(mod_id), message_p); +} + void nr_mac_rrc_inactivity_timer_ind(const module_id_t mod_id) { MessageDef *message_p = itti_alloc_new_message(TASK_MAC_UE, 0, NR_RRC_MAC_INAC_IND); diff --git a/openair2/RRC/NR_UE/L2_interface_ue.h b/openair2/RRC/NR_UE/L2_interface_ue.h index 09c7b7daae..5121f0859e 100644 --- a/openair2/RRC/NR_UE/L2_interface_ue.h +++ b/openair2/RRC/NR_UE/L2_interface_ue.h @@ -30,5 +30,6 @@ void nr_mac_rrc_msg3_ind(const module_id_t mod_id, const int rnti, bool prepare_ void nr_ue_rrc_timer_trigger(int instance, int hfn, int frame, int gnb_id); void nr_mac_rrc_ra_ind(const module_id_t mod_id, bool success); void process_msg_rcc_to_mac(nr_mac_rrc_message_t *msg, int instance_id); +void nr_mac_rrc_verification_failed(const module_id_t mod_id); #endif diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index 37bca1d45a..5f0fefdcff 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -24,6 +24,7 @@ #include "rrc_defs.h" #include "rrc_proto.h" +#include "verify_RRC.h" #include "L2_interface_ue.h" #include "LAYER2/NR_MAC_UE/mac_proto.h" @@ -3115,6 +3116,11 @@ void *rrc_nrue(void *notUsed) } break; + case NR_RRC_MAC_VERIFY: + LOG_W(NR_RRC, "L2 verification of RRC consistency failed\n"); + handle_rlf_detection(rrc); + break; + case NR_RRC_MAC_INAC_IND: LOG_D(NR_RRC, "Received data inactivity indication from lower layers\n"); NR_Release_Cause_t release_cause = RRC_CONNECTION_FAILURE; diff --git a/openair2/RRC/NR_UE/rrc_proto.h b/openair2/RRC/NR_UE/rrc_proto.h index 5efd15306a..f4bf1b4e1e 100644 --- a/openair2/RRC/NR_UE/rrc_proto.h +++ b/openair2/RRC/NR_UE/rrc_proto.h @@ -72,7 +72,6 @@ void nr_rrc_ue_decode_NR_SBCCH_SL_BCH_Message(NR_UE_RRC_INST_t *rrc, const uint16_t rx_slss_id); void nr_rrc_set_mac_queue(instance_t instance, notifiedFIFO_t *mac_input_nf); -bool check_cellgroup_config(const NR_CellGroupConfig_t *cgConfig); /** @}*/ #endif diff --git a/openair2/RRC/NR_UE/verify_RRC.c b/openair2/RRC/NR_UE/verify_RRC.c index 9d5cd5d726..42d3204d27 100644 --- a/openair2/RRC/NR_UE/verify_RRC.c +++ b/openair2/RRC/NR_UE/verify_RRC.c @@ -188,7 +188,7 @@ static bool check_csi_resourceMapping_consistency(NR_CSI_RS_ResourceMapping_t re return true; } -static bool check_csi_report_consistency(const NR_CSI_MeasConfig_t *meas) +bool check_csi_report_consistency(const NR_CSI_MeasConfig_t *meas) { if (!meas || !meas->csi_ReportConfigToAddModList) return true; @@ -223,8 +223,6 @@ static bool check_csi_resource_consistency(const NR_CSI_MeasConfig_t *meas) static bool check_csi_MeasConfig(struct NR_SetupRelease_CSI_MeasConfig *csi_MeasConfig) { const NR_CSI_MeasConfig_t *meas = csi_MeasConfig->choice.setup; - if (!check_csi_report_consistency(meas)) - return false; if (!check_csi_resource_consistency(meas)) return false; return true; diff --git a/openair2/RRC/NR_UE/verify_RRC.h b/openair2/RRC/NR_UE/verify_RRC.h new file mode 100644 index 0000000000..e4de684402 --- /dev/null +++ b/openair2/RRC/NR_UE/verify_RRC.h @@ -0,0 +1,16 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +/* + * \brief RRC functions prototypes for eNB and UE + */ + +#ifndef _VERIFY_RRC_H_ +#define _VERIFY_RRC_H_ + +#include "NR_MeasConfig.h" + +bool check_csi_report_consistency(const NR_CSI_MeasConfig_t *meas); +bool check_cellgroup_config(const NR_CellGroupConfig_t *cgConfig); +#endif From 82f9501424ae2c371fb249e960f8360ee33a5cde Mon Sep 17 00:00:00 2001 From: francescomani Date: Sat, 11 Apr 2026 18:07:15 +0200 Subject: [PATCH 28/72] include NR_L2_UE in dlsim and ulsim and get rid of indication functions from nr_unitary_common --- CMakeLists.txt | 4 +- .../SIMULATION/NR_PHY/nr_unitary_common.c | 80 ------------------- .../tests/test_nr_ue_ra_procedures.cpp | 8 +- 3 files changed, 9 insertions(+), 83 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e8758cea3..bc429cacb6 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2020,7 +2020,7 @@ add_executable(nr_dlsim ) target_link_libraries(nr_dlsim PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group - m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common + m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common NR_L2_UE softmodem_common ) target_link_libraries(nr_dlsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -2049,7 +2049,7 @@ add_executable(nr_ulsim target_link_libraries(nr_ulsim PRIVATE -Wl,--start-group UTIL SIMU PHY_COMMON PHY_NR_COMMON PHY_NR PHY_NR_UE SCHED_NR_LIB SCHED_NR_UE_LIB MAC_UE_NR MAC_NR_COMMON CONFIG_LIB L2_NR -Wl,--end-group - m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common softmodem_common + m pthread ${T_LIB} ITTI dl nr_ue_phy_meas physim_common softmodem_common NR_L2_UE ) target_link_libraries(nr_ulsim PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) diff --git a/openair1/SIMULATION/NR_PHY/nr_unitary_common.c b/openair1/SIMULATION/NR_PHY/nr_unitary_common.c index 9a595a9295..3c0e2ee87c 100644 --- a/openair1/SIMULATION/NR_PHY/nr_unitary_common.c +++ b/openair1/SIMULATION/NR_PHY/nr_unitary_common.c @@ -136,83 +136,3 @@ int nfapi_p7_message_header_unpack(void *pMessageBuf, UNUSED(config); return 0; } - -void nr_mac_rrc_sync_ind(const module_id_t module_id, const frame_t frame, const bool in_sync) -{ - UNUSED(module_id); - UNUSED(frame); - UNUSED(in_sync); -} - -void nr_mac_rrc_msg3_ind(const module_id_t mod_id, int rnti, int gnb_id) -{ - UNUSED(mod_id); - UNUSED(gnb_id); - UNUSED(rnti); -} - -void nr_mac_rrc_ra_ind(const module_id_t mod_id, bool success) -{ - UNUSED(mod_id); - UNUSED(success); -} - -void nr_mac_rrc_inactivity_timer_ind(const module_id_t mod_id) -{ - UNUSED(mod_id); -} - -void rrc_data_ind(const protocol_ctxt_t *const ctxt_pP, - const rb_id_t Srb_id, - const sdu_size_t sdu_sizeP, - const uint8_t *const buffer_pP) -{ - UNUSED(ctxt_pP); - UNUSED(Srb_id); - UNUSED(sdu_sizeP); - UNUSED(buffer_pP); -} - -typedef uint32_t channel_t; -int8_t nr_mac_rrc_data_ind_ue(const module_id_t module_id, - const int CC_id, - const uint8_t gNB_index, - const int hfn, - const frame_t frame, - const int slot, - const rnti_t rnti, - const uint32_t cellid, - const long arfcn, - const channel_t channel, - const uint8_t *pduP, - const sdu_size_t pdu_len) -{ - UNUSED(module_id); - UNUSED(CC_id); - UNUSED(gNB_index); - UNUSED(hfn); - UNUSED(frame); - UNUSED(slot); - UNUSED(rnti); - UNUSED(arfcn); - UNUSED(channel); - UNUSED(pduP); - UNUSED(pdu_len); - UNUSED(cellid); - return 0; -} - -void nr_mac_rrc_meas_ind_ue(module_id_t module_id, - uint32_t gNB_index, - uint16_t Nid_cell, - bool csi_meas, - bool is_neighboring_cell, - int rsrp_dBm) -{ - UNUSED(module_id); - UNUSED(Nid_cell); - UNUSED(gNB_index); - UNUSED(csi_meas); - UNUSED(is_neighboring_cell); - UNUSED(rsrp_dBm); -} diff --git a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_ra_procedures.cpp b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_ra_procedures.cpp index ae79c87aee..0fb34a9a37 100644 --- a/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_ra_procedures.cpp +++ b/openair2/LAYER2/NR_MAC_UE/tests/test_nr_ue_ra_procedures.cpp @@ -42,7 +42,9 @@ void nr_mac_rlc_data_ind(const module_id_t module_idP, int num_data) { } - +void nr_mac_rrc_verification_failed(const module_id_t mod_id) +{ +} bool nr_rlc_activate_srb0(int ue_id, void *data, void (*send_initial_ul_rrc_message)(int ue_id, const uint8_t *sdu, sdu_size_t sdu_len, void *data)) @@ -77,6 +79,10 @@ int8_t nr_mac_rrc_data_ind_ue(const module_id_t module_id, { return 0; } +bool check_csi_report_consistency(const NR_CSI_MeasConfig_t *meas) +{ + return true; +} void nr_mac_rrc_meas_ind_ue(module_id_t module_id, uint32_t gNB_index, uint16_t Nid_cell, From 90ec531554b95e8635140b09de6446c9572d8bb4 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 16 Nov 2025 16:25:05 +0100 Subject: [PATCH 29/72] Hashtable: remove unused code Signed-off-by: Robert Schmidt --- common/utils/ds/hashtable.c | 65 ------------------------------------- common/utils/ds/hashtable.h | 2 -- 2 files changed, 67 deletions(-) diff --git a/common/utils/ds/hashtable.c b/common/utils/ds/hashtable.c index c84d332973..78a62f570c 100644 --- a/common/utils/ds/hashtable.c +++ b/common/utils/ds/hashtable.c @@ -10,36 +10,6 @@ #include "assertions.h" -//------------------------------------------------------------------------------------------------------------------------------- -char *hashtable_rc_code2string(hashtable_rc_t rcP) -//------------------------------------------------------------------------------------------------------------------------------- -{ - switch (rcP) { - case HASH_TABLE_OK: - return "HASH_TABLE_OK"; - break; - - case HASH_TABLE_INSERT_OVERWRITTEN_DATA: - return "HASH_TABLE_INSERT_OVERWRITTEN_DATA"; - break; - - case HASH_TABLE_KEY_NOT_EXISTS: - return "HASH_TABLE_KEY_NOT_EXISTS"; - break; - - case HASH_TABLE_KEY_ALREADY_EXISTS: - return "HASH_TABLE_KEY_ALREADY_EXISTS"; - break; - - case HASH_TABLE_BAD_PARAMETER_HASHTABLE: - return "HASH_TABLE_BAD_PARAMETER_HASHTABLE"; - break; - - default: - return "UNKNOWN hashtable_rc_t"; - } -} -//------------------------------------------------------------------------------------------------------------------------------- /* * free int function * hash_free_int_func() is used when this hashtable is used to store int values as data (pointer = value). @@ -147,41 +117,6 @@ hashtable_rc_t hashtable_is_key_exists (const hash_table_t *const hashtblP, cons return HASH_TABLE_KEY_NOT_EXISTS; } -//------------------------------------------------------------------------------------------------------------------------------- -hashtable_rc_t hashtable_dump_content (const hash_table_t *const hashtblP, char *const buffer_pP, int *const remaining_bytes_in_buffer_pP ) -//------------------------------------------------------------------------------------------------------------------------------- -{ - hash_node_t *node = NULL; - unsigned int i = 0; - - if (hashtblP == NULL) { - *remaining_bytes_in_buffer_pP = snprintf( - buffer_pP, - *remaining_bytes_in_buffer_pP, - "HASH_TABLE_BAD_PARAMETER_HASHTABLE"); - return HASH_TABLE_BAD_PARAMETER_HASHTABLE; - } - - while ((i < hashtblP->size) && (*remaining_bytes_in_buffer_pP > 0)) { - if (hashtblP->nodes[i] != NULL) { - node=hashtblP->nodes[i]; - - while(node) { - *remaining_bytes_in_buffer_pP = snprintf( - buffer_pP, - *remaining_bytes_in_buffer_pP, - "Key 0x%"PRIx64" Element %p\n", - node->key, - node->data); - node=node->next; - } - } - - i += 1; - } - - return HASH_TABLE_OK; -} //------------------------------------------------------------------------------------------------------------------------------- /* diff --git a/common/utils/ds/hashtable.h b/common/utils/ds/hashtable.h index ba43b0230f..fd8fe013c3 100644 --- a/common/utils/ds/hashtable.h +++ b/common/utils/ds/hashtable.h @@ -44,12 +44,10 @@ typedef struct hash_table_iterator_s { const hash_table_t* const hashtbl; } hash_table_iterator_s; -char *hashtable_rc_code2string(hashtable_rc_t rcP); void hash_free_int_func(void *memoryP); hash_table_t *hashtable_create (const hash_size_t size, hash_size_t (*hashfunc)(const hash_key_t ), void (*freefunc)(void *)); hashtable_rc_t hashtable_destroy(hash_table_t **hashtbl); hashtable_rc_t hashtable_is_key_exists (const hash_table_t *const hashtbl, const uint64_t key); -hashtable_rc_t hashtable_dump_content (const hash_table_t *const hashtblP, char *const buffer_pP, int *const remaining_bytes_in_buffer_pP ); hashtable_rc_t hashtable_insert (hash_table_t *const hashtbl, const hash_key_t key, void *data); hashtable_rc_t hashtable_remove (hash_table_t *const hashtbl, const hash_key_t key); hashtable_rc_t hashtable_get (const hash_table_t *const hashtbl, const hash_key_t key, void **dataP); From d33466d632bf3c757dcdbd2b4a06c59f94b2e071 Mon Sep 17 00:00:00 2001 From: Aswanth KC Date: Wed, 8 Apr 2026 19:50:50 +0000 Subject: [PATCH 30/72] fix: issue 911 - replace timer oneshot to periodic. Updated SCTP reconnect timer from TIMER_ONE_SHOT to TIMER_PERIODIC so gNB continuously retries AMF lookup every 30 seconds after disconnect. Timer is now removed only when ngap_amf_associated_nb > 0, AMF association is actually back. --- openair3/NGAP/ngap_gNB_handlers.c | 12 ++++++++++-- openair3/NGAP/ngap_gNB_ue_context.c | 2 +- 2 files changed, 11 insertions(+), 3 deletions(-) diff --git a/openair3/NGAP/ngap_gNB_handlers.c b/openair3/NGAP/ngap_gNB_handlers.c index 754a2e2f86..78bc0b3de4 100644 --- a/openair3/NGAP/ngap_gNB_handlers.c +++ b/openair3/NGAP/ngap_gNB_handlers.c @@ -68,9 +68,17 @@ void ngap_handle_ng_setup_message(ngap_gNB_amf_data_t *amf_desc_p, int sctp_shut } } } else { + if (amf_desc_p->t_reconnect != -1 && amf_desc_p->ngap_gNB_instance->ngap_amf_associated_nb > 0) { + timer_remove(amf_desc_p->t_reconnect); + amf_desc_p->t_reconnect = -1; + NGAP_INFO("reconnected to AMF\n"); + } + /* Check that at least one setup message is pending */ - DevCheck(amf_desc_p->ngap_gNB_instance->ngap_amf_pending_nb > 0, amf_desc_p->ngap_gNB_instance->instance, - amf_desc_p->ngap_gNB_instance->ngap_amf_pending_nb, 0); + DevCheck(amf_desc_p->ngap_gNB_instance->ngap_amf_pending_nb > 0, + amf_desc_p->ngap_gNB_instance->instance, + amf_desc_p->ngap_gNB_instance->ngap_amf_pending_nb, + 0); if (amf_desc_p->ngap_gNB_instance->ngap_amf_pending_nb > 0) { /* Decrease pending messages number */ diff --git a/openair3/NGAP/ngap_gNB_ue_context.c b/openair3/NGAP/ngap_gNB_ue_context.c index 7555685afb..57856a890f 100644 --- a/openair3/NGAP/ngap_gNB_ue_context.c +++ b/openair3/NGAP/ngap_gNB_ue_context.c @@ -94,7 +94,7 @@ void ngap_release_ues_for_amf(ngap_gNB_amf_data_t *amf_desc_p) 0, TASK_NGAP, amf_desc_p->ngap_gNB_instance->instance, - TIMER_ONE_SHOT, + TIMER_PERIODIC, amf_desc_p, &tid); if (rc == 0) { From 9fe14b784fc810e8db56f9f3cca5a5b6c4751af2 Mon Sep 17 00:00:00 2001 From: Sagar Arora Date: Mon, 13 Apr 2026 11:48:06 +0200 Subject: [PATCH 31/72] fix(record_db doc): correct the path for T_messages.txt and mention use of ninja/make Signed-off-by: Sagar Arora --- common/utils/T/DOC/T/basic.md | 12 ++++++++++-- common/utils/T/DOC/T/record_db.md | 10 +++++++--- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/common/utils/T/DOC/T/basic.md b/common/utils/T/DOC/T/basic.md index 0103c30de7..ff54c7efc6 100644 --- a/common/utils/T/DOC/T/basic.md +++ b/common/utils/T/DOC/T/basic.md @@ -67,18 +67,26 @@ the T tracer and only output on the terminal). The default is ## Run a tracer -Go into the directory `common/utils/T/tracer` (or -`cmake_targets/ran_build/build/common/utils/T/tracer`) and run for example the +Go into the directory `common/utils/T/tracer` and run for example the `enb` tracer: ```shell +#in common/utils/T/tracer directory ./enb -d ../T_messages.txt ``` +You can also run it from `cmake_targets/ran_build/build/` directory: + +```shell +# in cmake_targets/ran_build/build directory +./common/utils/T/tracer/enb -d ../../../common/utils/T/T_messages.txt +``` + To trace a remote program, use the `-ip` option. For example, if you want to trace a program running on `192.168.12.148` do: ```shell +# in common/utils/T/tracer directory ./enb -d ../T_messages.txt -ip 192.168.12.148 ``` diff --git a/common/utils/T/DOC/T/record_db.md b/common/utils/T/DOC/T/record_db.md index 2c3825252f..27444fa87c 100644 --- a/common/utils/T/DOC/T/record_db.md +++ b/common/utils/T/DOC/T/record_db.md @@ -44,6 +44,7 @@ Or, from an existing build directory using cmake: ```shell cd cmake_targets/ran_build/build cmake -DT_RECORD_DB=ON . +# use make or ninja make T_tools ``` @@ -53,6 +54,9 @@ The binary is then available in: cmake_targets/ran_build/build/common/utils/T/tracer/record_db ``` +**NOTE*: Unlike other docs on T Tracer for `record_db` we are building in cmake +directory rather than in the directory of T. + ## Prerequisites - a running ClickHouse server reachable from `record_db` @@ -84,8 +88,8 @@ To enable all events except the most verbose groups and write them to ClickHouse: ```shell -cd cmake_targets/ran_build/build/common/utils/T/tracer -./record_db -d ../T_messages.txt \ +cd cmake_targets/ran_build/build/ +./common/utils/T/tracer/record_db -d ../../../common/utils/T/T_messages.txt \ -ON \ -off VCD \ -off HEAVY \ @@ -108,7 +112,7 @@ The event selection rules are the same as for [`record`](./record.md): These options are processed in order. For example: ```shell -./record_db -d ../T_messages.txt -OFF -on GNB_PHY_UL_FD_PUSCH_IQ -on GNB_PHY_UL_PAYLOAD_RX_BITS +./common/utils/T/tracer/record_db -d ../../../common/utils/T/T_messages.txt -OFF -on GNB_PHY_UL_FD_PUSCH_IQ -on GNB_PHY_UL_PAYLOAD_RX_BITS ``` This starts from everything disabled and enables only the two listed From 45ae5f19e498ab4696fb9890034e28cf44eb436f Mon Sep 17 00:00:00 2001 From: Shubhika Garg Date: Mon, 16 Mar 2026 16:41:36 +0100 Subject: [PATCH 32/72] chore: Update core network images to v2.2.1 release tag Signed-off-by: Shubhika Garg --- ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml | 6 +++--- .../yaml_files/5g_fdd_rfsimulator/docker-compose.yaml | 6 +++--- ci-scripts/yaml_files/5g_rfsimulator/README.md | 6 +++--- ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml | 6 +++--- .../yaml_files/5g_rfsimulator_24prb/docker-compose.yaml | 6 +++--- .../yaml_files/5g_rfsimulator_2x2/docker-compose.yaml | 6 +++--- ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml | 6 +++--- .../yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml | 6 +++--- .../yaml_files/5g_rfsimulator_multiue/docker-compose.yaml | 6 +++--- .../yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml | 6 +++--- .../yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml | 6 +++--- .../yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml | 6 +++--- .../yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml | 6 +++--- ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml | 6 +++--- ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml | 6 +++--- ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml | 6 +++--- 16 files changed, 48 insertions(+), 48 deletions(-) diff --git a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml index 58b5748668..798315a2b5 100644 --- a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml index f43c8aaa03..8b69688966 100644 --- a/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_rfsimulator/README.md b/ci-scripts/yaml_files/5g_rfsimulator/README.md index d2ef888771..e839b3ea30 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/README.md +++ b/ci-scripts/yaml_files/5g_rfsimulator/README.md @@ -39,9 +39,9 @@ Now pull images. ```bash $ docker pull mysql:8.0 -$ docker pull oaisoftwarealliance/oai-amf:v2.1.10 -$ docker pull oaisoftwarealliance/oai-smf:v2.1.10 -$ docker pull oaisoftwarealliance/oai-upf:v2.1.10 +$ docker pull oaisoftwarealliance/oai-amf:v2.2.1 +$ docker pull oaisoftwarealliance/oai-smf:v2.2.1 +$ docker pull oaisoftwarealliance/oai-upf:v2.2.1 $ docker pull oaisoftwarealliance/trf-gen-cn5g:focal $ docker pull oaisoftwarealliance/oai-gnb:develop diff --git a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml index cf7d5b5f85..a3bed3795d 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml index 299ebbc543..3f051e718f 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml index 8b4c02da2e..8c10c43805 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml index 0f03a33c82..de841dcd2a 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml index 9ed90c8b2d..5e6aa20d32 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml index 5e376f650e..5fcff71c8d 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml @@ -23,7 +23,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -35,7 +35,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -47,7 +47,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 environment: - TZ=Europe/Paris volumes: diff --git a/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml index 9fe4bdc25f..61e84260c1 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml @@ -23,7 +23,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:ci-tmp-pr-372-26c14bcd + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -35,7 +35,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.9 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -47,7 +47,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.9 + image: oaisoftwarealliance/oai-upf:v2.2.1 environment: - TZ=Europe/Paris volumes: diff --git a/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml index 785402c3f7..bb6342d3df 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml index fd57881969..d86a3abb44 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml @@ -25,7 +25,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -37,7 +37,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -49,7 +49,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 environment: - TZ=Europe/Paris volumes: diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml index 581c2362fe..7b3a509be8 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml b/ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml index 357444be73..f9dfbdeb2d 100644 --- a/ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml @@ -26,7 +26,7 @@ services: ipv4_address: 192.168.71.131 oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -38,7 +38,7 @@ services: ipv4_address: 192.168.71.132 oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -50,7 +50,7 @@ services: ipv4_address: 192.168.71.133 oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml b/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml index 183c244018..747ab27906 100644 --- a/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml @@ -36,7 +36,7 @@ services: oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -49,7 +49,7 @@ services: oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -62,7 +62,7 @@ services: oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris diff --git a/ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml b/ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml index 49f2e9a4cc..10088fdfaf 100644 --- a/ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml @@ -36,7 +36,7 @@ services: oai-amf: container_name: "rfsim5g-oai-amf" - image: oaisoftwarealliance/oai-amf:v2.1.10 + image: oaisoftwarealliance/oai-amf:v2.2.1 environment: - TZ=Europe/paris volumes: @@ -49,7 +49,7 @@ services: oai-smf: container_name: "rfsim5g-oai-smf" - image: oaisoftwarealliance/oai-smf:v2.1.10 + image: oaisoftwarealliance/oai-smf:v2.2.1 environment: - TZ=Europe/Paris volumes: @@ -62,7 +62,7 @@ services: oai-upf: container_name: "rfsim5g-oai-upf" - image: oaisoftwarealliance/oai-upf:v2.1.10 + image: oaisoftwarealliance/oai-upf:v2.2.1 init: true environment: - TZ=Europe/Paris From 74fcab8779a0fd6cd6e57871e093eb24a79cf551 Mon Sep 17 00:00:00 2001 From: Shubhika Garg Date: Tue, 17 Mar 2026 12:08:19 +0100 Subject: [PATCH 33/72] Update trf-gen-cn5g Docker images to latest tag - This tag is multi-architecture compatible Signed-off-by: Shubhika Garg --- ci-scripts/yaml_files/4g_l2sim_fdd/docker-compose.yml | 2 +- .../yaml_files/4g_rfsimulator_fdd_05MHz/docker-compose.yml | 2 +- .../yaml_files/4g_rfsimulator_fdd_10MHz/docker-compose.yml | 2 +- .../yaml_files/4g_rfsimulator_fdd_20MHz/docker-compose.yml | 2 +- .../yaml_files/4g_rfsimulator_tdd_05MHz/docker-compose.yml | 2 +- ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_rfsimulator/README.md | 4 ++-- ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_24prb/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_multiue/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml | 2 +- ci-scripts/yaml_files/fr1_epc_20897/docker-compose.yml | 2 +- ci-scripts/yaml_files/magma_lte_20892/docker-compose.yml | 2 +- ci-scripts/yaml_files/magma_nsa_20897/docker-compose.yml | 2 +- .../oai-cn5g/docker-compose-positioning.yaml | 2 +- doc/tutorial_resources/oai-cn5g/docker-compose.yaml | 2 +- 22 files changed, 23 insertions(+), 23 deletions(-) diff --git a/ci-scripts/yaml_files/4g_l2sim_fdd/docker-compose.yml b/ci-scripts/yaml_files/4g_l2sim_fdd/docker-compose.yml index 7bc7a4d8cc..6813e13b10 100644 --- a/ci-scripts/yaml_files/4g_l2sim_fdd/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_l2sim_fdd/docker-compose.yml @@ -166,7 +166,7 @@ services: retries: 5 trf_gen: - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest privileged: true container_name: l2sim4g-trf-gen networks: diff --git a/ci-scripts/yaml_files/4g_rfsimulator_fdd_05MHz/docker-compose.yml b/ci-scripts/yaml_files/4g_rfsimulator_fdd_05MHz/docker-compose.yml index a88f36fbc0..53793a924b 100644 --- a/ci-scripts/yaml_files/4g_rfsimulator_fdd_05MHz/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_rfsimulator_fdd_05MHz/docker-compose.yml @@ -166,7 +166,7 @@ services: retries: 5 trf_gen: - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest privileged: true container_name: rfsim4g-trf-gen networks: diff --git a/ci-scripts/yaml_files/4g_rfsimulator_fdd_10MHz/docker-compose.yml b/ci-scripts/yaml_files/4g_rfsimulator_fdd_10MHz/docker-compose.yml index 2b19d5bbb7..f61c077b70 100644 --- a/ci-scripts/yaml_files/4g_rfsimulator_fdd_10MHz/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_rfsimulator_fdd_10MHz/docker-compose.yml @@ -166,7 +166,7 @@ services: retries: 5 trf_gen: - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest privileged: true container_name: rfsim4g-trf-gen networks: diff --git a/ci-scripts/yaml_files/4g_rfsimulator_fdd_20MHz/docker-compose.yml b/ci-scripts/yaml_files/4g_rfsimulator_fdd_20MHz/docker-compose.yml index 0e6caffb13..81362b26d1 100644 --- a/ci-scripts/yaml_files/4g_rfsimulator_fdd_20MHz/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_rfsimulator_fdd_20MHz/docker-compose.yml @@ -166,7 +166,7 @@ services: retries: 5 trf_gen: - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest privileged: true container_name: rfsim4g-trf-gen networks: diff --git a/ci-scripts/yaml_files/4g_rfsimulator_tdd_05MHz/docker-compose.yml b/ci-scripts/yaml_files/4g_rfsimulator_tdd_05MHz/docker-compose.yml index b795350642..35672381cc 100644 --- a/ci-scripts/yaml_files/4g_rfsimulator_tdd_05MHz/docker-compose.yml +++ b/ci-scripts/yaml_files/4g_rfsimulator_tdd_05MHz/docker-compose.yml @@ -166,7 +166,7 @@ services: retries: 5 trf_gen: - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest privileged: true container_name: rfsim4g-trf-gen networks: diff --git a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml index 798315a2b5..c4b0e7dc4c 100644 --- a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml @@ -75,7 +75,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest init: true entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ diff --git a/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml index 8b69688966..2bfd0366c1 100644 --- a/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml @@ -74,7 +74,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest init: true entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ diff --git a/ci-scripts/yaml_files/5g_rfsimulator/README.md b/ci-scripts/yaml_files/5g_rfsimulator/README.md index e839b3ea30..76c5d67e31 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/README.md +++ b/ci-scripts/yaml_files/5g_rfsimulator/README.md @@ -42,7 +42,7 @@ $ docker pull mysql:8.0 $ docker pull oaisoftwarealliance/oai-amf:v2.2.1 $ docker pull oaisoftwarealliance/oai-smf:v2.2.1 $ docker pull oaisoftwarealliance/oai-upf:v2.2.1 -$ docker pull oaisoftwarealliance/trf-gen-cn5g:focal +$ docker pull oaisoftwarealliance/trf-gen-cn5g:latest $ docker pull oaisoftwarealliance/oai-gnb:develop $ docker pull oaisoftwarealliance/oai-nr-ue:develop @@ -570,7 +570,7 @@ runs the gnb service with modified gNB executable. ```bash docker compose -f docker-compose.yaml -f local-override.yaml up -d oai-gnb ``` -This command rebuilds both the gNB & nrUE and runs the oai-nr-ue container with +This command rebuilds both the gNB & nrUE and runs the oai-nr-ue container with modified nrUE executable. ```bash docker compose -f docker-compose.yaml -f local-override.yaml up -d oai-nr-ue diff --git a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml index a3bed3795d..d8f2b558ef 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml @@ -74,7 +74,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest init: true entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ diff --git a/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml index 3f051e718f..96cc63c103 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml @@ -74,7 +74,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest init: true entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ diff --git a/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml index 8c10c43805..a69db9e6bd 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml @@ -74,7 +74,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest init: true entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ diff --git a/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml index 5e6aa20d32..071ddc0ab1 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml @@ -74,7 +74,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest init: true entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ diff --git a/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml index 5fcff71c8d..8521977e87 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml @@ -70,7 +70,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ "ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity" diff --git a/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml index 61e84260c1..45cd6dc8dd 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml @@ -70,7 +70,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ "ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity" diff --git a/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml index bb6342d3df..2eaa726bec 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml @@ -74,7 +74,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest init: true entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ diff --git a/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml index d86a3abb44..33a39dbea9 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml @@ -72,7 +72,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ "ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity" diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml index 7b3a509be8..e967bfe1f1 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml @@ -74,7 +74,7 @@ services: oai-ext-dn: privileged: true container_name: rfsim5g-oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest init: true entrypoint: /bin/bash -c \ "iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\ diff --git a/ci-scripts/yaml_files/fr1_epc_20897/docker-compose.yml b/ci-scripts/yaml_files/fr1_epc_20897/docker-compose.yml index ee1f5c2110..3ab53378d9 100644 --- a/ci-scripts/yaml_files/fr1_epc_20897/docker-compose.yml +++ b/ci-scripts/yaml_files/fr1_epc_20897/docker-compose.yml @@ -184,7 +184,7 @@ services: retries: 5 trf_gen: - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest privileged: true container_name: prod-trf-gen networks: diff --git a/ci-scripts/yaml_files/magma_lte_20892/docker-compose.yml b/ci-scripts/yaml_files/magma_lte_20892/docker-compose.yml index d025588de7..535561be8c 100644 --- a/ci-scripts/yaml_files/magma_lte_20892/docker-compose.yml +++ b/ci-scripts/yaml_files/magma_lte_20892/docker-compose.yml @@ -168,7 +168,7 @@ services: retries: 5 trf_gen: - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest privileged: true container_name: prod-trf-gen networks: diff --git a/ci-scripts/yaml_files/magma_nsa_20897/docker-compose.yml b/ci-scripts/yaml_files/magma_nsa_20897/docker-compose.yml index 636f416bfb..5005a53de3 100644 --- a/ci-scripts/yaml_files/magma_nsa_20897/docker-compose.yml +++ b/ci-scripts/yaml_files/magma_nsa_20897/docker-compose.yml @@ -168,7 +168,7 @@ services: retries: 5 trf_gen: - image: oaisoftwarealliance/trf-gen-cn5g:focal + image: oaisoftwarealliance/trf-gen-cn5g:latest privileged: true container_name: prod-trf-gen networks: diff --git a/doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml b/doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml index 6b75c22b6b..168b34c988 100644 --- a/doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml +++ b/doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml @@ -172,7 +172,7 @@ services: privileged: true init: true container_name: oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:jammy + image: oaisoftwarealliance/trf-gen-cn5g:latest entrypoint: /bin/bash -c \ "ip route add 10.0.0.0/16 via 192.168.70.134 dev eth0; ip route; sleep infinity" command: ["/bin/bash", "-c", "trap : SIGTERM SIGINT; sleep infinity & wait"] diff --git a/doc/tutorial_resources/oai-cn5g/docker-compose.yaml b/doc/tutorial_resources/oai-cn5g/docker-compose.yaml index 0baf032fbb..151fadb3ee 100644 --- a/doc/tutorial_resources/oai-cn5g/docker-compose.yaml +++ b/doc/tutorial_resources/oai-cn5g/docker-compose.yaml @@ -154,7 +154,7 @@ services: privileged: true init: true container_name: oai-ext-dn - image: oaisoftwarealliance/trf-gen-cn5g:jammy + image: oaisoftwarealliance/trf-gen-cn5g:latest entrypoint: /bin/bash -c \ "ip route add 10.0.0.0/16 via 192.168.70.134 dev eth0; ip route; sleep infinity" command: ["/bin/bash", "-c", "trap : SIGTERM SIGINT; sleep infinity & wait"] From 8ecc693799c2c4a824af1b837a55a7d431a9af7e Mon Sep 17 00:00:00 2001 From: Shubhika Garg Date: Tue, 17 Mar 2026 13:57:29 +0100 Subject: [PATCH 34/72] docker-compose: Add iperf3 daemon to oai-ext-dn container in cn5g Signed-off-by: Shubhika Garg --- doc/tutorial_resources/oai-cn5g/docker-compose.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tutorial_resources/oai-cn5g/docker-compose.yaml b/doc/tutorial_resources/oai-cn5g/docker-compose.yaml index 151fadb3ee..c12449ff61 100644 --- a/doc/tutorial_resources/oai-cn5g/docker-compose.yaml +++ b/doc/tutorial_resources/oai-cn5g/docker-compose.yaml @@ -156,7 +156,7 @@ services: container_name: oai-ext-dn image: oaisoftwarealliance/trf-gen-cn5g:latest entrypoint: /bin/bash -c \ - "ip route add 10.0.0.0/16 via 192.168.70.134 dev eth0; ip route; sleep infinity" + "ip route add 10.0.0.0/16 via 192.168.70.134 dev eth0; ip route; iperf3 -s -D; sleep infinity" command: ["/bin/bash", "-c", "trap : SIGTERM SIGINT; sleep infinity & wait"] healthcheck: test: /bin/bash -c "ip r | grep 10.0.0" From 05c2165535d64e3bb798d63deee83c6b423b91b3 Mon Sep 17 00:00:00 2001 From: Shubhika Garg Date: Tue, 14 Apr 2026 15:17:16 +0200 Subject: [PATCH 35/72] chore: upgrade MySQL image to 9.6 Signed-off-by: Shubhika Garg --- ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_rfsimulator/README.md | 2 +- ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_multiue/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml | 2 +- .../yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml | 2 +- ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml | 2 +- doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml | 2 +- doc/tutorial_resources/oai-cn5g/docker-compose.yaml | 2 +- 18 files changed, 18 insertions(+), 18 deletions(-) diff --git a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml index c4b0e7dc4c..a606c50ccc 100644 --- a/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_f1_rfsimulator/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml index 2bfd0366c1..9d1a35a249 100644 --- a/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_fdd_rfsimulator/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_rfsimulator/README.md b/ci-scripts/yaml_files/5g_rfsimulator/README.md index 76c5d67e31..6090b4b918 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/README.md +++ b/ci-scripts/yaml_files/5g_rfsimulator/README.md @@ -38,7 +38,7 @@ Password: Now pull images. ```bash -$ docker pull mysql:8.0 +$ docker pull mysql:9.6 $ docker pull oaisoftwarealliance/oai-amf:v2.2.1 $ docker pull oaisoftwarealliance/oai-smf:v2.2.1 $ docker pull oaisoftwarealliance/oai-upf:v2.2.1 diff --git a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml index d8f2b558ef..a668adc840 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ./oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml index 96cc63c103..0ae419fb4c 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_24prb/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml index a69db9e6bd..d22850b214 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_2x2/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml index de841dcd2a..a78f2ef897 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_e1/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml index 071ddc0ab1..b27cd57a06 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_fr2_66prb/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml index 8521977e87..313d36bbfb 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_multiue/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql - ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh diff --git a/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml index 45cd6dc8dd..5e11c8cb3c 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_n2_ho/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql - ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh diff --git a/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml index 2eaa726bec..42899ae248 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_ntn_geo/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml index 33a39dbea9..c7343e9a25 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_ntn_leo/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql - ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh diff --git a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml index e967bfe1f1..60ca044f94 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator_u0_25prb/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml b/ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml index f9dfbdeb2d..85303fe749 100644 --- a/ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_vrtsim_chanmod/docker-compose.yaml @@ -3,7 +3,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml b/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml index 747ab27906..7ab1020d01 100644 --- a/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_vrtsim_cirdb/docker-compose.yaml @@ -12,7 +12,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml b/ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml index 10088fdfaf..349c61ff24 100644 --- a/ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_vrtsim_multiue/docker-compose.yaml @@ -12,7 +12,7 @@ services: mysql: container_name: "rfsim5g-mysql" - image: mysql:8.0 + image: mysql:9.6 init: true volumes: - ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql diff --git a/doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml b/doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml index 168b34c988..c3b9d937c1 100644 --- a/doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml +++ b/doc/tutorial_resources/oai-cn5g/docker-compose-positioning.yaml @@ -1,7 +1,7 @@ services: mysql: container_name: "mysql" - image: mysql:8.0 + image: mysql:9.6 volumes: - ./database/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql - ./healthscripts/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh diff --git a/doc/tutorial_resources/oai-cn5g/docker-compose.yaml b/doc/tutorial_resources/oai-cn5g/docker-compose.yaml index c12449ff61..d99eadb844 100644 --- a/doc/tutorial_resources/oai-cn5g/docker-compose.yaml +++ b/doc/tutorial_resources/oai-cn5g/docker-compose.yaml @@ -1,7 +1,7 @@ services: mysql: container_name: "mysql" - image: mysql:8.0 + image: mysql:9.6 volumes: - ./database/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql - ./healthscripts/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh From 1be48251f0626b930e6485e07f205f3cefc582d2 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 13 Apr 2026 10:19:14 +0200 Subject: [PATCH 36/72] NFAPI_LIB needs z(lib) for crc32() Signed-off-by: Robert Schmidt --- CMakeLists.txt | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 6e8758cea3..50ef0bfcba 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -752,6 +752,7 @@ set(NFAPI_SRC add_library(NFAPI_LIB ${NFAPI_SRC}) target_link_libraries(NFAPI_LIB PUBLIC nfapi_common) target_link_libraries(NFAPI_LIB PUBLIC nr_fapi_p5 nr_fapi_p7) +target_link_libraries(NFAPI_LIB PRIVATE z) if(OAI_WLS) target_compile_definitions(NFAPI_LIB PRIVATE ENABLE_WLS) @@ -1742,7 +1743,7 @@ target_link_libraries(lte-softmodem PRIVATE lte_rrc nr_rrc s1ap m2ap x2ap m3ap GTPV1U SECURITY UTIL SCTP_CLIENT MME_APP SCHED_LIB SCHED_RU_LIB PHY_COMMON PHY PHY_RU L2 L2_LTE NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB ${NAS_UE_LIB} ITTI SIMU radio_common softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(lte-softmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(lte-softmodem PRIVATE ${T_LIB}) @@ -1763,7 +1764,7 @@ add_executable(oairu target_link_libraries(oairu PRIVATE -Wl,--start-group SCHED_RU_LIB PHY_COMMON PHY_RU UTIL radio_common softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(oairu PRIVATE pthread m CONFIG_LIB rt ${T_LIB}) target_link_libraries(oairu PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) @@ -1792,7 +1793,7 @@ target_link_libraries(lte-uesoftmodem PRIVATE SECURITY UTIL SCTP_CLIENT MME_APP SCHED_RU_LIB SCHED_UE_LIB PHY_COMMON PHY_UE PHY_RU L2_UE L2_LTE SIMU NFAPI_LIB NFAPI_PNF_LIB NFAPI_USER_LIB MISC_NFAPI_LTE_LIB ${NAS_UE_LIB} ITTI radio_common softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(lte-uesoftmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(lte-uesoftmodem PRIVATE ${T_LIB}) @@ -1812,7 +1813,7 @@ add_executable(nr-oru ${OPENAIR_DIR}/executables/main_nr_ru.c ) target_link_libraries(nr-oru PRIVATE - UTIL SCHED_RU_LIB PHY_COMMON PHY_RU PHY_NR shlib_loader z dl + UTIL SCHED_RU_LIB PHY_COMMON PHY_RU PHY_NR shlib_loader dl radio_common softmodem_common) target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils barrier actor) @@ -1841,7 +1842,7 @@ target_link_libraries(nr-softmodem PRIVATE ngap s1ap L2_LTE_NR L2_NR MAC_NR_COMMON NFAPI_LIB NFAPI_VNF_LIB NFAPI_PNF_LIB NFAPI_USER_LIB SIMU x2ap f1ap m2ap m3ap e1ap radio_common time_management softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(nr-softmodem PRIVATE pthread m CONFIG_LIB rt) target_link_libraries(nr-softmodem PRIVATE ${T_LIB}) @@ -1881,7 +1882,7 @@ target_link_libraries(nr-cuup PRIVATE GTPV1U e1ap f1ap time_management softmodem_common alg - z dl pthread ${T_LIB}) + dl pthread ${T_LIB}) target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs) if(E2_AGENT) target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup) @@ -1909,7 +1910,7 @@ target_link_libraries(nr-uesoftmodem PRIVATE PHY_COMMON PHY_NR_COMMON PHY_NR_UE NR_L2_UE MAC_NR_COMMON NFAPI_LIB ITTI SIMU radio_common time_management softmodem_common - -Wl,--end-group z dl) + -Wl,--end-group dl) target_link_libraries(nr-uesoftmodem PRIVATE pthread m CONFIG_LIB rt nr_ue_phy_meas) target_link_libraries(nr-uesoftmodem PRIVATE ${T_LIB}) From 7e3406c3bd00466bd02cfab9d20cf4e58473fedb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sat, 11 Apr 2026 11:35:31 +0200 Subject: [PATCH 37/72] Delete dummy functions Signed-off-by: Robert Schmidt --- openair1/SCHED/nfapi_lte_dummy.c | 65 -------------------------------- 1 file changed, 65 deletions(-) diff --git a/openair1/SCHED/nfapi_lte_dummy.c b/openair1/SCHED/nfapi_lte_dummy.c index 1c22057064..55c339d7c8 100644 --- a/openair1/SCHED/nfapi_lte_dummy.c +++ b/openair1/SCHED/nfapi_lte_dummy.c @@ -16,18 +16,6 @@ int slot_ahead = 6; //uint8_t nfapi_mode=0; NR_IF_Module_t *NR_IF_Module_init(int Mod_id) {return NULL;} -void nr_fill_ulsch(PHY_VARS_gNB *gNB, - int frame, - int slot, - nfapi_nr_pusch_pdu_t *ulsch_pdu){} -void nr_fill_pucch(PHY_VARS_gNB *gNB, - int frame, - int slot, - nfapi_nr_pucch_pdu_t *pucch_pdu){} -void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) -{ -} - void nr_phy_config_request(NR_PHY_Config_t *gNB){} void nr_dump_frame_parms(NR_DL_FRAME_PARMS *fp){} @@ -70,59 +58,6 @@ void *pnf_nr_p7_thread_start(void *ptr) { return 0; } - -bool wls_vnf_nr_send_p7_message(vnf_p7_t *vnf_p7, nfapi_nr_p7_message_header_t *msg) -{ - return false; -} - - -bool wls_vnf_nr_send_p5_message(vnf_t *vnf,uint16_t p5_idx, nfapi_nr_p4_p5_message_header_t* msg, uint32_t msg_len) -{ - return false; -} - -void *wls_fapi_vnf_nr_start_thread(void *ptr) -{ - return NULL; -} - -bool wls_pnf_nr_send_p7_message(pnf_p7_t* pnf_p7,nfapi_nr_p7_message_header_t *msg, uint32_t msg_len) -{ - return false; -} - -bool wls_pnf_nr_send_p5_message(pnf_t *pnf, nfapi_nr_p4_p5_message_header_t *msg, uint32_t msg_len) -{ - return false; -} - -void *wls_fapi_pnf_nr_start_thread(void *ptr) -{ - return NULL; -} - -void wls_pnf_set_p7_config(void *p7_config) -{ - -} - -void wls_fapi_nr_pnf_stop() -{ -} - -void wls_pnf_close(pthread_t p5_thread) -{ -} - -void wls_vnf_send_stop_request() -{ -} - -void wls_vnf_stop() -{ -} - void socket_nfapi_nr_pnf_stop() { } From b81442de913e175374a84edb5bacb42fa95111ec Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 13 Apr 2026 10:22:58 +0200 Subject: [PATCH 38/72] Link NFAPI_LIB into SCHED_NR_LIB phy_procedures_nr_gNB.c calls pack_srs() functions to pack individual SRS "subPDUs". Correspondingly, the library depends on NFAPI_LIB. In nr_ulsim, in a later commit, we will need to unpack the SRS.indication, so remove dummy functions and implement correctly. Signed-off-by: Robert Schmidt --- CMakeLists.txt | 1 + .../SIMULATION/NR_PHY/nr_unitary_common.c | 68 ------------------- 2 files changed, 1 insertion(+), 68 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 50ef0bfcba..665904c53f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -713,6 +713,7 @@ set(SCHED_NR_SRC ) add_library(SCHED_NR_LIB ${SCHED_NR_SRC}) target_link_libraries(SCHED_NR_LIB PRIVATE asn1_nr_rrc_hdrs UTIL) +target_link_libraries(SCHED_NR_LIB PRIVATE NFAPI_LIB) # some FAPI messages pack "subPDUs" set(SCHED_SRC_RU ${OPENAIR1_DIR}/SCHED/ru_procedures.c diff --git a/openair1/SIMULATION/NR_PHY/nr_unitary_common.c b/openair1/SIMULATION/NR_PHY/nr_unitary_common.c index 9a595a9295..87e668db28 100644 --- a/openair1/SIMULATION/NR_PHY/nr_unitary_common.c +++ b/openair1/SIMULATION/NR_PHY/nr_unitary_common.c @@ -56,40 +56,6 @@ void handle_nr_slot_ind(uint16_t sfn, uint16_t slot) UNUSED(slot); } -int pack_nr_srs_beamforming_report(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen) -{ - UNUSED(pMessageBuf); - UNUSED(pPackedBuf); - UNUSED(packedBufLen); - return 0; -} -int unpack_nr_srs_beamforming_report(void *pMessageBuf, uint32_t messageBufLen, void *pUnpackedBuf, uint32_t unpackedBufLen) -{ - UNUSED(pMessageBuf); - UNUSED(messageBufLen); - UNUSED(pUnpackedBuf); - UNUSED(unpackedBufLen); - return 0; -} -int pack_nr_srs_normalized_channel_iq_matrix(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen) -{ - UNUSED(pMessageBuf); - UNUSED(pPackedBuf); - UNUSED(packedBufLen); - return 0; -} -int unpack_nr_srs_normalized_channel_iq_matrix(void *pMessageBuf, - uint32_t messageBufLen, - void *pUnpackedBuf, - uint32_t unpackedBufLen) -{ - UNUSED(pMessageBuf); - UNUSED(messageBufLen); - UNUSED(pUnpackedBuf); - UNUSED(unpackedBufLen); - return 0; -} - void configure_nr_nfapi_pnf(char *vnf_ip_addr, int vnf_p5_port, char *pnf_ip_addr, int pnf_p7_port, int vnf_p7_port) { UNUSED(vnf_ip_addr); @@ -102,40 +68,6 @@ void configure_nr_nfapi_vnf(eth_params_t params) { UNUSED(params); } -int nfapi_nr_p7_message_pack(void *pMessageBuf, void *pPackedBuf, uint32_t packedBufLen, nfapi_p7_codec_config_t *config) -{ - UNUSED(pMessageBuf); - UNUSED(pPackedBuf); - UNUSED(packedBufLen); - UNUSED(config); - return 0; -} -bool nfapi_nr_p7_message_unpack(void *pMessageBuf, - uint32_t messageBufLen, - void *pUnpackedBuf, - uint32_t unpackedBufLen, - nfapi_p7_codec_config_t *config) -{ - UNUSED(pMessageBuf); - UNUSED(messageBufLen); - UNUSED(pUnpackedBuf); - UNUSED(unpackedBufLen); - UNUSED(config); - return false; -} -int nfapi_p7_message_header_unpack(void *pMessageBuf, - uint32_t messageBufLen, - void *pUnpackedBuf, - uint32_t unpackedBufLen, - nfapi_p7_codec_config_t *config) -{ - UNUSED(pMessageBuf); - UNUSED(messageBufLen); - UNUSED(pUnpackedBuf); - UNUSED(unpackedBufLen); - UNUSED(config); - return 0; -} void nr_mac_rrc_sync_ind(const module_id_t module_id, const frame_t frame, const bool in_sync) { From 449910dbc75bc8a2c9db66cbbdce05704551e2c7 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 17 Mar 2026 08:06:16 +0100 Subject: [PATCH 39/72] Add fsn lib to simplify frame/slot handling Refactor existing code, and harmonize interface. Add a couple of tests. Signed-off-by: Robert Schmidt --- common/utils/CMakeLists.txt | 2 +- common/utils/fsn.c | 55 ++++++++++++ common/utils/fsn.h | 36 ++++++++ common/utils/tests/CMakeLists.txt | 5 ++ common/utils/tests/test_fsn.cpp | 84 +++++++++++++++++++ .../LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c | 40 ++------- openair2/LAYER2/NR_MAC_gNB/main.c | 1 + openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h | 9 +- 8 files changed, 189 insertions(+), 43 deletions(-) create mode 100644 common/utils/fsn.c create mode 100644 common/utils/fsn.h create mode 100644 common/utils/tests/test_fsn.cpp diff --git a/common/utils/CMakeLists.txt b/common/utils/CMakeLists.txt index 3196b39868..fc113ffa46 100644 --- a/common/utils/CMakeLists.txt +++ b/common/utils/CMakeLists.txt @@ -16,7 +16,7 @@ add_subdirectory(nr) add_subdirectory(LOG) add_subdirectory(threadPool) add_subdirectory(time_manager) -add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c reverse_bits.c) +add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c reverse_bits.c fsn.c) target_include_directories(utils PUBLIC .) target_link_libraries(utils PRIVATE ${T_LIB}) add_subdirectory(barrier) diff --git a/common/utils/fsn.c b/common/utils/fsn.c new file mode 100644 index 0000000000..e8566a2da9 --- /dev/null +++ b/common/utils/fsn.c @@ -0,0 +1,55 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include "fsn.h" + +#include + +static int num_slots[5] = {10, 20, 40, 80, 160}; + +fsn_t fsn_add_delta(fsn_t fsn, uint32_t delta) +{ + const int slots_frame = num_slots[fsn.mu]; + int next_f = fsn.f + delta / slots_frame; + int next_s = fsn.s + delta % slots_frame; + fsn_t res = { + .f = (next_f + next_s / slots_frame) % 1024, + .s = next_s % slots_frame, + .mu = fsn.mu, + }; + return res; +} + +int fsn_get_diff(fsn_t a, fsn_t b) +{ + assert(a.mu == b.mu); + const int slots_frame = num_slots[a.mu]; + int diff = (a.f * slots_frame + a.s) - (b.f * slots_frame + b.s); + if (diff < -512 * slots_frame) + diff += 1024 * slots_frame; + else if (diff > 512 * slots_frame) + diff -= 1024 * slots_frame; + return diff; +} + +fsn_t fsn_get_max(fsn_t a, fsn_t b) +{ + if (fsn_get_diff(a, b) <= 0) { + return b; + } else { + return a; + } +} + +bool fsn_equal(fsn_t a, fsn_t b) +{ + assert(a.mu == b.mu); + return a.f == b.f && a.s == b.s && a.mu == b.mu; +} + +bool fsn_in_the_past(fsn_t old, fsn_t new) +{ + int diff = fsn_get_diff(old, new); + return diff < 0; +} diff --git a/common/utils/fsn.h b/common/utils/fsn.h new file mode 100644 index 0000000000..dfa126bc85 --- /dev/null +++ b/common/utils/fsn.h @@ -0,0 +1,36 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#ifndef FSN_H_ +#define FSN_H_ + +#include +#include + +/// helper type to encapsulate a frame/slot combination in a single type. +typedef struct fsn { + uint16_t f; /// frame + uint16_t s; /// slot + uint8_t mu; /// numerology +} fsn_t; + +/* \brief Add delta slots to fsn. Note that this depends also on the stored + * numerology inside fsn. */ +fsn_t fsn_add_delta(fsn_t fsn, uint32_t delta); + +/* \brief Get difference of slots for a and b. + * \note asserts if numerology not the same. */ +int fsn_get_diff(fsn_t a, fsn_t b); + +/* \brief Gets the larger of two fsns */ +fsn_t fsn_get_max(fsn_t a, fsn_t b); + +/* \brief Compare if both fsn_t's are equal */ +bool fsn_equal(fsn_t a, fsn_t b); + +/* \brief Determine if told is in the past of tnew. The threshold is 512 + * frames. */ +bool fsn_in_the_past(fsn_t told, fsn_t tnew); + +#endif /* FSN_H_ */ diff --git a/common/utils/tests/CMakeLists.txt b/common/utils/tests/CMakeLists.txt index e3c310fed9..7401e2d1ea 100644 --- a/common/utils/tests/CMakeLists.txt +++ b/common/utils/tests/CMakeLists.txt @@ -2,3 +2,8 @@ add_executable(test_tpool_vs_actors test_tpool_vs_actors.c) target_link_libraries(test_tpool_vs_actors PRIVATE thread-pool pthread LOG minimal_lib actor) + +add_executable(test_fsn test_fsn.cpp) +add_dependencies(tests test_fsn) +target_link_libraries(test_fsn PRIVATE utils GTest::gtest) +add_test(NAME test_fsn COMMAND ./test_fsn) diff --git a/common/utils/tests/test_fsn.cpp b/common/utils/tests/test_fsn.cpp new file mode 100644 index 0000000000..3cbe34dfb2 --- /dev/null +++ b/common/utils/tests/test_fsn.cpp @@ -0,0 +1,84 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include +extern "C" { +#include "fsn.h" +} + +TEST(fsn, test_mu0_fsn_add) { + fsn_t current = {123, 5, 0}; + fsn_t next = current; + + next = fsn_add_delta(next, 12); + EXPECT_EQ(next.f, 124); + EXPECT_EQ(next.s, 7); + EXPECT_EQ(next.mu, 0); + + next = fsn_add_delta(next, 10); + EXPECT_EQ(next.f, 125); + EXPECT_EQ(next.s, 7); + EXPECT_EQ(next.mu, 0); + + next = fsn_add_delta(next, 10 * 1024); + EXPECT_EQ(next.f, 125); + EXPECT_EQ(next.s, 7); + EXPECT_EQ(next.mu, 0); +} + +TEST(fsn, test_mu0_get_diff) { + fsn_t current = {123, 5, 0}; + fsn_t next = fsn_add_delta(current, 12); + int diff; + + diff = fsn_get_diff(current, next); + EXPECT_EQ(diff, -12); + + diff = fsn_get_diff(next, current); + EXPECT_EQ(diff, 12); +} + +TEST(fsn, test_mu0_get_max) { + fsn_t current = {123, 5, 0}; + fsn_t next = {125, 7, 0}; + + fsn_t max = fsn_get_max(current, next); + EXPECT_EQ(max.f, next.f); + EXPECT_EQ(max.s, next.s); + EXPECT_EQ(max.mu, next.mu); +} + +TEST(fsn, test_mu0_equal) { + fsn_t a = {123, 5, 0}; + fsn_t b = a; + fsn_t c = {123, 6, 0}; + + EXPECT_TRUE(fsn_equal(a, b)); + EXPECT_FALSE(fsn_equal(a, c)); +} + +TEST(fsn, test_mu0_in_the_past) { + fsn_t a = {0, 0, 0}; + fsn_t n = {100, 9, 0}; + fsn_t b_bef = {511, 9, 0}; + fsn_t b_ex = {512, 0, 0}; + fsn_t b_aft = {512, 1, 0}; + + // a is in the past of n + EXPECT_TRUE(fsn_in_the_past(a, n)); + // similarly, a is in the past of b_bef + EXPECT_TRUE(fsn_in_the_past(a, b_bef)); + // difference "to both sides" is 512 frames, so we consider a still in the + // past of b_ex + EXPECT_TRUE(fsn_in_the_past(a, b_ex)); + // (b_aft - a) < (a - b_aft) => b_aft is in the past of a because of + // wrap-around + EXPECT_TRUE(fsn_in_the_past(b_aft, a)); +} + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index be869d9de9..db35776944 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -2528,34 +2528,6 @@ void post_process_ulsch(gNB_MAC_INST *nr_mac, post_process_pusch_t *pusch, NR_UE nr_mac->cset0_bwp_size); } -fsn_t fs_add_delta(const frame_structure_t *fs, uint32_t delta, fsn_t fsn) -{ - const int slots_frame = fs->numb_slots_frame; - fsn_t res = { - .f = (fsn.f + (fsn.s + delta) / slots_frame) % MAX_FRAME_NUMBER, - .s = (fsn.s + delta) % slots_frame, - }; - return res; -} -int fs_get_diff(const frame_structure_t *fs, fsn_t a, fsn_t b) -{ - const int slots_frame = fs->numb_slots_frame; - int diff = (a.f * slots_frame + a.s) - (b.f * slots_frame + b.s); - if (diff < -512 * slots_frame) - diff += 1024 * slots_frame; - else if (diff > 512 * slots_frame) - diff -= 1024 * slots_frame; - return diff; -} -fsn_t fs_get_max(const frame_structure_t *fs, fsn_t a, fsn_t b) -{ - if (fs_get_diff(fs, a, b) <= 0) { - return b; - } else { - return a; - } -} - static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp_pusch) { int frame = pp_pusch->frame; @@ -2579,8 +2551,8 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp // FAPI cannot handle more than MAX_DCI_CORESET DCIs max_dci = min(max_dci, MAX_DCI_CORESET); - fsn_t current = {frame, slot}; - fsn_t min_next = fs_add_delta(fs, min_rxtx, current); + fsn_t current = {frame, slot, *scc->ssbSubcarrierSpacing}; + fsn_t min_next = fsn_add_delta(current, min_rxtx); /* if it's the last DL slot, we should try all TDAs to make sure that the * scheduler can reach e.g. the next mixed slot. Otherwise, if we don't, we * might starve HARQ processes that need a retransmission in a specific slot @@ -2589,16 +2561,16 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp fsn_t *next = &nr_mac->ul_next; while (max_dci > 0) { /* go to the next UL slot, skipping DL if necessary */ - *next = fs_get_max(fs, *next, min_next); + *next = fsn_get_max(*next, min_next); while (!is_ul_slot(next->s, fs)) - *next = fs_add_delta(fs, 1, *next); + *next = fsn_add_delta(*next, 1); if (!is_dl_slot(current.s, fs)) // if current slot is not DL, nothing to do break; /* get a TDA so that next UL scheduling slot can be reached from current, * and exit if there is no such TDA. Remove koffset, as it is * "cell-specific", i.e., the UE will add it on the computation. */ - int k2 = fs_get_diff(fs, *next, current) - koffset; + int k2 = fsn_get_diff(*next, current) - koffset; DevAssert(k2 > 0); // we assume that all beams have the same symbol utilization in all RBs for // simplification (but this might not be true). Otherwise, we would need to @@ -2631,7 +2603,7 @@ static void nr_ulsch_preprocessor(gNB_MAC_INST *nr_mac, post_process_pusch_t *pp break; max_dci -= sched; - *next = fs_add_delta(fs, 1, *next); + *next = fsn_add_delta(*next, 1); } } diff --git a/openair2/LAYER2/NR_MAC_gNB/main.c b/openair2/LAYER2/NR_MAC_gNB/main.c index ca12c48c48..1f619308c2 100644 --- a/openair2/LAYER2/NR_MAC_gNB/main.c +++ b/openair2/LAYER2/NR_MAC_gNB/main.c @@ -293,6 +293,7 @@ void mac_top_init_gNB(ngran_node_t node_type, RC.nrmac[i]->cset0_bwp_start = 0; RC.nrmac[i]->cset0_bwp_size = 0; + RC.nrmac[i]->ul_next = (fsn_t) {.mu = *scc->ssbSubcarrierSpacing}; RC.nrmac[i]->print_ue_stats = true; pthread_mutex_init(&RC.nrmac[i]->sched_lock, NULL); diff --git a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h index f7ea466a94..391bfeabe8 100644 --- a/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h +++ b/openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h @@ -16,6 +16,7 @@ #include #include #include +#include "fsn.h" #include "common/utils/ds/seq_arr.h" #include "common/utils/nr/nr_common.h" #include "common/utils/ds/byte_array.h" @@ -878,14 +879,6 @@ typedef struct { NR_ControlResourceSet_t coreset; } NR_sched_ctrl_sib1_t; -/// helper type to encapsulate a frame/slot combination in a single type. -/// Currently only used in the UL preprocessor. Note: if you use this type -/// further, please refactor it into a common type first. -typedef struct fsn { - frame_t f; - slot_t s; -} fsn_t; - typedef struct NR_du_stats { /// cell-wide wide-band CQI distribution, see 28.552 5.1.1.11.1; /// 0-15 CQI, 1-8 RI, 1-3 CQI table From 51e17fd7c8bb36465b3b88382a371c97bfc3ed4f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 17 Mar 2026 08:06:48 +0100 Subject: [PATCH 40/72] Add test_tpool_vs_actors to unit tests Signed-off-by: Robert Schmidt --- common/utils/tests/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/common/utils/tests/CMakeLists.txt b/common/utils/tests/CMakeLists.txt index 7401e2d1ea..14d2f83d7a 100644 --- a/common/utils/tests/CMakeLists.txt +++ b/common/utils/tests/CMakeLists.txt @@ -2,6 +2,8 @@ add_executable(test_tpool_vs_actors test_tpool_vs_actors.c) target_link_libraries(test_tpool_vs_actors PRIVATE thread-pool pthread LOG minimal_lib actor) +add_test(NAME test_tpool_vs_actors COMMAND ./test_tpool_vs_actors) +add_dependencies(tests test_tpool_vs_actors) add_executable(test_fsn test_fsn.cpp) add_dependencies(tests test_fsn) From 34aad5d2761bb50c0d054aa31da6f379f26b4d4f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 11 Nov 2025 17:58:41 +0100 Subject: [PATCH 41/72] Add spsc_q data structure library This data structure is designed to support the use cases of the L1, i.e., provide a queue which can be quickly used to order L1 jobs in FIFO order with simple iteration of jobs to treat. It is a Single-Consumer Single-Producer queue library. Signed-off-by: Robert Schmidt --- common/utils/ds/CMakeLists.txt | 1 + common/utils/ds/spsc_q.c | 112 +++++++++++++++++ common/utils/ds/spsc_q.h | 32 +++++ common/utils/ds/tests/CMakeLists.txt | 10 +- common/utils/ds/tests/test_spsc_q.cpp | 135 +++++++++++++++++++++ common/utils/ds/tests/test_spsc_q_perf.cpp | 75 ++++++++++++ 6 files changed, 363 insertions(+), 2 deletions(-) create mode 100644 common/utils/ds/spsc_q.c create mode 100644 common/utils/ds/spsc_q.h create mode 100644 common/utils/ds/tests/test_spsc_q.cpp create mode 100644 common/utils/ds/tests/test_spsc_q_perf.cpp diff --git a/common/utils/ds/CMakeLists.txt b/common/utils/ds/CMakeLists.txt index 7007d77fac..6f7053a1be 100644 --- a/common/utils/ds/CMakeLists.txt +++ b/common/utils/ds/CMakeLists.txt @@ -6,6 +6,7 @@ add_library(ds OBJECT seq_arr.c hashtable.c obj_hashtable.c + spsc_q.c ) target_include_directories(ds PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) diff --git a/common/utils/ds/spsc_q.c b/common/utils/ds/spsc_q.c new file mode 100644 index 0000000000..fdc97f00b8 --- /dev/null +++ b/common/utils/ds/spsc_q.c @@ -0,0 +1,112 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include +#include +#include + +#include "spsc_q.h" + +#define rb_lock(LOCK) if (pthread_mutex_lock(LOCK) != 0) abort() +#define rb_unlock(LOCK) if (pthread_mutex_unlock(LOCK) != 0) abort() + +spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz) +{ + /* internally, use one element more: the ringbuffer is full if + * (write_idx + 1) % cnt == read_idx, so it's full if one element is still + * free (because otherwise write_idx == read_idx, but this is the empty + * condition) */ + cnt += 1; + spsc_q_t rb = {.cnt = cnt, .elsiz = elsiz}; + rb.buf = calloc(cnt, elsiz); + pthread_mutex_init(&rb.mtx, NULL); + return rb; +} + +void spsc_q_free(spsc_q_t *rb) +{ + free(rb->buf); + pthread_mutex_destroy(&rb->mtx); + memset(rb, 0, sizeof(*rb)); +} + +bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz) +{ + assert(elsiz == rb->elsiz); + rb_lock(&rb->mtx); + if ((rb->write_idx + 1) % rb->cnt == rb->read_idx) { + rb_unlock(&rb->mtx); + return false; + } + + uint8_t *bufpos = &rb->buf[rb->write_idx * rb->elsiz]; + memcpy(bufpos, src, elsiz); + rb->write_idx = (rb->write_idx + 1) % rb->cnt; + rb_unlock(&rb->mtx); + return true; +} + +bool spsc_q_get_if(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz) +{ + assert(elsiz == rb->elsiz); + rb_lock(&rb->mtx); + if (rb->write_idx == rb->read_idx) { + rb_unlock(&rb->mtx); + return false; + } + + uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; + if (!p(bufpos, user)) { + rb_unlock(&rb->mtx); + return false; + } + + memcpy(dest, bufpos, elsiz); + rb->read_idx = (rb->read_idx + 1) % rb->cnt; + rb_unlock(&rb->mtx); + return true; +} + +static bool always(const void *data, void *user) +{ + (void) data; + (void) user; + return true; +} +bool spsc_q_get(spsc_q_t *rb, void *dest, size_t elsiz) +{ + return spsc_q_get_if(rb, always, NULL, dest, elsiz); +} + +int spsc_q_get_while(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz, size_t max_len) +{ + assert(elsiz == rb->elsiz); + rb_lock(&rb->mtx); + size_t count = 0; + while (rb->write_idx != rb->read_idx && count < max_len) { + uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; + if (!p(bufpos, user)) + break; + memcpy(dest + count * elsiz, bufpos, elsiz); + count++; + rb->read_idx = (rb->read_idx + 1) % rb->cnt; + } + rb_unlock(&rb->mtx); + return count; +} + +int spsc_q_drop_while(spsc_q_t *rb, pred p, void *user) +{ + rb_lock(&rb->mtx); + size_t dropped = 0; + while (rb->write_idx != rb->read_idx) { + uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; + if (!p(bufpos, user)) + break; + dropped++; + rb->read_idx = (rb->read_idx + 1) % rb->cnt; + } + rb_unlock(&rb->mtx); + return dropped; +} diff --git a/common/utils/ds/spsc_q.h b/common/utils/ds/spsc_q.h new file mode 100644 index 0000000000..d00cb4b77d --- /dev/null +++ b/common/utils/ds/spsc_q.h @@ -0,0 +1,32 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#ifndef SPSC_Q_H_ +#define SPSC_Q_H_ + +#include +#include +#include + +typedef struct spsc_q { + uint8_t *buf; + size_t elsiz; + size_t cnt; + size_t write_idx; + size_t read_idx; + pthread_mutex_t mtx; +} spsc_q_t; + +spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz); +void spsc_q_free(spsc_q_t *rb); + +bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz); +bool spsc_q_get(spsc_q_t *rb, void *dest, size_t elsiz); + +typedef bool (*pred)(const void *data, void *user); +bool spsc_q_get_if(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz); +int spsc_q_drop_while(spsc_q_t *rb, pred p, void *user); +int spsc_q_get_while(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz, size_t max_len); + +#endif /* SPSC_Q_H_ */ diff --git a/common/utils/ds/tests/CMakeLists.txt b/common/utils/ds/tests/CMakeLists.txt index a38cf159cb..1a2e5277a1 100644 --- a/common/utils/ds/tests/CMakeLists.txt +++ b/common/utils/ds/tests/CMakeLists.txt @@ -8,5 +8,11 @@ add_test(NAME test_seq_arr COMMAND test_seq_arr) # no options required add_executable(test_hashtable test_hashtable.cpp) add_dependencies(tests test_hashtable) target_link_libraries(test_hashtable PRIVATE ds GTest::gtest) -add_test(NAME test_hashtable - COMMAND ./test_hashtable) +add_test(NAME test_hashtable COMMAND ./test_hashtable) + +add_executable(test_spsc_q test_spsc_q.cpp) +target_link_libraries(test_spsc_q ds GTest::gtest) +add_dependencies(tests test_spsc_q) +add_test(NAME test_spsc_q COMMAND test_spsc_q) +add_executable(test_spsc_q_perf test_spsc_q_perf.cpp) +target_link_libraries(test_spsc_q_perf PRIVATE ds benchmark::benchmark) diff --git a/common/utils/ds/tests/test_spsc_q.cpp b/common/utils/ds/tests/test_spsc_q.cpp new file mode 100644 index 0000000000..0c1638d490 --- /dev/null +++ b/common/utils/ds/tests/test_spsc_q.cpp @@ -0,0 +1,135 @@ +/* + * SPDX-License-Identifier: LicenseRef-CSSL-1.0 + */ + +#include "gtest/gtest.h" +extern "C" { +#include "spsc_q.h" +} + +TEST(spsc_q, basic_test) { + spsc_q_t rb = spsc_q_alloc(2, sizeof(int)); + + int a = 1; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + EXPECT_FALSE(spsc_q_put(&rb, &a, sizeof(a))); + + int b; + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(a, b); + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(a, b); + EXPECT_FALSE(spsc_q_get(&rb, &b, sizeof(b))); + + spsc_q_free(&rb); +} + +TEST(spsc_q, cont_write) { + spsc_q_t rb = spsc_q_alloc(10, sizeof(int)); + + int a = 1; + for (int i = 0; i < 1111; ++i) { + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + + int b; + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - 3); + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - 2); + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - 1); + } + + spsc_q_free(&rb); +} + +bool count(const void *data, void *user) +{ + EXPECT_TRUE(data != NULL); + int *c = (int *)user; + (*c)++; + return true; /* count all */ +} +TEST(spsc_q, iterator) { + spsc_q_t rb = spsc_q_alloc(10, sizeof(int)); + int n = 8; + for (int i = 0; i < n; ++i) + EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i))); + + int c = 0; + int results[n]; + int ret = spsc_q_get_while(&rb, count, &c, results, sizeof(*results), n); + EXPECT_EQ(c, n); + EXPECT_EQ(ret, n); + for (int i = 0; i < n; ++i) + EXPECT_EQ(results[i], i); + + int b; + EXPECT_FALSE(spsc_q_get(&rb, &b, sizeof(b))); /* empty! */ + + spsc_q_free(&rb); +} + +bool drop(const void *data, void *user) +{ + const int *e = (const int *)data; + int *c = (int *)user; + EXPECT_EQ(*e, *c); + (*c)++; + return true; /* drop all */ +} +TEST(spsc_q, drop) { + spsc_q_t rb = spsc_q_alloc(10, sizeof(int)); + int n = 8; + for (int i = 0; i < n; ++i) + EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i))); + + int c = 0; + int ret = spsc_q_drop_while(&rb, drop, &c); + EXPECT_EQ(c, n); + EXPECT_EQ(ret, n); + + int b; + EXPECT_FALSE(spsc_q_get(&rb, &b, sizeof(b))); /* empty! */ + + spsc_q_free(&rb); +} + + +TEST(spsc_q, full) { + int n = 3; + spsc_q_t rb = spsc_q_alloc(n, sizeof(int)); + int a = 1; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_FALSE(spsc_q_put(&rb, &a, sizeof(a))); /* full! */ + + int b; + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - n); + + EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a))); + a++; + EXPECT_FALSE(spsc_q_put(&rb, &a, sizeof(a))); /* full! */ + + EXPECT_TRUE(spsc_q_get(&rb, &b, sizeof(b))); + EXPECT_EQ(b, a - n); + + spsc_q_free(&rb); +} + +int main(int argc, char** argv) +{ + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/common/utils/ds/tests/test_spsc_q_perf.cpp b/common/utils/ds/tests/test_spsc_q_perf.cpp new file mode 100644 index 0000000000..6487e9e6c3 --- /dev/null +++ b/common/utils/ds/tests/test_spsc_q_perf.cpp @@ -0,0 +1,75 @@ +#include "benchmark/benchmark.h" +#include "stdio.h" +#include "assert.h" +#include "pthread.h" + +extern "C" { +#include "spsc_q.h" +} + +typedef struct q_args { + spsc_q_t q; + int range; +} q_args_t; + +static void *producer(void *arg) +{ + q_args_t *a = (q_args_t *) arg; + int i = 0; + while (i <= a->range) { + if (spsc_q_put(&a->q, &i, sizeof(i))) + i++; + } + return NULL; +} + +static void *consumer(void *arg) +{ + q_args_t *a = (q_args_t *) arg; + int i = 0; + while (i != a->range) { + int r; + if (spsc_q_get(&a->q, &r, sizeof(r))) { + assert(r == i); + i++; + } + } + return NULL; +} + +static void run_test(int range, int q_size) +{ + q_args_t args = { + .q = spsc_q_alloc(q_size, sizeof(int)), + .range = range, + }; + + pthread_t p, c; + int ret; + // start + ret = pthread_create(&p, NULL, producer, &args); + assert(ret == 0); + ret = pthread_create(&c, NULL, consumer, &args); + assert(ret == 0); + + ret = pthread_join(p, NULL); + assert(ret == 0); + ret = pthread_join(c, NULL); + assert(ret == 0); + // stop + // data/time => throughput + + spsc_q_free(&args.q); +} + +static void BM_spsc_q(benchmark::State &state) +{ + int range = 500000; + int q_size = state.range(0); + for (auto _ : state) + run_test(range, q_size); +} + +BENCHMARK(BM_spsc_q)->RangeMultiplier(2)->Range(10, 160); + +BENCHMARK_MAIN(); From e0ef042fe65b2cfa8a63d16560fb748c16b2b3d2 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 3 Apr 2026 16:44:54 +0200 Subject: [PATCH 42/72] remove gnb dependence in common nr_modulation file --- openair1/PHY/MODULATION/nr_beamforming.c | 1 + openair1/PHY/MODULATION/nr_modulation.c | 11 +++--- openair1/PHY/MODULATION/nr_modulation.h | 5 ++- .../MODULATION/tests/test_nr_modulation.cpp | 34 +++++++++---------- openair1/PHY/NR_TRANSPORT/nr_dlsch.c | 2 +- 5 files changed, 26 insertions(+), 27 deletions(-) diff --git a/openair1/PHY/MODULATION/nr_beamforming.c b/openair1/PHY/MODULATION/nr_beamforming.c index 65084e401c..a10b95d6d2 100644 --- a/openair1/PHY/MODULATION/nr_beamforming.c +++ b/openair1/PHY/MODULATION/nr_beamforming.c @@ -3,6 +3,7 @@ */ #include "nr_modulation.h" +#include "openair1/PHY/TOOLS/tools_defs.h" void nr_beam_precoding(c16_t **txdataF, c16_t **txdataF_BF, diff --git a/openair1/PHY/MODULATION/nr_modulation.c b/openair1/PHY/MODULATION/nr_modulation.c index e3afd9b93f..8a1349b648 100644 --- a/openair1/PHY/MODULATION/nr_modulation.c +++ b/openair1/PHY/MODULATION/nr_modulation.c @@ -3,6 +3,7 @@ */ #include "nr_modulation.h" +#include "openair1/PHY/TOOLS/tools_defs.h" #include "PHY/NR_REFSIG/nr_mod_table.h" #include "executables/softmodem-common.h" #include @@ -688,12 +689,12 @@ c16_t nr_layer_precoder_cm(int n_layers, int symSz, c16_t datatx_F_precoding[n_layers][symSz], int ap, - nfapi_nr_pm_pdu_t *pmi_pdu, + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS], int offset) { c16_t precodatatx_F = {0}; for (int al = 0; al < n_layers; al++) { - c16_t prec_weight = pmi_pdu->weights[al][ap]; + c16_t prec_weight = weights[al][ap]; precodatatx_F = c16maddShift(datatx_F_precoding[al][offset], prec_weight, precodatatx_F, 15); } return precodatatx_F; @@ -803,15 +804,15 @@ static inline __attribute__((always_inline)) __m128i cmac_prec128(__m128i y, __m #endif #define load_consts(Type, Instruct, Rank) \ - const Type w_c##Rank = Instruct(c16toI32(c16conj(pmi_pdu->weights[Rank][ant]))); \ - const Type w_s##Rank = Instruct(c16toI32(c16swap(pmi_pdu->weights[Rank][ant]))); \ + const Type w_c##Rank = Instruct(c16toI32(c16conj(weights[Rank][ant]))); \ + const Type w_s##Rank = Instruct(c16toI32(c16swap(weights[Rank][ant]))); \ const Type *in##Rank = (Type *)(txdataF_res_mapped[Rank] + sc_offset + (out-beginning)); void nr_layer_precoder_simd(const int n_layers, const int symSz, const c16_t txdataF_res_mapped[n_layers][symSz], const int ant, - const nfapi_nr_pm_pdu_t *pmi_pdu, + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS], const int sc_offset, const int re_cnt, c16_t *txdataF_precoded) diff --git a/openair1/PHY/MODULATION/nr_modulation.h b/openair1/PHY/MODULATION/nr_modulation.h index ef834db509..769bf43da1 100644 --- a/openair1/PHY/MODULATION/nr_modulation.h +++ b/openair1/PHY/MODULATION/nr_modulation.h @@ -7,7 +7,6 @@ #include #include "PHY/defs_nr_common.h" -#include "PHY/defs_gNB.h" #define DMRS_MOD_ORDER 2 /*Precoding matices: W[pmi][antenna_port][layer]*/ @@ -127,7 +126,7 @@ c16_t nr_layer_precoder_cm(int n_layers, int symSz, c16_t datatx_F_precoding[n_layers][symSz], int ap, - nfapi_nr_pm_pdu_t *pmi_pdu, + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS], int offset); /*! \brief Precoding with SIMDe, txdataF_precoded[] = prec_matrix[] * txdataF_res_mapped[] @@ -140,7 +139,7 @@ void nr_layer_precoder_simd(const int n_layers, const int symSz, const c16_t txdataF_res_mapped[n_layers][symSz], const int ant, - const nfapi_nr_pm_pdu_t *pmi_pdu, + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS], const int sc_offset, const int re_cnt, c16_t *txdataF_precoded); diff --git a/openair1/PHY/MODULATION/tests/test_nr_modulation.cpp b/openair1/PHY/MODULATION/tests/test_nr_modulation.cpp index d232c442bf..7a66696384 100644 --- a/openair1/PHY/MODULATION/tests/test_nr_modulation.cpp +++ b/openair1/PHY/MODULATION/tests/test_nr_modulation.cpp @@ -15,7 +15,6 @@ extern "C" { #endif #include "openair1/PHY/TOOLS/tools_defs.h" // c16_t -#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface_scf.h" // nfapi_nr_pm_pdu_t #include "nfapi/open-nFAPI/nfapi/public_inc/fapi_nr_ue_interface.h" // fapi_nr_dl_config_dci_dl_pdu_rel15_t //#include "openair1/PHY/MODULATION/nr_modulation.h" // nr_layer_precoder_simd_[cm, simd] @@ -25,7 +24,7 @@ c16_t nr_layer_precoder_cm(int n_layers, int symbol_size, c16_t (*dataF_in)[/*symbol_size*/], // c16_t dataF_in[n_layers][symbol_size] int ant, - nfapi_nr_pm_pdu_t *pmi_pdu, + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS], int offset); // Forward declare the function but hide the Variable-Length Array (VLA) feature from C not available in C++ @@ -33,7 +32,7 @@ void nr_layer_precoder_simd(int n_layers, int symbol_size, const c16_t (*dataF_in)[/*symbol_size*/], // c16_t dataF_in[n_layers][symbol_size] int ant, - const nfapi_nr_pm_pdu_t *pmi_pdu, + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS], int offset, int re_cnt, c16_t *dataF_out); @@ -65,15 +64,15 @@ TEST(NrLayerPrecoderTest, Basic) c16_t(*dataF_in)[symbol_size] = reinterpret_cast(buffer_in.data()); c16_t(*dataF_out)[symbol_size] = reinterpret_cast(buffer_out.data()); - // Create and populate the pmi_pdu structure - nfapi_nr_pm_pdu_t pmi_pdu = {0}; + // Create and populate the weights + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS]; for (int layer = 0; layer < n_layers; ++layer) { - pmi_pdu.weights[layer][ant] = (c16_t){-SHRT_MAX, 0}; + weights[layer][ant] = (c16_t){-SHRT_MAX, 0}; } // Call the C function for (int symbol = 0; symbol < re_cnt; symbol++) - dataF_out[ant][symbol] = nr_layer_precoder_cm(n_layers, symbol_size, dataF_in, ant, &pmi_pdu, symbol); + dataF_out[ant][symbol] = nr_layer_precoder_cm(n_layers, symbol_size, dataF_in, ant, weights, symbol); // You can calculate the expected value manually or test rough correctness for (int symbol = 0; symbol < re_cnt; symbol++) { @@ -110,14 +109,14 @@ TEST(NrLayerPrecoderTest, SIMD) c16_t(*dataF_in)[symbol_size] = reinterpret_cast(buffer_in.data()); c16_t(*dataF_out)[symbol_size] = reinterpret_cast(buffer_out.data()); - // Create and populate the pmi_pdu structure - nfapi_nr_pm_pdu_t pmi_pdu = {0}; + // Create and populate the weights + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS]; for (int layer = 0; layer < n_layers; ++layer) { - pmi_pdu.weights[layer][ant] = (c16_t){-SHRT_MAX, 0}; + weights[layer][ant] = (c16_t){-SHRT_MAX, 0}; } // Call the C function - nr_layer_precoder_simd(n_layers, symbol_size, dataF_in, ant, &pmi_pdu, 0, re_cnt, dataF_out[ant]); + nr_layer_precoder_simd(n_layers, symbol_size, dataF_in, ant, weights, 0, re_cnt, dataF_out[ant]); // You can calculate the expected value manually or test rough correctness for (int symbol = 0; symbol < re_cnt; symbol++) { @@ -156,15 +155,14 @@ TEST(NrLayerPrecoderTest, Compare_CM_SIMD) c16_t(*dataF_out_cm)[symbol_size] = reinterpret_cast(buffer_out_cm.data()); c16_t(*dataF_out_simd)[symbol_size] = reinterpret_cast(buffer_out_simd.data()); - // Create and populate the pmi_pdu structure - nfapi_nr_pm_pdu_t pmi_pdu = {0}; - pmi_pdu.pm_idx = 4; + // Create and populate the weights + c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS]; for (int layer = 0; layer < n_layers; ++layer) { // Could not use convert_precoder_weight() as complex.h could not be used in googletest // Use the logic in convert_precoder_weight() // precoder [−1,−j] - pmi_pdu.weights[layer][0] = (c16_t){-SHRT_MAX, 0}; - pmi_pdu.weights[layer][1] = (c16_t){0, -SHRT_MAX}; + weights[layer][0] = (c16_t){-SHRT_MAX, 0}; + weights[layer][1] = (c16_t){0, -SHRT_MAX}; } // Get the results for all the antenna @@ -172,10 +170,10 @@ TEST(NrLayerPrecoderTest, Compare_CM_SIMD) // Call the C function for (int symbol = 0; symbol < re_cnt; symbol++) - dataF_out_cm[ant][symbol] = nr_layer_precoder_cm(n_layers, symbol_size, dataF_in, ant, &pmi_pdu, symbol); + dataF_out_cm[ant][symbol] = nr_layer_precoder_cm(n_layers, symbol_size, dataF_in, ant, weights, symbol); // Call the C function - nr_layer_precoder_simd(n_layers, symbol_size, dataF_in, ant, &pmi_pdu, 0, re_cnt, dataF_out_simd[ant]); + nr_layer_precoder_simd(n_layers, symbol_size, dataF_in, ant, weights, 0, re_cnt, dataF_out_simd[ant]); // Compare the result from both C function for (int symbol = 0; symbol < re_cnt; symbol++) { diff --git a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c index d227148353..8f1bb827be 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_dlsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_dlsch.c @@ -438,7 +438,7 @@ static inline void do_txdataF(c16_t **txdataF, symbol_sz, txdataF_precoding, ant, - pmi_pdu, + pmi_pdu->weights, subCarrier, re_cnt, &txdataF[ant][txdataF_offset_per_symbol]); From a1b68b9f989fb556c3d185c0c451d19e79595d50 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 3 Apr 2026 17:12:17 +0200 Subject: [PATCH 43/72] fix oran_isolate compilation --- radio/fhi_72/oran_isolate.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/radio/fhi_72/oran_isolate.c b/radio/fhi_72/oran_isolate.c index ed330ed30e..a7624f92c5 100644 --- a/radio/fhi_72/oran_isolate.c +++ b/radio/fhi_72/oran_isolate.c @@ -205,7 +205,7 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot) .prach_buf = NULL, }; - prach_item_t *prach_id = find_nr_prach(&ru->gNB_list[0]->prach_list, *frame, *slot, ru->nr_frame_parms->nb_antennas_rx, SEARCH_EXIST); + prach_item_t *prach_id = find_nr_prach(&ru->gNB_list[0]->prach_list, *frame, *slot, ru->nr_frame_parms->nb_antennas_rx, NR_SEARCH_EXIST); if (prach_id) { struct xran_fh_config *fh_cfg = get_xran_fh_config(0); int slots_per_subframe = 1 << fh_cfg->frame_conf.nNumerology; From 3ff08f25ca52b9ecb292bf8d7043be25f51ba5f8 Mon Sep 17 00:00:00 2001 From: francescomani Date: Fri, 10 Apr 2026 11:22:29 +0200 Subject: [PATCH 44/72] remove UNUSED_VARIABLE and replace with UNUSED --- common/platform_constants.h | 1 - nfapi/oai_integration/wls_integration/wls_pnf.c | 2 +- nfapi/oai_integration/wls_integration/wls_vnf.c | 2 +- openair1/PHY/LTE_UE_TRANSPORT/dci_tools_ue.c | 4 ++-- openair1/SIMULATION/TOOLS/channel_sim.c | 12 ++++++------ 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/common/platform_constants.h b/common/platform_constants.h index d7cf6f9ed6..26894fd82b 100644 --- a/common/platform_constants.h +++ b/common/platform_constants.h @@ -66,7 +66,6 @@ #define RETURNok (0) #define RETURNerror (-1) #define DEFAULT_NAS_PATH "PWD" -#define UNUSED_VARIABLE(vARIABLE) (void)(vARIABLE) #define NUM_PRACH_RX_FOR_NOISE_ESTIMATE 100 // SDAP diff --git a/nfapi/oai_integration/wls_integration/wls_pnf.c b/nfapi/oai_integration/wls_integration/wls_pnf.c index 42c09b4845..738f15a1a1 100644 --- a/nfapi/oai_integration/wls_integration/wls_pnf.c +++ b/nfapi/oai_integration/wls_integration/wls_pnf.c @@ -183,7 +183,7 @@ bool wls_pnf_nr_send_p5_message(pnf_t *pnf, nfapi_nr_p4_p5_message_header_t *msg bool wls_pnf_nr_send_p7_message(pnf_p7_t *pnf_p7, nfapi_nr_p7_message_header_t *msg, uint32_t msg_len) { - UNUSED_VARIABLE(msg_len); + UNUSED(msg_len); if (pnf_p7->terminate) { // don't send any further P7 messages after terminating return false; diff --git a/nfapi/oai_integration/wls_integration/wls_vnf.c b/nfapi/oai_integration/wls_integration/wls_vnf.c index a250188c78..7249bae551 100644 --- a/nfapi/oai_integration/wls_integration/wls_vnf.c +++ b/nfapi/oai_integration/wls_integration/wls_vnf.c @@ -410,7 +410,7 @@ int wls_fapi_nr_vnf_start(nfapi_vnf_config_t *cfg) bool wls_vnf_nr_send_p5_message(vnf_t *vnf, uint16_t p5_idx, nfapi_nr_p4_p5_message_header_t *msg, uint32_t msg_len) { - UNUSED_VARIABLE(p5_idx); + UNUSED(p5_idx); int packed_len = vnf->_public.pack_func(msg, msg_len, vnf->tx_message_buffer, sizeof(vnf->tx_message_buffer), &vnf->_public.codec_config); diff --git a/openair1/PHY/LTE_UE_TRANSPORT/dci_tools_ue.c b/openair1/PHY/LTE_UE_TRANSPORT/dci_tools_ue.c index 2f04a0eeaf..b85b05277d 100644 --- a/openair1/PHY/LTE_UE_TRANSPORT/dci_tools_ue.c +++ b/openair1/PHY/LTE_UE_TRANSPORT/dci_tools_ue.c @@ -4255,7 +4255,7 @@ int generate_ue_ulsch_params_from_dci(void *dci_pdu, LOG_D(PHY,"Format 0 DCI :ulsch (ue): cshift %d\n",ulsch->harq_processes[harq_pid]->n_DMRS2); LOG_D(PHY,"Format 0 DCI :ulsch (ue): phich status %d\n",ulsch->harq_processes[harq_pid]->status); #else - UNUSED_VARIABLE(dai); + UNUSED(dai); #endif return(0); } else { @@ -4889,7 +4889,7 @@ int generate_eNB_ulsch_params_from_dci(PHY_VARS_eNB *eNB, printf("ulsch (eNB): Nsymb_pusch %d\n",ulsch->harq_processes[harq_pid]->Nsymb_pusch); printf("ulsch (eNB): cshift %d\n",ulsch->harq_processes[harq_pid]->n_DMRS2); #else - UNUSED_VARIABLE(dai); + UNUSED(dai); #endif return(0); } else { diff --git a/openair1/SIMULATION/TOOLS/channel_sim.c b/openair1/SIMULATION/TOOLS/channel_sim.c index 80f933db69..0e6e7404d0 100644 --- a/openair1/SIMULATION/TOOLS/channel_sim.c +++ b/openair1/SIMULATION/TOOLS/channel_sim.c @@ -256,9 +256,9 @@ void do_DL_sig(sim_t *sim, LOG_D(OCM,"[SIM][DL] UE %d : rx_pwr (ADC out) %f dB/RE (%d) for subframe %d, writing to %p, length %d\n",UE_id, 10*log10((double)rx_pwr2),rx_pwr2,subframe,rxdata,lengthN_RB_DL) ,subframe); #else - UNUSED_VARIABLE(rx_pwr2); - UNUSED_VARIABLE(tx_pwr); - UNUSED_VARIABLE(rx_pwr); + UNUSED(rx_pwr2); + UNUSED(tx_pwr); + UNUSED(rx_pwr); #endif } // RU_output_mask @@ -434,8 +434,8 @@ void do_UL_sig(sim_t *sim, uint16_t subframe, uint8_t abstraction_flag, LTE_DL_F rx_pwr2 = signal_energy(rxdata[0]+sf_offset_tdd,frame_parms->samples_per_tti)*(double)frame_parms->ofdm_symbol_size/(12.0*frame_parms->N_RB_DL); LOG_D(OCM,"[SIM][UL] RU %d rx_pwr (ADC out) %f dB (%d) for subframe %d (offset %d) = %p\n",ru_id,10*log10((double)rx_pwr2),rx_pwr2,subframe,sf_offset,rxdata[0]+sf_offset_tdd); #else - UNUSED_VARIABLE(tx_pwr); - UNUSED_VARIABLE(rx_pwr); - UNUSED_VARIABLE(rx_pwr2); + UNUSED(tx_pwr); + UNUSED(rx_pwr); + UNUSED(rx_pwr2); #endif } From 17a87cd858d9f01e24742eef0d2b555ad0d2e6e6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 23 Mar 2026 12:45:49 +0100 Subject: [PATCH 45/72] Simplify BuildImage Signed-off-by: Robert Schmidt --- ci-scripts/cls_containerize.py | 105 +++++++++++---------------------- 1 file changed, 36 insertions(+), 69 deletions(-) diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index c7831136e2..461c64d133 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -184,11 +184,6 @@ class Containerize(): self.services = '' self.deploymentTag = '' - self.cli = '' - self.cliBuildOptions = '' - self.dockerfileprefix = '' - self.host = '' - self.num_attempts = 1 self.flexricTag = '' @@ -203,18 +198,7 @@ class Containerize(): cmd = cls_cmd.getConnection(node) log_files = [] - # Checking the hostname to get adapted on cli and dockerfileprefixes - cmd.run('hostnamectl') - result = re.search('Ubuntu|Red Hat', cmd.getBefore()) - self.host = result.group(0) - if self.host == 'Ubuntu': - self.cli = 'docker' - self.dockerfileprefix = '.ubuntu' - self.cliBuildOptions = '' - elif self.host == 'Red Hat': - self.cli = 'sudo podman' - self.dockerfileprefix = '.rhel9' - self.cliBuildOptions = '--disable-compression' + dockerfileprefix = '.ubuntu' # we always build the ran-build image with all targets # Creating a tupple with the imageName, the DockerFile prefix pattern, targetName and sanitized option @@ -232,23 +216,20 @@ class Containerize(): imageNames.append(('oai-nr-cuup', 'nr-cuup', 'oai-nr-cuup', '')) imageNames.append(('oai-lte-ue', 'lteUE', 'oai-lte-ue', '')) imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', '')) - if self.host == 'Red Hat': - imageNames.append(('oai-physim', 'phySim', 'oai-physim', '')) - if self.host == 'Ubuntu': - imageNames.append(('oai-lte-ru', 'lteRU', 'oai-lte-ru', '')) - # Building again the 5G images with Address Sanitizer - imageNames.append(('ran-build', 'build', 'ran-build-asan', '--build-arg "BUILD_OPTION=--sanitize"')) - imageNames.append(('oai-enb', 'eNB', 'oai-enb-asan', '--build-arg "BUILD_OPTION=--sanitize"')) - imageNames.append(('oai-gnb', 'gNB', 'oai-gnb-asan', '--build-arg "BUILD_OPTION=--sanitize"')) - imageNames.append(('oai-lte-ue', 'lteUE', 'oai-lte-ue-asan', '--build-arg "BUILD_OPTION=--sanitize"')) - imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue-asan', '--build-arg "BUILD_OPTION=--sanitize"')) - imageNames.append(('oai-nr-cuup', 'nr-cuup', 'oai-nr-cuup-asan', '--build-arg "BUILD_OPTION=--sanitize"')) - imageNames.append(('ran-build-fhi72', 'build.fhi72', 'ran-build-fhi72', '')) - imageNames.append(('oai-gnb', 'gNB.fhi72', 'oai-gnb-fhi72', '')) - imageNames.append(('oai-nr-oru', 'nrORU.fhi72', 'oai-nr-oru', '')) + imageNames.append(('oai-lte-ru', 'lteRU', 'oai-lte-ru', '')) + # Building again the 5G images with Address Sanitizer + imageNames.append(('ran-build', 'build', 'ran-build-asan', '--build-arg "BUILD_OPTION=--sanitize"')) + imageNames.append(('oai-enb', 'eNB', 'oai-enb-asan', '--build-arg "BUILD_OPTION=--sanitize"')) + imageNames.append(('oai-gnb', 'gNB', 'oai-gnb-asan', '--build-arg "BUILD_OPTION=--sanitize"')) + imageNames.append(('oai-lte-ue', 'lteUE', 'oai-lte-ue-asan', '--build-arg "BUILD_OPTION=--sanitize"')) + imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue-asan', '--build-arg "BUILD_OPTION=--sanitize"')) + imageNames.append(('oai-nr-cuup', 'nr-cuup', 'oai-nr-cuup-asan', '--build-arg "BUILD_OPTION=--sanitize"')) + imageNames.append(('ran-build-fhi72', 'build.fhi72', 'ran-build-fhi72', '')) + imageNames.append(('oai-gnb', 'gNB.fhi72', 'oai-gnb-fhi72', '')) + imageNames.append(('oai-nr-oru', 'nrORU.fhi72', 'oai-nr-oru', '')) result = re.search('build_cross_arm64', self.imageKind) if result is not None: - self.dockerfileprefix = '.ubuntu.cross-arm64' + dockerfileprefix = '.ubuntu.cross-arm64' result = re.search('native_armv9', self.imageKind) if result is not None: imageNames.append(('oai-gnb', 'gNB', 'oai-gnb', '')) @@ -263,10 +244,6 @@ class Containerize(): imageNames.append(('oai-nr-ue', 'nrUE', 'oai-nr-ue', '')) cmd.cd(lSourcePath) - # if asterix, copy the entitlement and subscription manager configurations - if self.host == 'Red Hat': - cmd.run('mkdir -p ./etc-pki-entitlement') - cmd.run('cp /etc/pki/entitlement/*.pem ./etc-pki-entitlement/') baseImage = 'ran-base' baseTag = 'develop' @@ -275,7 +252,7 @@ class Containerize(): if (self.ranAllowMerge): imageTag = 'ci-temp' if self.ranTargetBranch == 'develop': - cmd.run(f'git diff HEAD..origin/develop -- cmake_targets/build_oai cmake_targets/tools/build_helper docker/Dockerfile.base{self.dockerfileprefix} | grep --colour=never -i INDEX') + cmd.run(f'git diff HEAD..origin/develop -- cmake_targets/build_oai cmake_targets/tools/build_helper docker/Dockerfile.base{dockerfileprefix} | grep --colour=never -i INDEX') result = re.search('index', cmd.getBefore()) if result is not None: forceBaseImageBuild = True @@ -289,31 +266,30 @@ class Containerize(): forceBaseImageBuild = True # Let's remove any previous run artifacts if still there - cmd.run(f"{self.cli} image prune --force") + cmd.run(f"docker image prune --force") for image,pattern,name,option in imageNames: - cmd.run(f"{self.cli} image rm {name}:{imageTag}", reportNonZero=False) + cmd.run(f"docker image rm {name}:{imageTag}", reportNonZero=False) cmd.run(f'docker login -u oaicicd -p oaicicd {DEFAULT_REGISTRY}') ubuntuImage = "ubuntu:noble" # Build the base image only on Push Events (not on Merge Requests) # On when the base image docker file is being modified. if forceBaseImageBuild: - cmd.run(f"{self.cli} image rm {baseImage}:{baseTag}") + cmd.run(f"docker image rm {baseImage}:{baseTag}") logfile = f'{lSourcePath}/cmake_targets/log/ran-base.docker.log' - if self.host == 'Ubuntu': - option = f" --build-arg UBUNTU_IMAGE={DEFAULT_REGISTRY}/{ubuntuImage}" - cmd.run(f"{self.cli} build {self.cliBuildOptions} --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{self.dockerfileprefix} {option} . &> {logfile}", timeout=1600) + option = f" --build-arg UBUNTU_IMAGE={DEFAULT_REGISTRY}/{ubuntuImage}" + cmd.run(f"docker build --target {baseImage} --tag {baseImage}:{baseTag} --file docker/Dockerfile.base{dockerfileprefix} {option} . &> {logfile}", timeout=1600) t = ("ran-base", archiveArtifact(cmd, ctx, logfile)) log_files.append(t) # First verify if the base image was properly created. - ret = cmd.run(f"{self.cli} image inspect --format=\'Size = {{{{.Size}}}} bytes\' {baseImage}:{baseTag}") + ret = cmd.run(f"docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' {baseImage}:{baseTag}") allImagesSize = {} if ret.returncode != 0: logging.error('\u001B[1m Could not build properly ran-base\u001B[0m') # Recover the name of the failed container? - cmd.run(f"{self.cli} ps --quiet --filter \"status=exited\" -n1 | xargs --no-run-if-empty {self.cli} rm -f") - cmd.run(f"{self.cli} image prune --force") + cmd.run(f"docker ps --quiet --filter \"status=exited\" -n1 | xargs --no-run-if-empty docker rm -f") + cmd.run(f"docker image prune --force") cmd.close() logging.error('\u001B[1m Building OAI Images Failed\u001B[0m') HTML.CreateHtmlTestRow(self.imageKind, 'KO', CONST.ALL_PROCESSES_OK) @@ -333,32 +309,31 @@ class Containerize(): for image,pattern,name,option in imageNames: # the archived Dockerfiles have "ran-base:latest" as base image # we need to update them with proper tag - cmd.run(f'git checkout -- docker/Dockerfile.{pattern}{self.dockerfileprefix}') - cmd.run(f'sed -i -e "s#{baseImage}:latest#{baseImage}:{baseTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}') + cmd.run(f'git checkout -- docker/Dockerfile.{pattern}{dockerfileprefix}') + cmd.run(f'sed -i -e "s#{baseImage}:latest#{baseImage}:{baseTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}') # target images should use the proper ran-build image if image != 'ran-build' and "-asan" in name: - cmd.run(f'sed -i -e "s#ran-build:latest#ran-build-asan:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}') + cmd.run(f'sed -i -e "s#ran-build:latest#ran-build-asan:{imageTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}') elif "fhi72" in name or name == "oai-nr-oru": - cmd.run(f'sed -i -e "s#ran-build-fhi72:latest#ran-build-fhi72:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}') + cmd.run(f'sed -i -e "s#ran-build-fhi72:latest#ran-build-fhi72:{imageTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}') elif image != 'ran-build': - cmd.run(f'sed -i -e "s#ran-build:latest#ran-build:{imageTag}#" docker/Dockerfile.{pattern}{self.dockerfileprefix}') + cmd.run(f'sed -i -e "s#ran-build:latest#ran-build:{imageTag}#" docker/Dockerfile.{pattern}{dockerfileprefix}') if image == 'oai-gnb-aerial': cmd.run('cp -f /opt/nvidia-ipc/nvipc_src.2026.01.07.tar.gz .') logfile = f'{lSourcePath}/cmake_targets/log/{name}.docker.log' - if self.host == 'Ubuntu': - option = option + f" --build-arg UBUNTU_IMAGE={DEFAULT_REGISTRY}/{ubuntuImage}" - ret = cmd.run(f'{self.cli} build {self.cliBuildOptions} --target {image} --tag {name}:{imageTag} --file docker/Dockerfile.{pattern}{self.dockerfileprefix} {option} . > {logfile} 2>&1', timeout=1200) + option = option + f" --build-arg UBUNTU_IMAGE={DEFAULT_REGISTRY}/{ubuntuImage}" + ret = cmd.run(f'docker build --target {image} --tag {name}:{imageTag} --file docker/Dockerfile.{pattern}{dockerfileprefix} {option} . > {logfile} 2>&1', timeout=1200) t = (name, archiveArtifact(cmd, ctx, logfile)) log_files.append(t) if image == 'oai-gnb-aerial': cmd.run('rm -f nvipc_src.2026.01.07.tar.gz') # check the status of the build - ret = cmd.run(f"{self.cli} image inspect --format=\'Size = {{{{.Size}}}} bytes\' {name}:{imageTag}") + ret = cmd.run(f"docker image inspect --format=\'Size = {{{{.Size}}}} bytes\' {name}:{imageTag}") if ret.returncode != 0: logging.error('\u001B[1m Could not build properly ' + name + '\u001B[0m') status = False # Here we should check if the last container corresponds to a failed command and destroy it - cmd.run(f"{self.cli} ps --quiet --filter \"status=exited\" -n1 | xargs --no-run-if-empty {self.cli} rm -f") + cmd.run(f"docker ps --quiet --filter \"status=exited\" -n1 | xargs --no-run-if-empty docker rm -f") allImagesSize[name] = 'N/A -- Build Failed' break else: @@ -372,16 +347,16 @@ class Containerize(): logging.debug(f'{name} size is unknown') allImagesSize[name] = 'unknown' # Now pruning dangling images in between target builds - cmd.run(f"{self.cli} image prune --force") + cmd.run(f"docker image prune --force") cmd.run(f'docker logout {DEFAULT_REGISTRY}') # Remove all intermediate build images and clean up - cmd.run(f"{self.cli} image rm ran-build:{imageTag} ran-build-asan:{imageTag} ran-build-fhi72:{imageTag} || true") - cmd.run(f"{self.cli} volume prune --force") + cmd.run(f"docker image rm ran-build:{imageTag} ran-build-asan:{imageTag} ran-build-fhi72:{imageTag} || true") + cmd.run(f"docker volume prune --force") # Remove some cached artifacts to prevent out of diskspace problem logging.debug(cmd.run("df -h").stdout) logging.debug(cmd.run("docker system df").stdout) - cmd.run(f"{self.cli} buildx prune --filter until=1h --force") + cmd.run(f"docker buildx prune --filter until=1h --force") logging.debug(cmd.run("df -h").stdout) logging.debug(cmd.run("docker system df").stdout) @@ -489,20 +464,12 @@ class Containerize(): cmd = cls_cmd.getConnection(node) cmd.cd(lSourcePath) - ret = cmd.run('hostnamectl') - result = re.search('Ubuntu', ret.stdout) - host = result.group(0) - if host != 'Ubuntu': - cmd.close() - raise Exception("Can build unit tests only on Ubuntu server") - logging.debug('running on Ubuntu as expected') - # check that ran-base image exists as we expect it baseImage = 'ran-base' baseTag = 'develop' if self.ranAllowMerge: if self.ranTargetBranch == 'develop': - cmd.run(f'git diff HEAD..origin/develop -- cmake_targets/build_oai cmake_targets/tools/build_helper docker/Dockerfile.base{self.dockerfileprefix} | grep --colour=never -i INDEX') + cmd.run(f'git diff HEAD..origin/develop -- cmake_targets/build_oai cmake_targets/tools/build_helper docker/Dockerfile.base.ubuntu | grep --colour=never -i INDEX') result = re.search('index', cmd.getBefore()) if result is not None: baseTag = 'ci-temp' From b15d39421234e0562b68e043a5e8c55d3b22bfc8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 15 Apr 2026 12:57:44 +0200 Subject: [PATCH 46/72] Improve spsc_q through atomics Because it is SPSC, atomic variables are enough to synchronize the two threads. Concretely put() is modified to ensure that read_idx is "acquired" so that the read index has been written, including memory, by the other thread. "Release" ensures that the write operation (including the memory to the buffer) is written before it is visible to the other thread in get() (which in turn "acquires" it). For more information, see also [1]. The rest of the library has been simplified to work only with put() and get(), reducing total code and the surface for possible bugs. This (and the previous) version has been tested with the threadSanitizer: TSAN_OPTIONS=halt_on_error=1 ./common/utils/ds/tests/test_spsc_q_perf On my machine, using Google Benchmark, I measure a considerable 5x speed improvement: $ /tmp/benchmark/tools/compare.py benchmarks pthread.json atomic.json Comparing pthread.json to atomic.json Benchmark Time CPU Time Old Time New CPU Old CPU New -------------------------------------------------------------------------------------------------------------- BM_spsc_q/10 -0.8201 -0.0740 266779912 47989020 52387 48512 BM_spsc_q/16 -0.8301 -0.0520 249656592 42428540 51462 48784 BM_spsc_q/32 -0.8003 -0.0841 230248798 45972155 53841 49311 BM_spsc_q/64 -0.7995 -0.0506 210429791 42199674 50690 48124 BM_spsc_q/128 -0.7930 -0.1101 205212273 42483155 52745 46936 BM_spsc_q/160 -0.7880 -0.1663 216644738 45938247 53400 44518 OVERALL_GEOMEAN -0.8057 -0.0904 0 0 0 0 [1] https://en.cppreference.com/w/c/atomic/memory_order.html Assisted-By: Claude:claude-sonnet-4-6 Signed-off-by: Robert Schmidt --- common/utils/ds/spsc_q.c | 48 +++++++++++----------------------------- common/utils/ds/spsc_q.h | 13 +++++++---- 2 files changed, 22 insertions(+), 39 deletions(-) diff --git a/common/utils/ds/spsc_q.c b/common/utils/ds/spsc_q.c index fdc97f00b8..6f05a19865 100644 --- a/common/utils/ds/spsc_q.c +++ b/common/utils/ds/spsc_q.c @@ -8,9 +8,6 @@ #include "spsc_q.h" -#define rb_lock(LOCK) if (pthread_mutex_lock(LOCK) != 0) abort() -#define rb_unlock(LOCK) if (pthread_mutex_unlock(LOCK) != 0) abort() - spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz) { /* internally, use one element more: the ringbuffer is full if @@ -20,51 +17,47 @@ spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz) cnt += 1; spsc_q_t rb = {.cnt = cnt, .elsiz = elsiz}; rb.buf = calloc(cnt, elsiz); - pthread_mutex_init(&rb.mtx, NULL); return rb; } void spsc_q_free(spsc_q_t *rb) { free(rb->buf); - pthread_mutex_destroy(&rb->mtx); memset(rb, 0, sizeof(*rb)); } bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz) { assert(elsiz == rb->elsiz); - rb_lock(&rb->mtx); - if ((rb->write_idx + 1) % rb->cnt == rb->read_idx) { - rb_unlock(&rb->mtx); + size_t w = atomic_load_explicit(&rb->write_idx, memory_order_relaxed); + size_t r = atomic_load_explicit(&rb->read_idx, memory_order_acquire); + if ((w + 1) % rb->cnt == r) { return false; } - uint8_t *bufpos = &rb->buf[rb->write_idx * rb->elsiz]; + uint8_t *bufpos = &rb->buf[w * rb->elsiz]; memcpy(bufpos, src, elsiz); - rb->write_idx = (rb->write_idx + 1) % rb->cnt; - rb_unlock(&rb->mtx); + atomic_store_explicit(&rb->write_idx, (w + 1) % rb->cnt, memory_order_release); return true; } bool spsc_q_get_if(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz) { assert(elsiz == rb->elsiz); - rb_lock(&rb->mtx); - if (rb->write_idx == rb->read_idx) { - rb_unlock(&rb->mtx); + size_t w = atomic_load_explicit(&rb->write_idx, memory_order_acquire); + size_t r = atomic_load_explicit(&rb->read_idx, memory_order_relaxed); + if (w == r) { return false; } uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; if (!p(bufpos, user)) { - rb_unlock(&rb->mtx); return false; } - memcpy(dest, bufpos, elsiz); - rb->read_idx = (rb->read_idx + 1) % rb->cnt; - rb_unlock(&rb->mtx); + if (dest) + memcpy(dest, bufpos, elsiz); + atomic_store_explicit(&rb->read_idx, (r + 1) % rb->cnt, memory_order_release); return true; } @@ -82,31 +75,16 @@ bool spsc_q_get(spsc_q_t *rb, void *dest, size_t elsiz) int spsc_q_get_while(spsc_q_t *rb, pred p, void *user, void *dest, size_t elsiz, size_t max_len) { assert(elsiz == rb->elsiz); - rb_lock(&rb->mtx); size_t count = 0; - while (rb->write_idx != rb->read_idx && count < max_len) { - uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; - if (!p(bufpos, user)) - break; - memcpy(dest + count * elsiz, bufpos, elsiz); + while (count < max_len && spsc_q_get_if(rb, p, user, dest + elsiz * count, elsiz)) count++; - rb->read_idx = (rb->read_idx + 1) % rb->cnt; - } - rb_unlock(&rb->mtx); return count; } int spsc_q_drop_while(spsc_q_t *rb, pred p, void *user) { - rb_lock(&rb->mtx); size_t dropped = 0; - while (rb->write_idx != rb->read_idx) { - uint8_t *bufpos = &rb->buf[rb->read_idx * rb->elsiz]; - if (!p(bufpos, user)) - break; + while (spsc_q_get_if(rb, p, user, NULL, rb->elsiz)) dropped++; - rb->read_idx = (rb->read_idx + 1) % rb->cnt; - } - rb_unlock(&rb->mtx); return dropped; } diff --git a/common/utils/ds/spsc_q.h b/common/utils/ds/spsc_q.h index d00cb4b77d..7fd968b2f0 100644 --- a/common/utils/ds/spsc_q.h +++ b/common/utils/ds/spsc_q.h @@ -7,15 +7,20 @@ #include #include -#include +#include + +#if defined(__cplusplus) +// use atomic_size_t to help interworking with C++ +using std::atomic_size_t; +#endif typedef struct spsc_q { uint8_t *buf; size_t elsiz; size_t cnt; - size_t write_idx; - size_t read_idx; - pthread_mutex_t mtx; + + atomic_size_t write_idx; + atomic_size_t read_idx; } spsc_q_t; spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz); From 85b4057bde17f864deda55ea445fd48643f21a90 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 17 Mar 2026 08:42:59 +0100 Subject: [PATCH 47/72] Refactor PUCCH processing into its own function Signed-off-by: Robert Schmidt --- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 4 +- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 6 +-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 52 +++++++++---------- 3 files changed, 29 insertions(+), 33 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index f7497a148a..eaf7fcbfb5 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -227,14 +227,14 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_uci_pucch_pdu_format_2_3_4_t* uci_pdu, - nfapi_nr_pucch_pdu_t* pucch_pdu); + const nfapi_nr_pucch_pdu_t* pucch_pdu); void nr_decode_pucch0(PHY_VARS_gNB *gNB, c16_t **rxdataF, int frame, int slot, nfapi_nr_uci_pucch_pdu_format_0_1_t* uci_pdu, - nfapi_nr_pucch_pdu_t* pucch_pdu); + const nfapi_nr_pucch_pdu_t* pucch_pdu); #endif /*__NR_TRANSPORT__H__*/ diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 8017fb0bc9..09af58141e 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -66,7 +66,7 @@ void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t LOG_W(PHY, "PUCCH list is full\n"); } -int get_pucch0_cs_lut_index(PHY_VARS_gNB *gNB, nfapi_nr_pucch_pdu_t *pucch_pdu) +int get_pucch0_cs_lut_index(PHY_VARS_gNB *gNB, const nfapi_nr_pucch_pdu_t *pucch_pdu) { int i = 0; @@ -129,7 +129,7 @@ void nr_decode_pucch0(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu, - nfapi_nr_pucch_pdu_t *pucch_pdu) + const nfapi_nr_pucch_pdu_t *pucch_pdu) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; int soffset = (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size; @@ -1132,7 +1132,7 @@ void nr_decode_pucch2(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu, - nfapi_nr_pucch_pdu_t *pucch_pdu) + const nfapi_nr_pucch_pdu_t *pucch_pdu) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; // pucch_GroupHopping_t pucch_GroupHopping = pucch_pdu->group_hop_flag + (pucch_pdu->sequence_hop_flag<<1); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 7a5af5b070..11b8da6b73 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -989,6 +989,28 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, } } +static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_t *pucch, nfapi_nr_uci_t *uci) +{ + const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; + + switch (pucch_pdu->format_type) { + case 0: + uci->pdu_type = NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE; + uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_0_1_t); + nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu_format0 = &uci->pucch_pdu_format_0_1; + nr_decode_pucch0(gNB, rxdataF, pucch->frame, pucch->slot, uci_pdu_format0, pucch_pdu); + break; + case 2: + uci->pdu_type = NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE; + uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_2_3_4_t); + nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu_format2 = &uci->pucch_pdu_format_2_3_4; + nr_decode_pucch2(gNB, rxdataF, pucch->frame, pucch->slot, uci_pdu_format2, pucch_pdu); + break; + default: + AssertFatal(1 == 0, "Only PUCCH formats 0 and 2 are currently supported\n"); + } +} + int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO) { /* those variables to log T_GNB_PHY_PUCCH_PUSCH_IQ only when we try to decode */ @@ -1022,44 +1044,18 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N gNB_I0_measurements(gNB, slot_rx, first_symb, num_symb, rb_mask_ul); } - const int soffset = (slot_rx & 3) * frame_parms->symbols_per_slot * ofdm_symbol_size; start_meas(&gNB->phy_proc_rx); - for (int i = 0; i < gNB->max_nb_pucch; i++) { NR_gNB_PUCCH_t *pucch = &gNB->pucch[i]; if (!(pucch && pucch->active && pucch->frame == frame_rx && pucch->slot == slot_rx)) continue; c16_t **rxdataF = gNB->common_vars.rxdataF[pucch->beam_nb]; pucch_decode_done = 1; - nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; UL_INFO->uci_ind.uci_list = UL_INFO->uci_pdu_list; - nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list + UL_INFO->uci_ind.num_ucis; UL_INFO->uci_ind.sfn = frame_rx; UL_INFO->uci_ind.slot = slot_rx; - switch (pucch_pdu->format_type) { - case 0: - uci->pdu_type = NFAPI_NR_UCI_FORMAT_0_1_PDU_TYPE; - uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_0_1_t); - nfapi_nr_uci_pucch_pdu_format_0_1_t *uci_pdu_format0 = &uci->pucch_pdu_format_0_1; - int offset = - pucch_pdu->start_symbol_index * ofdm_symbol_size + (frame_parms->first_carrier_offset + pucch_pdu->prb_start * 12); - LOG_D(NR_PHY, - "frame %d, slot %d: PUCCH signal energy %d\n", - frame_rx, - slot_rx, - signal_energy_nodc(&rxdataF[0][soffset + offset], 12)); - nr_decode_pucch0(gNB, rxdataF, frame_rx, slot_rx, uci_pdu_format0, pucch_pdu); - break; - case 2: - uci->pdu_type = NFAPI_NR_UCI_FORMAT_2_3_4_PDU_TYPE; - uci->pdu_size = sizeof(nfapi_nr_uci_pucch_pdu_format_2_3_4_t); - nfapi_nr_uci_pucch_pdu_format_2_3_4_t *uci_pdu_format2 = &uci->pucch_pdu_format_2_3_4; - LOG_D(PHY, "%d.%d Calling nr_decode_pucch2\n", frame_rx, slot_rx); - nr_decode_pucch2(gNB, rxdataF, frame_rx, slot_rx, uci_pdu_format2, pucch_pdu); - break; - default: - AssertFatal(1 == 0, "Only PUCCH formats 0 and 2 are currently supported\n"); - } + nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list + UL_INFO->uci_ind.num_ucis; + handle_pucch(gNB, rxdataF, pucch, uci); UL_INFO->uci_ind.num_ucis += 1; pucch->active = false; } From e8e3f60e73b119888d250e5d6b3146d1122a41a8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 8 Apr 2026 13:40:45 +0200 Subject: [PATCH 48/72] Use spsc_q for PUCCH processing Use the spsc queue to pass jobs from TX thread (scheduler) to RX thread (handling those jobs), reducing the amount spent searching for PUCCH jobs in an array, clarifying the design, and making it thread-safe. Signed-off-by: Robert Schmidt --- CMakeLists.txt | 2 +- openair1/PHY/INIT/nr_init.c | 9 +-- openair1/PHY/NR_TRANSPORT/pucch_rx.c | 52 +++++++-------- openair1/PHY/defs_gNB.h | 7 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 79 +++++++++++++---------- 5 files changed, 74 insertions(+), 75 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 665904c53f..6d15f78a29 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1070,7 +1070,7 @@ add_library(PHY_NR_COMMON ${PHY_NR_SRC_COMMON}) target_link_libraries(PHY_NR_COMMON PUBLIC UTIL) add_library(PHY_NR ${PHY_NR_SRC}) -target_link_libraries(PHY_NR nr_phy_common nr_common nr_fapi_p5 polar smallblock) +target_link_libraries(PHY_NR nr_phy_common nr_common nr_fapi_p5 polar smallblock ds) add_library(PHY_NR_NO_AVX_256 ${PHY_NR_SRC}) target_link_libraries(PHY_NR_NO_AVX_256 nr_phy_common nr_common) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 59bdd7012d..8a2a794fbd 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -437,14 +437,11 @@ void init_nr_transport(PHY_VARS_gNB *gNB) else buffer_ul_slots = (nb_ul_slots_period < slot_ahead) ? nb_ul_slots_period : slot_ahead; - gNB->max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; gNB->max_nb_pusch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; gNB->max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot - gNB->pucch = (NR_gNB_PUCCH_t *)malloc16(gNB->max_nb_pucch * sizeof(NR_gNB_PUCCH_t)); - for (int i = 0; i < gNB->max_nb_pucch; i++) { - memset(&gNB->pucch[i], 0, sizeof(gNB->pucch[i])); - } + int max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; + gNB->pucch_queue = spsc_q_alloc(max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t)); gNB->srs = (NR_gNB_SRS_t *)malloc16(gNB->max_nb_srs * sizeof(NR_gNB_SRS_t)); for (int i = 0; i < gNB->max_nb_srs; i++) @@ -465,7 +462,7 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) { const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - free(gNB->pucch); + spsc_q_free(&gNB->pucch_queue); free(gNB->srs); for (int i = 0; i < gNB->max_nb_pusch; i++) diff --git a/openair1/PHY/NR_TRANSPORT/pucch_rx.c b/openair1/PHY/NR_TRANSPORT/pucch_rx.c index 09af58141e..fd6bd8ea42 100644 --- a/openair1/PHY/NR_TRANSPORT/pucch_rx.c +++ b/openair1/PHY/NR_TRANSPORT/pucch_rx.c @@ -33,37 +33,29 @@ //#define DEBUG_NR_PUCCH_RX 1 void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pucch_pdu_t *pucch_pdu) { - for (int i = 0; i < gNB->max_nb_pucch; i++) { - NR_gNB_PUCCH_t *pucch = &gNB->pucch[i]; - if (pucch->active == false) { - pucch->frame = frame; - pucch->slot = slot; - pucch->active = true; - pucch->beam_nb = 0; - if (gNB->common_vars.beam_id) { - int fapi_beam_idx = pucch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; - int bitmap = SL_to_bitmap(pucch_pdu->start_symbol_index, pucch_pdu->nr_of_symbols); - pucch->beam_nb = beam_index_allocation(gNB->enable_analog_das, - fapi_beam_idx, - &gNB->common_vars, - slot, - gNB->frame_parms.symbols_per_slot, - bitmap); - } - memcpy((void *)&pucch->pucch_pdu, (void *)pucch_pdu, sizeof(nfapi_nr_pucch_pdu_t)); - LOG_D(PHY, - "Programming PUCCH[%d] for %d.%d, format %d, nb_harq %d, nb_sr %d, nb_csi %d\n", - i, - pucch->frame, - pucch->slot, - pucch->pucch_pdu.format_type, - pucch->pucch_pdu.bit_len_harq, - pucch->pucch_pdu.sr_flag, - pucch->pucch_pdu.bit_len_csi_part1); - return; - } + LOG_D(PHY, + "%4d.%2d UE %04x Programming PUCCH format %d, nb_harq %d, nb_sr %d, nb_csi %d\n", + frame, + slot, + pucch_pdu->rnti, + pucch_pdu->format_type, + pucch_pdu->bit_len_harq, + pucch_pdu->sr_flag, + pucch_pdu->bit_len_csi_part1); + NR_gNB_PUCCH_job_t pucch = {.frame = frame, .slot = slot, .pucch_pdu = *pucch_pdu}; + if (gNB->common_vars.beam_id) { + int fapi_beam_idx = pucch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; + int bitmap = SL_to_bitmap(pucch_pdu->start_symbol_index, pucch_pdu->nr_of_symbols); + pucch.beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->common_vars, + slot, + gNB->frame_parms.symbols_per_slot, + bitmap); } - LOG_W(PHY, "PUCCH list is full\n"); + bool found = spsc_q_put(&gNB->pucch_queue, &pucch, sizeof(pucch)); + if (!found) + LOG_W(NR_PHY, "PUCCH list is full: dropping PUCCH UE %04x\n", pucch_pdu->rnti); } int get_pucch0_cs_lut_index(PHY_VARS_gNB *gNB, const nfapi_nr_pucch_pdu_t *pucch_pdu) diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index a6fb9d8890..9657cef20d 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -19,6 +19,7 @@ #include "nfapi_nr_interface_scf.h" #include "common/utils/threadPool/task_ans.h" #include "openair1/PHY/defs_RU.h" +#include "common/utils/ds/spsc_q.h" #define MAX_NUM_RU_PER_gNB 8 #define MAX_PUCCH0_NID 8 @@ -190,7 +191,6 @@ typedef struct { } NR_gNB_ULSCH_t; typedef struct { - bool active; // identifier for concurrent beams int beam_nb; /// Frame where current PUCCH pdu was sent @@ -199,7 +199,7 @@ typedef struct { uint32_t slot; /// ULSCH PDU nfapi_nr_pucch_pdu_t pucch_pdu; -} NR_gNB_PUCCH_t; +} NR_gNB_PUCCH_job_t; typedef struct { bool active; @@ -369,7 +369,6 @@ typedef struct PHY_VARS_gNB_s { nfapi_nr_ul_tti_request_t UL_tti_req; - int max_nb_pucch; int max_nb_srs; int max_nb_pdsch; int max_nb_pusch; @@ -380,7 +379,7 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_DLSCH_t *dlsch; NR_gNB_PRS prs_vars; NR_gNB_PUSCH *pusch_vars; - NR_gNB_PUCCH_t *pucch; + spsc_q_t pucch_queue; NR_gNB_SRS_t *srs; NR_gNB_ULSCH_t *ulsch; NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB]; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 11b8da6b73..1f28622231 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -2,6 +2,7 @@ * SPDX-License-Identifier: LicenseRef-CSSL-1.0 */ +#include "common/utils/fsn.h" #include "PHY/defs_gNB.h" #include "sched_nr.h" #include "PHY/NR_TRANSPORT/nr_transport_proto.h" @@ -613,10 +614,11 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, // Function to fill UL RB mask to be used for N0 measurements static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, - int frame_rx, - int slot_rx, + fsn_t now, uint32_t rb_mask_ul[14][9], - nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf) + nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf, + const NR_gNB_PUCCH_job_t *pucch, + int n_pucch) { for (int symbol = 0; symbol < 14; symbol++) { for (int m = 0; m < 9; m++) { @@ -630,20 +632,9 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, } } - for (int i = 0; i < gNB->max_nb_pucch; i++){ - const NR_gNB_PUCCH_t *pucch = gNB->pucch + i; - if (!(pucch && pucch->active && pucch->frame == frame_rx && pucch->slot == slot_rx)) - continue; - const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; + for (int i = 0; i < n_pucch; ++i) { + const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch[i].pucch_pdu; const int start = pucch_pdu->start_symbol_index; - LOG_D(PHY, - "%d.%d pucch %d : start_symbol %d, nb_symbols %d, prb_size %d\n", - frame_rx, - slot_rx, - i, - start, - pucch_pdu->nr_of_symbols, - pucch_pdu->prb_size); for (int symbol = start; symbol < (start + pucch_pdu->nr_of_symbols); symbol++) { if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) { for (int rb = 0; rb < pucch_pdu->prb_size; rb++) { @@ -661,7 +652,7 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); - if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !ulsch->handled)) + if (!(ulsch->active && ulsch->frame == now.f && ulsch->slot == now.s && !ulsch->handled)) continue; uint8_t symbol_start = ulsch_harq->ulsch_pdu.start_symbol_index; uint8_t symbol_end = symbol_start + ulsch_harq->ulsch_pdu.nr_of_symbols; @@ -678,7 +669,7 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, for (int i = 0; i < gNB->max_nb_srs; i++) { NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (!(srs && srs->active && srs->frame == frame_rx && srs->slot == slot_rx)) + if (!(srs && srs->active && srs->frame == now.f && srs->slot == now.s)) continue; nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; @@ -989,7 +980,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, } } -static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_t *pucch, nfapi_nr_uci_t *uci) +static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_job_t *pucch, nfapi_nr_uci_t *uci) { const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; @@ -1011,16 +1002,41 @@ static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_ } } +static bool drop_old_pucch(const void *data, void *user) +{ + const NR_gNB_PUCCH_job_t *pucch = data; + const fsn_t *now = user; + const fsn_t t = {pucch->frame, pucch->slot, now->mu}; + bool drop = fsn_in_the_past(t, *now); + if (drop) + LOG_E(NR_PHY, "%4d.%2d PUCCH job for UE %04x is in the past (%4d.%2d)\n", now->f, now->s, pucch->pucch_pdu.rnti, t.f, t.s); + return drop; +} + +static bool get_current_pucch(const void *data, void *user) +{ + const NR_gNB_PUCCH_job_t *pucch = data; + const fsn_t *now = user; + const fsn_t t = {pucch->frame, pucch->slot, now->mu}; + return fsn_equal(t, *now); +} + int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO) { /* those variables to log T_GNB_PHY_PUCCH_PUSCH_IQ only when we try to decode */ - int pucch_decode_done = 0; int pusch_decode_done = 0; int pusch_DTX = 0; const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; const uint16_t ofdm_symbol_size = frame_parms->ofdm_symbol_size; const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; + + fsn_t now = {frame_rx, slot_rx, frame_parms->numerology_index}; + + spsc_q_drop_while(&gNB->pucch_queue, drop_old_pucch, &now); + NR_gNB_PUCCH_job_t pucch[MAX_NUM_NR_UCI_PDUS]; + int n_pucch = spsc_q_get_while(&gNB->pucch_queue, get_current_pucch, &now, pucch, sizeof(*pucch), MAX_NUM_NR_UCI_PDUS); + LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB @@ -1028,7 +1044,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL; if (frame_parms->frame_type == TDD) slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; - fill_ul_rb_mask(gNB, frame_rx, slot_rx, rb_mask_ul, slot_conf); + fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch); int first_symb = 0, num_symb = 0; if (frame_parms->frame_type == TDD) @@ -1045,19 +1061,14 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N } start_meas(&gNB->phy_proc_rx); - for (int i = 0; i < gNB->max_nb_pucch; i++) { - NR_gNB_PUCCH_t *pucch = &gNB->pucch[i]; - if (!(pucch && pucch->active && pucch->frame == frame_rx && pucch->slot == slot_rx)) - continue; + UL_INFO->uci_ind.uci_list = UL_INFO->uci_pdu_list; + UL_INFO->uci_ind.sfn = frame_rx; + UL_INFO->uci_ind.slot = slot_rx; + UL_INFO->uci_ind.num_ucis = n_pucch; + nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list; + for (int i = 0; i < n_pucch; ++i) { c16_t **rxdataF = gNB->common_vars.rxdataF[pucch->beam_nb]; - pucch_decode_done = 1; - UL_INFO->uci_ind.uci_list = UL_INFO->uci_pdu_list; - UL_INFO->uci_ind.sfn = frame_rx; - UL_INFO->uci_ind.slot = slot_rx; - nfapi_nr_uci_t *uci = UL_INFO->uci_ind.uci_list + UL_INFO->uci_ind.num_ucis; - handle_pucch(gNB, rxdataF, pucch, uci); - UL_INFO->uci_ind.num_ucis += 1; - pucch->active = false; + handle_pucch(gNB, rxdataF, &pucch[i], uci++); } UL_INFO->crc_ind.sfn = frame_rx; @@ -1377,7 +1388,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N stop_meas(&gNB->phy_proc_rx); - if (pucch_decode_done || pusch_decode_done) { + if (n_pucch > 0 || pusch_decode_done) { T(T_GNB_PHY_PUCCH_PUSCH_IQ, T_INT(frame_rx), T_INT(slot_rx), From 1aed7b5802a1e5d217ea22e60ec5db0274083129 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 11:59:00 +0100 Subject: [PATCH 49/72] Cleanly pass pusch_vars to nr_pusch_channel_estimation() Pass pointer to data. This necessitates to move delay from NR_gNB_ULSCH_t (ULSCH data, including transport channels etc) to NR_gNB_PUSCH (lower-PHY PUSCH data): 1. This allows to pass a single pointer to nr_pusch_channel_estimation() 2. To me, delay estimation seems to be a lower-PHY calculation, so it fits better anyway. Signed-off-by: Robert Schmidt --- openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c | 6 +++--- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 4 ++-- openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 2 +- openair1/PHY/defs_gNB.h | 3 ++- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 2 +- openair1/SIMULATION/NR_PHY/ulsim.c | 8 ++++---- 6 files changed, 13 insertions(+), 12 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 4d70db6f02..68196e78c7 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -452,7 +452,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, int nl, unsigned short p, unsigned char symbol, - int ul_id, + NR_gNB_PUSCH *pusch_vars, int beam_nb, unsigned short bwp_start_subcarrier, nfapi_nr_pusch_pdu_t *pusch_pdu, @@ -534,7 +534,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, int nest_count = 0; uint64_t noise_amp2 = 0; - delay_t *delay = &gNB->ulsch[ul_id].delay; + delay_t *delay = &pusch_vars->delay; memset(delay, 0, sizeof(*delay)); int nb_antennas_rx = fp->nb_antennas_rx; @@ -579,7 +579,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, rdata->delay = &delay_arr[rdata->aarx]; rdata->beam_nb = beam_nb; rdata->frame_parms = fp; - rdata->pusch_vars = &gNB->pusch_vars[ul_id]; + rdata->pusch_vars = pusch_vars; rdata->chest_freq = gNB->chest_freq; rdata->rxdataF = gNB->common_vars.rxdataF; rdata->scope = gNB->scopeData; diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index 540a04ec52..57bac0dccc 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -17,7 +17,7 @@ \param Ns slot number (0..19) \param p \param symbol symbol within slot -\param ul_id +\param pusch_vars lower-phy PUSCH info \param bwp_start_subcarrier first allocated subcarrier \param pusch_pdu \param max_ch maximum value of estimated channel @@ -29,7 +29,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, int nl, unsigned short p, unsigned char symbol, - int ul_id, + NR_gNB_PUSCH *pusch_vars, int beam_nb, unsigned short bwp_start_subcarrier, nfapi_nr_pusch_pdu_t *pusch_pdu, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 82d6b0d99d..1984c4b03c 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -1159,7 +1159,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t nl, get_dmrs_port(nl, rel15_ul->dmrs_ports), symbol, - ulsch_id, + pusch_vars, beam_nb, bwp_start_subcarrier, rel15_ul, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 9657cef20d..bd611bbe8f 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -187,7 +187,6 @@ typedef struct { bool active; /// Flag to indicate that the UL configuration has been handled. Used to remove a stale ULSCH when frame wraps around uint8_t handled; - delay_t delay; } NR_gNB_ULSCH_t; typedef struct { @@ -275,6 +274,8 @@ typedef struct { int llr_offset[14]; /// flag to indicate DTX on reception int DTX; + /// delay estimation + delay_t delay; } NR_gNB_PUSCH; /// Context data structure for RX/TX portion of slot processing diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 1f28622231..7f6ae103c5 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -542,7 +542,7 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, nfapi_nr_pusch_pdu_t *pusch_pdu = &harq_process->ulsch_pdu; // Get estimated timing advance for MAC - const int sync_pos = gNB->ulsch[ULSCH_id].delay.est_delay; + const int sync_pos = pusch->delay.est_delay; // scale the 16 factor in N_TA calculation in 38.213 section 4.2 according to the used FFT size uint16_t bw_scaling = 16 * gNB->frame_parms.ofdm_symbol_size / 2048; diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 0d432bfdd0..011b7ee680 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1208,6 +1208,7 @@ int main(int argc, char *argv[]) uint8_t round = 0; crc_status = 1; errors_decoding = 0; + NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[UE_id]; while (round < max_rounds && crc_status && !stop) { @@ -1594,7 +1595,6 @@ int main(int argc, char *argv[]) } } - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[UE_id]; if (n_trials == 1 && round == 0) { __attribute__((unused)) int off = ((nb_rb & 1) == 1) ? 4 : 0; @@ -1742,9 +1742,9 @@ int main(int argc, char *argv[]) if (!crc_status) effRate += ((double)TBS) / (double)round; - sum_pusch_delay += ulsch_gNB->delay.est_delay; - min_pusch_delay = min(ulsch_gNB->delay.est_delay, min_pusch_delay); - max_pusch_delay = max(ulsch_gNB->delay.est_delay, max_pusch_delay); + sum_pusch_delay += pusch_vars->delay.est_delay; + min_pusch_delay = min(pusch_vars->delay.est_delay, min_pusch_delay); + max_pusch_delay = max(pusch_vars->delay.est_delay, max_pusch_delay); delay_pusch_est_count++; if (do_SRS == 1) { From 0161568d69ff7394f3b6e46b750285ef468b2beb Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 12:21:05 +0100 Subject: [PATCH 50/72] Apply const correctness and avoid pointers Amidst all these unnecessary pointers, the pointer on pusch_vars->ptrs_symbols is necessary to reset the PTRS number of symbols. For clarity, remove this pointer and use pusch_vars->ptrs_symbols directly. Signed-off-by: Robert Schmidt --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 54 +++++++++---------- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 4 +- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 12 ++--- 3 files changed, 32 insertions(+), 38 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 68196e78c7..0119023882 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -34,7 +34,7 @@ typedef struct puschAntennaProc_s { int aarx; int beam_nb; int numAntennas; - nfapi_nr_pusch_pdu_t *pusch_pdu; + const nfapi_nr_pusch_pdu_t *pusch_pdu; int *max_ch; c16_t *pilot; int *nest_count; @@ -82,7 +82,7 @@ static void nr_pusch_antenna_processing(void *arg) int aarx = rdata->aarx; int numAntennas = rdata->numAntennas; unsigned short bwp_start_subcarrier = rdata->bwp_start_subcarrier; - nfapi_nr_pusch_pdu_t *pusch_pdu = rdata->pusch_pdu; + const nfapi_nr_pusch_pdu_t *pusch_pdu = rdata->pusch_pdu; int *max_ch = rdata->max_ch; c16_t *pilot = rdata->pilot; uint64_t noise_amp2 = *(rdata->noise_amp2); @@ -455,7 +455,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_vars, int beam_nb, unsigned short bwp_start_subcarrier, - nfapi_nr_pusch_pdu_t *pusch_pdu, + const nfapi_nr_pusch_pdu_t *pusch_pdu, int *max_ch, uint32_t *nvar, c16_t *pusch_dmrs_slot_mem, @@ -646,7 +646,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, // #define DEBUG_UL_PTRS void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, - nfapi_nr_pusch_pdu_t *rel15_ul, + const nfapi_nr_pusch_pdu_t *rel15_ul, uint8_t ulsch_id, uint8_t nr_tti_rx, unsigned char symbol, @@ -656,15 +656,13 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, int32_t *ptrs_re_symbol = NULL; int8_t ret = 0; uint8_t symbInSlot = rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols; - uint8_t *startSymbIndex = &rel15_ul->start_symbol_index; - uint8_t *nbSymb = &rel15_ul->nr_of_symbols; - uint8_t *L_ptrs = &rel15_ul->pusch_ptrs.ptrs_time_density; - uint8_t *K_ptrs = &rel15_ul->pusch_ptrs.ptrs_freq_density; - uint16_t *dmrsSymbPos = &rel15_ul->ul_dmrs_symb_pos; - uint16_t *ptrsSymbPos = &pusch_vars->ptrs_symbols; - uint8_t *ptrsSymbIdx = &pusch_vars->ptrs_symbol_index; - uint16_t *nb_rb = &rel15_ul->rb_size; - uint8_t *ptrsReOffset = &rel15_ul->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset; + uint8_t startSymbIndex = rel15_ul->start_symbol_index; + uint8_t nbSymb = rel15_ul->nr_of_symbols; + uint8_t L_ptrs = rel15_ul->pusch_ptrs.ptrs_time_density; + uint8_t K_ptrs = rel15_ul->pusch_ptrs.ptrs_freq_density; + uint16_t dmrsSymbPos = rel15_ul->ul_dmrs_symb_pos; + uint16_t nb_rb = rel15_ul->rb_size; + uint8_t ptrsReOffset = rel15_ul->pusch_ptrs.ptrs_ports_list[0].ptrs_re_offset; /* loop over antennas */ for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { @@ -673,7 +671,7 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, *ptrs_re_symbol = 0; phase_per_symbol[symbol].i = 0; /* set DMRS estimates to 0 angle with magnitude 1 */ - if (is_dmrs_symbol(symbol, *dmrsSymbPos)) { + if (is_dmrs_symbol(symbol, dmrsSymbPos)) { /* set DMRS real estimation to 32767 */ phase_per_symbol[symbol].r = INT16_MAX; // 32767 #ifdef DEBUG_UL_PTRS @@ -683,17 +681,13 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, phase_per_symbol[symbol].r = 0; } - if (symbol == *startSymbIndex) { - *ptrsSymbPos = 0; - set_ptrs_symb_idx(ptrsSymbPos, *nbSymb, *startSymbIndex, 1 << *L_ptrs, *dmrsSymbPos); + if (symbol == startSymbIndex) { + pusch_vars->ptrs_symbols = 0; + set_ptrs_symb_idx(&pusch_vars->ptrs_symbols, nbSymb, startSymbIndex, 1 << L_ptrs, dmrsSymbPos); } - /* if not PTRS symbol set current ptrs symbol index to zero*/ - *ptrsSymbIdx = 0; - /* Check if current symbol contains PTRS */ - if (is_ptrs_symbol(symbol, *ptrsSymbPos)) { - *ptrsSymbIdx = symbol; + if (is_ptrs_symbol(symbol, pusch_vars->ptrs_symbols)) { /*------------------------------------------------------------------------------------------------------- */ /* 1) Estimate common phase error per PTRS symbol */ /*------------------------------------------------------------------------------------------------------- */ @@ -703,9 +697,9 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, rel15_ul->scid, nr_tti_rx, symbol); - nr_ptrs_cpe_estimation(*K_ptrs, - *ptrsReOffset, - *nb_rb, + nr_ptrs_cpe_estimation(K_ptrs, + ptrsReOffset, + nb_rb, rel15_ul->rnti, frame_parms->ofdm_symbol_size, &pusch_vars->rxdataF_comp[aarx][(symbol * nb_re_pusch)], @@ -720,8 +714,8 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, /* 2) Interpolate PTRS estimated value in TD */ /*------------------------------------------------------------------------------------------------------- */ /* If L-PTRS is > 0 then we need interpolation */ - if (*L_ptrs > 0) { - ret = nr_ptrs_process_slot(*dmrsSymbPos, *ptrsSymbPos, (int16_t *)phase_per_symbol, *startSymbIndex, *nbSymb); + if (L_ptrs > 0) { + ret = nr_ptrs_process_slot(dmrsSymbPos, pusch_vars->ptrs_symbols, (int16_t *)phase_per_symbol, startSymbIndex, nbSymb); if (ret != 0) { LOG_W(PHY, "[PTRS] Compensation is skipped due to error in PTRS slot processing !!\n"); } @@ -730,17 +724,17 @@ void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, /*------------------------------------------------------------------------------------------------------- */ /* 3) Compensated DMRS based estimated signal with PTRS estimation */ /*--------------------------------------------------------------------------------------------------------*/ - for (uint8_t i = *startSymbIndex; i < symbInSlot; i++) { + for (uint8_t i = startSymbIndex; i < symbInSlot; i++) { /* DMRS Symbol has 0 phase so no need to rotate the respective symbol */ /* Skip rotation if the slot processing is wrong */ - if ((!is_dmrs_symbol(i, *dmrsSymbPos)) && (ret == 0)) { + if ((!is_dmrs_symbol(i, dmrsSymbPos)) && (ret == 0)) { #ifdef DEBUG_UL_PTRS printf("[PHY][UL][PTRS]: Rotate Symbol %2d with %d + j* %d\n", i, phase_per_symbol[i].r, phase_per_symbol[i].i); #endif rotate_cpx_vector(&pusch_vars->rxdataF_comp[aarx][i * nb_re_pusch], &phase_per_symbol[i], &pusch_vars->rxdataF_comp[aarx][i * nb_re_pusch], - ((*nb_rb) * NR_NB_SC_PER_RB), + (nb_rb * NR_NB_SC_PER_RB), 15); } // if not DMRS Symbol } // symbol loop diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index 57bac0dccc..b5ec042419 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -32,7 +32,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_vars, int beam_nb, unsigned short bwp_start_subcarrier, - nfapi_nr_pusch_pdu_t *pusch_pdu, + const nfapi_nr_pusch_pdu_t *pusch_pdu, int *max_ch, uint32_t *nvar, c16_t *pusch_dmrs_slot_mem, @@ -52,7 +52,7 @@ void nr_est_srs_timing_advance_offset(uint16_t ofdm_symbol_size, void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, - nfapi_nr_pusch_pdu_t *rel15_ul, + const nfapi_nr_pusch_pdu_t *rel15_ul, uint8_t ulsch_id, uint8_t nr_tti_rx, unsigned char symbol, diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 1984c4b03c..d9a79ac84d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -80,7 +80,7 @@ static void nr_ulsch_extract_rbs(c16_t* const rxdataF, int rxoffset, int choffset, int is_dmrs_symbol, - nfapi_nr_pusch_pdu_t *pusch_pdu, + const nfapi_nr_pusch_pdu_t *pusch_pdu, NR_DL_FRAME_PARMS *frame_parms) { uint8_t delta = 0; @@ -160,7 +160,7 @@ static void nr_ulsch_extract_rbs(c16_t* const rxdataF, } } -static int get_nb_re_pusch (NR_DL_FRAME_PARMS *frame_parms, nfapi_nr_pusch_pdu_t *rel15_ul,int symbol) +static int get_nb_re_pusch (NR_DL_FRAME_PARMS *frame_parms, const nfapi_nr_pusch_pdu_t *rel15_ul, int symbol) { uint8_t dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; if (dmrs_symbol_flag == 1) { @@ -186,7 +186,7 @@ static void nr_ulsch_channel_compensation(uint32_t buffer_length, c16_t **rxComp, int nb_layers, c16_t rho[][nb_layers][buffer_length], - nfapi_nr_pusch_pdu_t *rel15_ul, + const nfapi_nr_pusch_pdu_t *rel15_ul, uint32_t symbol, uint32_t output_shift) { @@ -861,7 +861,7 @@ static void inner_rx(PHY_VARS_gNB *gNB, int slot, NR_DL_FRAME_PARMS *frame_parms, NR_gNB_PUSCH *pusch_vars, - nfapi_nr_pusch_pdu_t *rel15_ul, + const nfapi_nr_pusch_pdu_t *rel15_ul, c16_t **rxF, int16_t **llr, int soffset, @@ -1003,7 +1003,7 @@ static void inner_rx(PHY_VARS_gNB *gNB, typedef struct puschSymbolProc_s { PHY_VARS_gNB *gNB; NR_DL_FRAME_PARMS *frame_parms; - nfapi_nr_pusch_pdu_t *rel15_ul; + const nfapi_nr_pusch_pdu_t *rel15_ul; int ulsch_id; int slot; int startSymbol; @@ -1023,7 +1023,7 @@ static void nr_pusch_symbol_processing(void *arg) PHY_VARS_gNB *gNB = rdata->gNB; NR_DL_FRAME_PARMS *frame_parms = rdata->frame_parms; - nfapi_nr_pusch_pdu_t *rel15_ul = rdata->rel15_ul; + const nfapi_nr_pusch_pdu_t *rel15_ul = rdata->rel15_ul; int ulsch_id = rdata->ulsch_id; int slot = rdata->slot; NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ulsch_id]; From c99e5642b53fd02f5763e4d98b3fb3e8c3dc8d21 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 12:23:15 +0100 Subject: [PATCH 51/72] Pass pointer directly Signed-off-by: Robert Schmidt --- .../NR_ESTIMATION/nr_ul_channel_estimation.c | 5 ++-- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 2 +- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 12 ++++++-- .../PHY/NR_TRANSPORT/nr_ulsch_demodulation.c | 29 ++++++++++--------- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 3 +- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c index 0119023882..a5190997fa 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_channel_estimation.c @@ -628,7 +628,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, * * PARAMETERS : gNB : gNB data structure * rel15_ul : UL parameters - * UE_id : UE ID + * pusch_vars : PUSCH data * nr_tti_rx : slot rx TTI * dmrs_symbol_flag: DMRS Symbol Flag * symbol : OFDM Symbol @@ -647,12 +647,11 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, const nfapi_nr_pusch_pdu_t *rel15_ul, - uint8_t ulsch_id, + NR_gNB_PUSCH *pusch_vars, uint8_t nr_tti_rx, unsigned char symbol, uint32_t nb_re_pusch) { - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ulsch_id]; int32_t *ptrs_re_symbol = NULL; int8_t ret = 0; uint8_t symbInSlot = rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols; diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index b5ec042419..25b9c27414 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -53,7 +53,7 @@ void nr_est_srs_timing_advance_offset(uint16_t ofdm_symbol_size, void nr_pusch_ptrs_processing(PHY_VARS_gNB *gNB, NR_DL_FRAME_PARMS *frame_parms, const nfapi_nr_pusch_pdu_t *rel15_ul, - uint8_t ulsch_id, + NR_gNB_PUSCH *pusch_vars, uint8_t nr_tti_rx, unsigned char symbol, uint32_t nb_re_pusch); diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index eaf7fcbfb5..43ecb0d006 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -85,11 +85,19 @@ void free_gNB_dlsch(NR_gNB_DLSCH_t *dlsch, uint16_t N_RB, const NR_DL_FRAME_PARM - LLR computation This function supports TM1, 2, 3, 5, and 6. @param ue Pointer to PHY variables - @param UE_id id of current UE + @param pusch_vars ULSCH PUSCH data + @param rel15_ul FAPI message to process + @param unav_res unav_res result @param frame Frame number @param slot Slot number */ -int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t slot, int beam_nb); +int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, + NR_gNB_PUSCH *pusch_vars, + const nfapi_nr_pusch_pdu_t *rel15_ul, + uint32_t *ret_unav_res, + uint32_t frame, + uint8_t slot, + int beam_nb); /*! \brief This function implements the idft transform precoding in PUSCH diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index d9a79ac84d..684e65699e 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -857,7 +857,6 @@ static uint8_t nr_ulsch_mmse_2layers(c16_t **rxdataF_comp, } static void inner_rx(PHY_VARS_gNB *gNB, - int ulsch_id, int slot, NR_DL_FRAME_PARMS *frame_parms, NR_gNB_PUSCH *pusch_vars, @@ -952,7 +951,7 @@ static void inner_rx(PHY_VARS_gNB *gNB, nr_pusch_ptrs_processing(gNB, frame_parms, rel15_ul, - ulsch_id, + pusch_vars, slot, symbol, buffer_length); @@ -1004,7 +1003,7 @@ typedef struct puschSymbolProc_s { PHY_VARS_gNB *gNB; NR_DL_FRAME_PARMS *frame_parms; const nfapi_nr_pusch_pdu_t *rel15_ul; - int ulsch_id; + NR_gNB_PUSCH *pusch_vars; int slot; int startSymbol; int numSymbols; @@ -1024,11 +1023,10 @@ static void nr_pusch_symbol_processing(void *arg) PHY_VARS_gNB *gNB = rdata->gNB; NR_DL_FRAME_PARMS *frame_parms = rdata->frame_parms; const nfapi_nr_pusch_pdu_t *rel15_ul = rdata->rel15_ul; - int ulsch_id = rdata->ulsch_id; int slot = rdata->slot; - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ulsch_id]; + NR_gNB_PUSCH *pusch_vars = rdata->pusch_vars; for (int symbol = rdata->startSymbol; symbol < rdata->startSymbol + rdata->numSymbols; symbol++) { - if (gNB->pusch_vars[ulsch_id].ul_valid_re_per_slot[symbol] == 0) + if (pusch_vars->ul_valid_re_per_slot[symbol] == 0) continue; int soffset = (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size; int buffer_length = ceil_mod(pusch_vars->ul_valid_re_per_slot[symbol] * NR_NB_SC_PER_RB, 16); @@ -1038,7 +1036,6 @@ static void nr_pusch_symbol_processing(void *arg) llrss[l] = llrs[l]; inner_rx(gNB, - ulsch_id, slot, frame_parms, pusch_vars, @@ -1047,12 +1044,12 @@ static void nr_pusch_symbol_processing(void *arg) llrss, soffset, symbol, - gNB->pusch_vars[ulsch_id].log2_maxh, + pusch_vars->log2_maxh, rdata->nvar, rdata->rxFext_slot_mem, rdata->pusch_ch_est_dmrs_interpl_slot_mem); - int nb_re_pusch = gNB->pusch_vars[ulsch_id].ul_valid_re_per_slot[symbol]; + int nb_re_pusch = pusch_vars->ul_valid_re_per_slot[symbol]; // layer de-mapping int16_t *llr_ptr = llrs[0]; if (rel15_ul->nrOfLayers != 1) { @@ -1098,12 +1095,16 @@ static uint32_t average_u32(const uint32_t *x, uint16_t size) return (uint32_t)(sum_x / size); } -int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t slot, int beam_nb) +int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, + NR_gNB_PUSCH *pusch_vars, + const nfapi_nr_pusch_pdu_t *rel15_ul, + uint32_t *ret_unav_res, + uint32_t frame, + uint8_t slot, + int beam_nb) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - nfapi_nr_pusch_pdu_t *rel15_ul = &gNB->ulsch[ulsch_id].harq_process->ulsch_pdu; - NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ulsch_id]; uint32_t bwp_start_subcarrier = ((rel15_ul->rb_start + rel15_ul->bwp_start) * NR_NB_SC_PER_RB + frame_parms->first_carrier_offset) % frame_parms->ofdm_symbol_size; LOG_D(PHY,"pusch %d.%d : bwp_start_subcarrier %d, rb_start %d, first_carrier_offset %d\n", frame,slot,bwp_start_subcarrier, rel15_ul->rb_start, frame_parms->first_carrier_offset); LOG_D(PHY,"pusch %d.%d : ul_dmrs_symb_pos %x\n",frame,slot,rel15_ul->ul_dmrs_symb_pos); @@ -1261,7 +1262,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t unav_res, rel15_ul->qam_mod_order, rel15_ul->nrOfLayers); - gNB->ulsch[ulsch_id].unav_res = unav_res; + *ret_unav_res = unav_res; // initialize scrambling sequence // int16_t scramblingSequence[G + 96] __attribute__((aligned(32))); @@ -1378,7 +1379,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, uint8_t ulsch_id, uint32_t frame, uint8_t rdata->startSymbol = symbol; // Last task processes remainder symbols rdata->numSymbols = task_index == loop_iter - 1 ? rel15_ul->nr_of_symbols - (loop_iter - 1) * numSymbols : numSymbols; - rdata->ulsch_id = ulsch_id; + rdata->pusch_vars = pusch_vars; rdata->llr = pusch_vars->llr; rdata->scramblingSequence = scramblingSequence; rdata->nvar = nvar; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 7f6ae103c5..a82088e7ac 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -1086,6 +1086,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !ulsch->handled)) continue; LOG_D(PHY, "PUSCH ID %d with RNTI %x detection started in frame %d slot %d\n", ULSCH_id, ulsch->rnti, frame_rx, slot_rx); + const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu; #ifdef DEBUG_RXDATA RU_t *ru = gNB->RU_list[0]; @@ -1119,8 +1120,8 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N pusch_decode_done = 1; start_meas(&gNB->rx_pusch_stats); - nr_rx_pusch_tp(gNB, ULSCH_id, frame_rx, slot_rx, ulsch->beam_nb); NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; + nr_rx_pusch_tp(gNB, pusch_vars, pdu, &ulsch->unav_res, frame_rx, slot_rx, ulsch->beam_nb); pusch_vars->ulsch_power_tot = 0; pusch_vars->ulsch_noise_power_tot = 0; for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { From a2f8f2ead34c13175f6922d9b27300b4635a8c69 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 13:25:20 +0100 Subject: [PATCH 52/72] Remove first useless iteration It is pointless to iterate ulsch_to_decode to find the number of jobs if we can simply pass it to the function. Use an additional {} block to limit the amount of indentation. Signed-off-by: Robert Schmidt --- openair1/PHY/NR_TRANSPORT/nr_ulsch.h | 2 +- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 2 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 50 +++++++------------ openair1/SIMULATION/NR_PHY/ulschsim.c | 2 +- 4 files changed, 22 insertions(+), 34 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h index b1e932f60d..514c8b60e5 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h @@ -33,7 +33,7 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, uint32_t frame, uint8_t nr_tti_rx, uint32_t *G, - uint8_t *ULSCH_ids, + int *ULSCH_ids, int nb_pusch); /*! \brief Perform PUSCH unscrambling. TS 38.211 V15.4.0 subclause 6.3.1.1 diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 29a53ec5a0..e04f688a54 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -94,7 +94,7 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, uint32_t frame, uint8_t nr_tti_rx, uint32_t *G, - uint8_t *ULSCH_ids, + int *ULSCH_ids, int nb_pusch) { nrLDPC_TB_decoding_parameters_t TBs[nb_pusch]; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index a82088e7ac..e52171de82 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -323,31 +323,18 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, stop_meas(&gNB->phase_comp_stats); } -static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, bool *ulsch_to_decode, NR_UL_IND_t *UL_INFO) +static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int *ulsch_to_decode, int nb_pusch, NR_UL_IND_t *UL_INFO) { + DevAssert(nb_pusch > 0); NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - int nb_pusch = 0; - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { - if (ulsch_to_decode[ULSCH_id]) { - nb_pusch++; - } - } - - if (nb_pusch == 0) { - return 0; - } - - uint8_t ULSCH_ids[nb_pusch]; uint32_t G[nb_pusch]; - int pusch_id = 0; - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { + for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { - if (ulsch_to_decode[ULSCH_id]) { - - ULSCH_ids[pusch_id] = ULSCH_id; - - nfapi_nr_pusch_pdu_t *pusch_pdu = &gNB->ulsch[ULSCH_id].harq_process->ulsch_pdu; + int ULSCH_id = ulsch_to_decode[pusch_id]; + DevAssert(ULSCH_id >= 0); + { + const nfapi_nr_pusch_pdu_t *pusch_pdu = &gNB->ulsch[ULSCH_id].harq_process->ulsch_pdu; uint16_t nb_re_dmrs; uint16_t start_symbol = pusch_pdu->start_symbol_index; @@ -384,7 +371,6 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs pusch_pdu->qam_mod_order, pusch_pdu->nrOfLayers); - pusch_id++; } } @@ -392,15 +378,15 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, boo //--------------------- ULSCH decoding --------------------- //---------------------------------------------------------- - int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, G, ULSCH_ids, nb_pusch); + int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, G, ulsch_to_decode, nb_pusch); // CRC check per uplink shared channel - for (pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { - uint8_t ULSCH_id = ULSCH_ids[pusch_id]; + for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { + uint8_t ULSCH_id = ulsch_to_decode[pusch_id]; NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_gNB_PUSCH *pusch = &gNB->pusch_vars[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; - nfapi_nr_pusch_pdu_t *pusch_pdu = &ulsch_harq->ulsch_pdu; + const nfapi_nr_pusch_pdu_t *pusch_pdu = &ulsch_harq->ulsch_pdu; bool crc_valid = false; @@ -1077,9 +1063,10 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N UL_INFO->rx_ind.sfn = frame_rx; UL_INFO->rx_ind.slot = slot_rx; UL_INFO->rx_ind.pdu_list = UL_INFO->rx_pdu_list; - bool ulsch_to_decode[gNB->max_nb_pusch]; - bzero(ulsch_to_decode, sizeof(ulsch_to_decode)); + int num_pusch = 0; + int ulsch_idx_to_decode[gNB->max_nb_pusch]; for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { + ulsch_idx_to_decode[ULSCH_id] = -1; NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); @@ -1163,7 +1150,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N pusch_vars->DTX = 0; } - ulsch_to_decode[ULSCH_id] = true; + ulsch_idx_to_decode[num_pusch++] = ULSCH_id; stop_meas(&gNB->rx_pusch_stats); // LOG_M("rxdataF_comp.m","rxF_comp",gNB->pusch_vars[0]->rxdataF_comp[0],6900,1,1); // LOG_M("rxdataF_ext.m","rxF_ext",gNB->pusch_vars[0]->rxdataF_ext[0],6900,1,1); @@ -1176,9 +1163,10 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N if (gNB->max_nb_pusch == 1) start_meas(&gNB->ulsch_decoding_stats); - int const ret_nr_ulsch_procedures = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ulsch_to_decode, UL_INFO); - if (ret_nr_ulsch_procedures != 0) { - LOG_E(PHY,"Error in nr_ulsch_procedures, returned %d\n",ret_nr_ulsch_procedures); + if (num_pusch > 0) { + int ret_nr_ulsch_procedures = nr_ulsch_procedures(gNB, frame_rx, slot_rx, ulsch_idx_to_decode, num_pusch, UL_INFO); + if (ret_nr_ulsch_procedures != 0) + LOG_E(PHY,"Error in nr_ulsch_procedures, returned %d\n",ret_nr_ulsch_procedures); } /* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1 diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index f07ad92ca9..64559757b1 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -419,7 +419,7 @@ int main(int argc, char **argv) uint16_t N_RE_prime,code_rate; unsigned char mod_order; uint8_t rvidx = 0; - uint8_t UE_id = 0; + int UE_id = 0; NR_gNB_ULSCH_t *ulsch_gNB = &gNB->ulsch[UE_id]; NR_UL_gNB_HARQ_t *harq_process_gNB = ulsch_gNB->harq_process; From c0a2dc9006492f09f53c0d49ccc4c1a7f58f8ce8 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 15:25:59 +0100 Subject: [PATCH 53/72] Remove second useless loop iteration in nr_ulsch_procedures() Set the FAPI message in nr_ulschsim correctly in line with what nr_ulsch_procedures() expected. Add an AssertFatal() to verify that the assumed length of DMRS in the simulator and what is actually set in the FAPI message matches. Finally, the internal nr_get_G() code (looping over all PUSCH) does not double the DMRS because Nl == 3 || Nl == 4, so harmonize in the simulator. Signed-off-by: Robert Schmidt --- openair1/PHY/NR_TRANSPORT/nr_ulsch.h | 2 - openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 19 ++++++-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 48 +------------------ openair1/SIMULATION/NR_PHY/ulschsim.c | 12 +++-- 4 files changed, 23 insertions(+), 58 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h index 514c8b60e5..816ce9a16a 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.h +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.h @@ -23,7 +23,6 @@ NR_gNB_ULSCH_t new_gNB_ulsch(uint8_t max_ldpc_iterations, uint16_t N_RB_UL); @param frame_parms, Pointer to frame descriptor structure @param frame, current received frame @param nr_tti_rx, current received TTI - @param G @param ULSCH_ids, array of ULSCH ids @param nb_pusch, number of uplink shared channels */ @@ -32,7 +31,6 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, NR_DL_FRAME_PARMS *frame_parms, uint32_t frame, uint8_t nr_tti_rx, - uint32_t *G, int *ULSCH_ids, int nb_pusch); diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index e04f688a54..e3b44e99ec 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -93,7 +93,6 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, NR_DL_FRAME_PARMS *frame_parms, uint32_t frame, uint8_t nr_tti_rx, - uint32_t *G, int *ULSCH_ids, int nb_pusch) { @@ -112,17 +111,29 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, NR_gNB_ULSCH_t *ulsch = &phy_vars_gNB->ulsch[ULSCH_id]; NR_gNB_PUSCH *pusch = &phy_vars_gNB->pusch_vars[ULSCH_id]; NR_UL_gNB_HARQ_t *harq_process = ulsch->harq_process; - nfapi_nr_pusch_pdu_t *pusch_pdu = &harq_process->ulsch_pdu; + const nfapi_nr_pusch_pdu_t *pusch_pdu = &harq_process->ulsch_pdu; nrLDPC_TB_decoding_parameters_t *TB_parameters = &TBs[pusch_id]; - TB_parameters->G = G[pusch_id]; - if (!harq_process) { LOG_E(PHY, "ulsch_decoding.c: NULL harq_process pointer\n"); return -1; } + uint8_t number_dmrs_symbols = count_bits64_with_mask(pusch_pdu->ul_dmrs_symb_pos, pusch_pdu->start_symbol_index, pusch_pdu->nr_of_symbols); + int factor = pusch_pdu->dmrs_config_type == pusch_dmrs_type1 ? 6 : 4; + int nb_re_dmrs = factor * pusch_pdu->num_dmrs_cdm_grps_no_data; + uint32_t G = nr_get_G(pusch_pdu->rb_size, + pusch_pdu->nr_of_symbols, + nb_re_dmrs, + number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs + ulsch->unav_res, + pusch_pdu->qam_mod_order, + pusch_pdu->nrOfLayers); + DevAssert(G > 0); + TB_parameters->G = G; + + // The harq_pid is not unique among the active HARQ processes in the instance so we use ULSCH_id instead TB_parameters->harq_unique_pid = ULSCH_id; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index e52171de82..70b9c15a44 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -327,58 +327,12 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int { DevAssert(nb_pusch > 0); NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - - uint32_t G[nb_pusch]; - for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { - - int ULSCH_id = ulsch_to_decode[pusch_id]; - DevAssert(ULSCH_id >= 0); - { - const nfapi_nr_pusch_pdu_t *pusch_pdu = &gNB->ulsch[ULSCH_id].harq_process->ulsch_pdu; - - uint16_t nb_re_dmrs; - uint16_t start_symbol = pusch_pdu->start_symbol_index; - uint16_t number_symbols = pusch_pdu->nr_of_symbols; - - uint8_t number_dmrs_symbols = 0; - for (int l = start_symbol; l < start_symbol + number_symbols; l++) - number_dmrs_symbols += ((pusch_pdu->ul_dmrs_symb_pos)>>l)&0x01; - - if (pusch_pdu->dmrs_config_type==pusch_dmrs_type1) - nb_re_dmrs = 6*pusch_pdu->num_dmrs_cdm_grps_no_data; - else - nb_re_dmrs = 4*pusch_pdu->num_dmrs_cdm_grps_no_data; - - G[pusch_id] = nr_get_G(pusch_pdu->rb_size, - number_symbols, - nb_re_dmrs, - number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs - gNB->ulsch[ULSCH_id].unav_res, - pusch_pdu->qam_mod_order, - pusch_pdu->nrOfLayers); - AssertFatal(G[pusch_id]>0,"G is 0 : rb_size %u, number_symbols %d, nb_re_dmrs %d, number_dmrs_symbols %d, qam_mod_order %u, nrOfLayer %u\n", - pusch_pdu->rb_size, - number_symbols, - nb_re_dmrs, - number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs - pusch_pdu->qam_mod_order, - pusch_pdu->nrOfLayers); - LOG_D(PHY,"rb_size %d, number_symbols %d, nb_re_dmrs %d, dmrs symbol positions %d, number_dmrs_symbols %d, qam_mod_order %d, nrOfLayer %d\n", - pusch_pdu->rb_size, - number_symbols, - nb_re_dmrs, - pusch_pdu->ul_dmrs_symb_pos, - number_dmrs_symbols, // number of dmrs symbols irrespective of single or double symbol dmrs - pusch_pdu->qam_mod_order, - pusch_pdu->nrOfLayers); - } - } //---------------------------------------------------------- //--------------------- ULSCH decoding --------------------- //---------------------------------------------------------- - int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, G, ulsch_to_decode, nb_pusch); + int ret_nr_ulsch_decoding = nr_ulsch_decoding(gNB, frame_parms, frame_rx, slot_rx, ulsch_to_decode, nb_pusch); // CRC check per uplink shared channel for (int pusch_id = 0; pusch_id < nb_pusch; pusch_id++) { diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 64559757b1..7d193f5119 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -414,7 +414,7 @@ int main(int argc, char **argv) unsigned int TBS = 8424; unsigned int available_bits; uint8_t nb_re_dmrs = 6; - uint8_t length_dmrs = 1; + uint8_t length_dmrs = 2; uint8_t N_PRB_oh; uint16_t N_RE_prime,code_rate; unsigned char mod_order; @@ -429,9 +429,6 @@ int main(int argc, char **argv) nr_phy_data_tx_t phy_data = {0}; NR_UE_ULSCH_t *ulsch_ue = &phy_data.ulsch; - if ((Nl==4)||(Nl==3)) - nb_re_dmrs = nb_re_dmrs*2; - mod_order = nr_get_Qm_ul(Imcs, mcs_table); code_rate = nr_get_code_rate_ul(Imcs, mcs_table); available_bits = nr_get_G(nb_rb, nb_symb_sch, nb_re_dmrs, length_dmrs, 0, mod_order, Nl); @@ -445,10 +442,15 @@ int main(int argc, char **argv) rel15_ul->qam_mod_order = mod_order; rel15_ul->mcs_index = Imcs; rel15_ul->pusch_data.rv_index = rvidx; + rel15_ul->ul_dmrs_symb_pos = nb_re_dmrs; + rel15_ul->dmrs_config_type = pusch_dmrs_type1; + rel15_ul->num_dmrs_cdm_grps_no_data = 1; rel15_ul->nrOfLayers = Nl; rel15_ul->target_code_rate = code_rate; rel15_ul->pusch_data.tb_size = TBS>>3; rel15_ul->maintenance_parms_v3.ldpcBaseGraph = get_BG(TBS, code_rate); + int bits = count_bits64_with_mask(rel15_ul->ul_dmrs_symb_pos, rel15_ul->start_symbol_index, rel15_ul->nr_of_symbols); + AssertFatal(length_dmrs == bits, "length_dmrs %d bits %d\n", length_dmrs, bits); /////////////////////////////////////////////////// double modulated_input[16 * 68 * 384]; // [hna] 16 segments, 68*Zc @@ -558,7 +560,7 @@ int main(int argc, char **argv) printf("\n"); exit(-1); #endif - nr_ulsch_decoding(gNB, frame_parms, frame, subframe, &G, &UE_id, 1); + nr_ulsch_decoding(gNB, frame_parms, frame, subframe, &UE_id, 1); if (harq_process_gNB->processedSegments == harq_process_gNB->C) { bool crc_valid = check_crc(harq_process_gNB->b, lenWithCrc(1, (harq_process_gNB->TBS) << 3), crcType(1, (harq_process_gNB->TBS) << 3)); if (!crc_valid) { From 337abd5716748ce6a33cfd97950e3414b44ee5c6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 17:16:04 +0100 Subject: [PATCH 54/72] Remove superfluous tbs parameter Signed-off-by: Robert Schmidt --- openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c | 5 ++--- openair1/PHY/defs_gNB.h | 2 -- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 13 +++++++------ openair1/SIMULATION/NR_PHY/ulschsim.c | 2 +- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index e3b44e99ec..9cb6e22c03 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -145,10 +145,9 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, // ------------------------------------------------------------------ TB_parameters->processedSegments = &harq_process->processedSegments; - harq_process->TBS = pusch_pdu->pusch_data.tb_size; TB_parameters->BG = pusch_pdu->maintenance_parms_v3.ldpcBaseGraph; - TB_parameters->A = (harq_process->TBS) << 3; + TB_parameters->A = pusch_pdu->pusch_data.tb_size << 3; NR_gNB_PHY_STATS_t *stats = get_phy_stats(phy_vars_gNB, ulsch->rnti); if (stats) { stats->frame = frame; @@ -160,7 +159,7 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, if (!harq_process->harq_to_be_cleared) { stats->ulsch_stats.current_Qm = TB_parameters->Qm; stats->ulsch_stats.current_RI = TB_parameters->nb_layers; - stats->ulsch_stats.total_bytes_tx += harq_process->TBS; + stats->ulsch_stats.total_bytes_tx += pusch_pdu->pusch_data.tb_size; } } diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index bd611bbe8f..8ab19981dd 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -136,8 +136,6 @@ typedef struct { /// flag used to clear d properly (together with d_to_be_cleared below) /// set to true in nr_fill_ulsch() when new_data_indicator is received bool harq_to_be_cleared; - /// Transport block size (This is A from 38.212 V15.4.0 section 5.1) - uint32_t TBS; /// Pointer to the payload (38.212 V15.4.0 section 5.1) uint8_t *b; /// Pointers to code blocks after code block segmentation and CRC attachment (38.212 V15.4.0 section 5.2.2) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 70b9c15a44..b2ed09a6ec 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -347,7 +347,8 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int // if all segments are done if (ulsch_harq->processedSegments == ulsch_harq->C) { if (ulsch_harq->C > 1) { - crc_valid = check_crc(ulsch_harq->b, lenWithCrc(1, (ulsch_harq->TBS) << 3), crcType(1, (ulsch_harq->TBS) << 3)); + int tbs = pusch_pdu->pusch_data.tb_size; + crc_valid = check_crc(ulsch_harq->b, lenWithCrc(1, tbs << 3), crcType(1, tbs << 3)); } else { // When the number of code blocks is 1 (C = 1) and ulsch_harq->processedSegments = 1, we can assume a good TB because of the // CRC check made by the LDPC for early termination, so, no need to perform CRC check twice for a single code block @@ -412,8 +413,8 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int T_INT((int)dmrs_port), // dmrs_antenna_port T_INT((int)pusch_pdu->scid), // dmrs_nscid T_INT((int)frame_parms->nb_antennas_rx), // rx antenna - T_INT(((ulsch_harq->TBS) << 3)), // number_of_bits - T_BUFFER((uint8_t *)((ulsch_harq->b)), ((ulsch_harq->TBS) << 3) / 8)); // data + T_INT((pusch_pdu->pusch_data.tb_size << 3)), // number_of_bits + T_BUFFER((uint8_t *)((ulsch_harq->b)), pusch_pdu->pusch_data.tb_size)); // data } #endif @@ -432,7 +433,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int pusch_pdu->pusch_data.new_data_indicator, ulsch->active, ulsch_harq->round, - ulsch_harq->TBS, + pusch_pdu->pusch_data.tb_size, ulsch->max_ldpc_iterations); nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 0, 0, crc, pdu); LOG_D(PHY, "ULSCH received ok \n"); @@ -453,7 +454,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ulsch_harq->ulsch_pdu.pusch_data.rv_index, ulsch_harq->ulsch_pdu.rb_start, ulsch_harq->ulsch_pdu.rb_size, - ulsch_harq->TBS); + pusch_pdu->pusch_data.tb_size); nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 1, 0, crc, pdu); gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK"); ulsch->handled = 1; @@ -547,7 +548,7 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, if (crc_flag) pdu->pdu_length = 0; else { - pdu->pdu_length = harq_process->TBS; + pdu->pdu_length = pusch_pdu->pusch_data.tb_size; pdu->pdu = harq_process->b; } } diff --git a/openair1/SIMULATION/NR_PHY/ulschsim.c b/openair1/SIMULATION/NR_PHY/ulschsim.c index 7d193f5119..c5b8723908 100644 --- a/openair1/SIMULATION/NR_PHY/ulschsim.c +++ b/openair1/SIMULATION/NR_PHY/ulschsim.c @@ -562,7 +562,7 @@ int main(int argc, char **argv) #endif nr_ulsch_decoding(gNB, frame_parms, frame, subframe, &UE_id, 1); if (harq_process_gNB->processedSegments == harq_process_gNB->C) { - bool crc_valid = check_crc(harq_process_gNB->b, lenWithCrc(1, (harq_process_gNB->TBS) << 3), crcType(1, (harq_process_gNB->TBS) << 3)); + bool crc_valid = check_crc(harq_process_gNB->b, lenWithCrc(1, TBS << 3), crcType(1, TBS << 3)); if (!crc_valid) { n_false_positive++; } From cbe69c7dfaff6bbccb0aebf051e7a8ed714b3cab Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 17:35:53 +0100 Subject: [PATCH 55/72] Refactor nr_fill_indication() Pass pointers directly instead of ULSCH_id. Put const where possible and applicable. Signed-off-by: Robert Schmidt --- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 64 +++++++---------------- 1 file changed, 20 insertions(+), 44 deletions(-) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index b2ed09a6ec..c9f94ff5b6 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -27,22 +27,14 @@ //#define DEBUG_RXDATA //#define SRS_IND_DEBUG -static void nr_fill_indication(PHY_VARS_gNB *gNB, - int frame, - int slot_rx, - int UE_id, - uint8_t harq_pid, - uint8_t crc_flag, - int dtx_flag, - nfapi_nr_crc_t *crc, - nfapi_nr_rx_data_pdu_t *pdu); -static void nr_fill_indication(PHY_VARS_gNB *gNB, +static void nr_fill_indication(const PHY_VARS_gNB *gNB, int frame, int slot_rx, - int ULSCH_id, - uint8_t harq_pid, - uint8_t crc_flag, + const NR_gNB_PUSCH *pusch, + const nfapi_nr_pusch_pdu_t *pusch_pdu, + NR_gNB_PHY_STATS_t *stats, + uint8_t *payload, int dtx_flag, nfapi_nr_crc_t *crc, nfapi_nr_rx_data_pdu_t *pdu); @@ -340,6 +332,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; NR_gNB_PUSCH *pusch = &gNB->pusch_vars[ULSCH_id]; NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; + NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti); const nfapi_nr_pusch_pdu_t *pusch_pdu = &ulsch_harq->ulsch_pdu; bool crc_valid = false; @@ -435,7 +428,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ulsch_harq->round, pusch_pdu->pusch_data.tb_size, ulsch->max_ldpc_iterations); - nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 0, 0, crc, pdu); + nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch, pusch_pdu, stats, ulsch->harq_process->b, 0, crc, pdu); LOG_D(PHY, "ULSCH received ok \n"); ulsch->active = false; ulsch_harq->round = 0; @@ -455,7 +448,7 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int ulsch_harq->ulsch_pdu.rb_start, ulsch_harq->ulsch_pdu.rb_size, pusch_pdu->pusch_data.tb_size); - nr_fill_indication(gNB, ulsch->frame, ulsch->slot, ULSCH_id, ulsch->harq_pid, 1, 0, crc, pdu); + nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch, pusch_pdu, stats, NULL, 0, crc, pdu); gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK"); ulsch->handled = 1; LOG_D(PHY, "ULSCH %d in error\n",ULSCH_id); @@ -466,22 +459,17 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int return ret_nr_ulsch_decoding; } -static void nr_fill_indication(PHY_VARS_gNB *gNB, +static void nr_fill_indication(const PHY_VARS_gNB *gNB, int frame, int slot_rx, - int ULSCH_id, - uint8_t harq_pid, - uint8_t crc_flag, + const NR_gNB_PUSCH *pusch, + const nfapi_nr_pusch_pdu_t *pusch_pdu, + NR_gNB_PHY_STATS_t *stats, + uint8_t *payload, int dtx_flag, nfapi_nr_crc_t *crc, nfapi_nr_rx_data_pdu_t *pdu) { - NR_UL_gNB_HARQ_t *harq_process = gNB->ulsch[ULSCH_id].harq_process; - NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, gNB->ulsch[ULSCH_id].rnti); - NR_gNB_PUSCH *pusch = &gNB->pusch_vars[ULSCH_id]; - - nfapi_nr_pusch_pdu_t *pusch_pdu = &harq_process->ulsch_pdu; - // Get estimated timing advance for MAC const int sync_pos = pusch->delay.est_delay; @@ -504,14 +492,6 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, timing_advance_update = max(timing_advance_update, 0); timing_advance_update = min(timing_advance_update, 63); - if (crc_flag == 0) - LOG_D(PHY, - "%d.%d : Received PUSCH : Estimated timing advance PUSCH is = %d, timing_advance_update is %d \n", - frame, - slot_rx, - sync_pos, - timing_advance_update); - // estimate UL_CQI for MAC int SNRtimes10 = dB_fixed_x10(pusch->ulsch_power_tot) - dB_fixed_x10(pusch->ulsch_noise_power_tot); @@ -531,8 +511,8 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, crc->handle = pusch_pdu->handle; crc->rnti = pusch_pdu->rnti; - crc->harq_id = harq_pid; - crc->tb_crc_status = crc_flag; + crc->harq_id = pusch_pdu->pusch_data.harq_process_id; + crc->tb_crc_status = payload == NULL; // 222.10.02/04: pass 0, fail 1 crc->num_cb = pusch_pdu->pusch_data.num_cb; crc->ul_cqi = cqi; crc->timing_advance = timing_advance_update; @@ -541,16 +521,12 @@ static void nr_fill_indication(PHY_VARS_gNB *gNB, pdu->handle = pusch_pdu->handle; pdu->rnti = pusch_pdu->rnti; - pdu->harq_id = harq_pid; + pdu->harq_id = pusch_pdu->pusch_data.harq_process_id; pdu->ul_cqi = cqi; pdu->timing_advance = timing_advance_update; pdu->rssi = crc->rssi; - if (crc_flag) - pdu->pdu_length = 0; - else { - pdu->pdu_length = pusch_pdu->pusch_data.tb_size; - pdu->pdu = harq_process->b; - } + pdu->pdu_length = payload != NULL ? pusch_pdu->pusch_data.tb_size : 0; + pdu->pdu = payload; } // Function to fill UL RB mask to be used for N0 measurements @@ -1088,8 +1064,8 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N /* 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 */ nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++]; - nfapi_nr_rx_data_pdu_t *pdu = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++]; - nr_fill_indication(gNB, frame_rx, slot_rx, ULSCH_id, ulsch->harq_pid, 1, 1, crc, pdu); + nfapi_nr_rx_data_pdu_t *rx = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++]; + nr_fill_indication(gNB, frame_rx, slot_rx, pusch_vars, pdu, stats, NULL, 1, crc, rx); pusch_DTX++; gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_DTX"); continue; From 25f77993caa23d134c5d127d8184278cc91a994e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 17:53:08 +0100 Subject: [PATCH 56/72] Refactor PUSCH processing into its own function Signed-off-by: Robert Schmidt --- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 168 +++++++++++----------- 1 file changed, 88 insertions(+), 80 deletions(-) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index c9f94ff5b6..dc74669532 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -919,6 +919,92 @@ static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_ } } +static bool handle_pusch_decode_trigger(PHY_VARS_gNB *gNB, NR_gNB_PUSCH *pusch_vars, NR_gNB_ULSCH_t *ulsch, NR_UL_IND_t *UL_INFO, int *pusch_DTX) +{ + NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; + AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); + const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu; + +#ifdef DEBUG_RXDATA + RU_t *ru = gNB->RU_list[0]; + int slot_offset = frame_parms->get_samples_slot_timestamp(ulsch->slot, frame_parms, 0); + slot_offset -= ru->N_TA_offset; + int32_t sample_offset = gNB->common_vars.debugBuff_sample_offset; + int16_t buf = (int16_t *)&gNB->common_vars.debugBuff[offset]; + buf[0] = (int16_t)ulsch->rnti; + buf[1] = (int16_t)pdu->rb_size; + buf[2] = (int16_t)pdu->rb_start; + buf[3] = (int16_t)pdu->nr_of_symbols; + buf[4] = (int16_t)pdu->start_symbol_index; + buf[5] = (int16_t)pdu->mcs_index; + buf[6] = (int16_t)pdu->pusch_data.rv_index; + buf[7] = (int16_t)ulsch->harq_pid; + memcpy(&gNB->common_vars.debugBuff[gNB->common_vars.debugBuff_sample_offset + 4], + &ru->common.rxdata[0][slot_offset], + frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) * sizeof(int32_t)); + gNB->common_vars.debugBuff_sample_offset += (frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4); + if (gNB->common_vars.debugBuff_sample_offset > ((frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 2) * 20)) { + FILE *f; + f = fopen("rxdata_buff.raw", "w"); + if (f == NULL) + exit(1); + fwrite((int16_t *)gNB->common_vars.debugBuff, + 2, + (frame_parms->get_samples_per_slot(ulsch->slot, frame_parms) + 1000 + 4) * 20 * 2, + f); + fclose(f); + exit(-1); + } +#endif + + start_meas(&gNB->rx_pusch_stats); + nr_rx_pusch_tp(gNB, pusch_vars, pdu, &ulsch->unav_res, ulsch->frame, ulsch->slot, ulsch->beam_nb); + pusch_vars->ulsch_power_tot = 0; + pusch_vars->ulsch_noise_power_tot = 0; + for (int aarx = 0; aarx < gNB->frame_parms.nb_antennas_rx; aarx++) { + pusch_vars->ulsch_power_tot += pusch_vars->ulsch_power[aarx]; + pusch_vars->ulsch_noise_power_tot += pusch_vars->ulsch_noise_power[aarx]; + } + if (dB_fixed_x10(pusch_vars->ulsch_power_tot) < dB_fixed_x10(pusch_vars->ulsch_noise_power_tot) + gNB->pusch_thres) { + NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti); + + LOG_D(PHY, + "PUSCH not detected in %d.%d (%d,%d,%d)\n", + ulsch->frame, + ulsch->slot, + dB_fixed_x10(pusch_vars->ulsch_power_tot), + dB_fixed_x10(pusch_vars->ulsch_noise_power_tot), + gNB->pusch_thres); + pusch_vars->ulsch_power_tot = pusch_vars->ulsch_noise_power_tot; + pusch_vars->DTX = 1; + if (stats) + stats->ulsch_stats.DTX++; + 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 */ + nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++]; + 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)++; + gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_DTX"); + return false; + } + } else { + LOG_D(PHY, + "PUSCH detected in %d.%d (%d,%d,%d)\n", + ulsch->frame, + ulsch->slot, + dB_fixed_x10(pusch_vars->ulsch_power_tot), + dB_fixed_x10(pusch_vars->ulsch_noise_power_tot), + gNB->pusch_thres); + + pusch_vars->DTX = 0; + } + stop_meas(&gNB->rx_pusch_stats); + + return true; +} + static bool drop_old_pucch(const void *data, void *user) { const NR_gNB_PUCCH_job_t *pucch = data; @@ -999,92 +1085,14 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { ulsch_idx_to_decode[ULSCH_id] = -1; NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; - NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; - AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !ulsch->handled)) continue; - LOG_D(PHY, "PUSCH ID %d with RNTI %x detection started in frame %d slot %d\n", ULSCH_id, ulsch->rnti, frame_rx, slot_rx); - const nfapi_nr_pusch_pdu_t *pdu = &ulsch_harq->ulsch_pdu; - -#ifdef DEBUG_RXDATA - RU_t *ru = gNB->RU_list[0]; - int slot_offset = frame_parms->get_samples_slot_timestamp(slot_rx, frame_parms, 0); - slot_offset -= ru->N_TA_offset; - int32_t sample_offset = gNB->common_vars.debugBuff_sample_offset; - int16_t buf = (int16_t *)&gNB->common_vars.debugBuff[offset]; - buf[0] = (int16_t)ulsch->rnti; - buf[1] = (int16_t)pdu->rb_size; - buf[2] = (int16_t)pdu->rb_start; - buf[3] = (int16_t)pdu->nr_of_symbols; - buf[4] = (int16_t)pdu->start_symbol_index; - buf[5] = (int16_t)pdu->mcs_index; - buf[6] = (int16_t)pdu->pusch_data.rv_index; - buf[7] = (int16_t)ulsch->harq_pid; - memcpy(&gNB->common_vars.debugBuff[gNB->common_vars.debugBuff_sample_offset + 4], - &ru->common.rxdata[0][slot_offset], - get_samples_per_slot(slot_rx, frame_parms) * sizeof(int32_t)); - gNB->common_vars.debugBuff_sample_offset += (get_samples_per_slot(slot_rx, frame_parms) + 1000 + 4); - if (gNB->common_vars.debugBuff_sample_offset > ((get_samples_per_slot(slot_rx, frame_parms) + 1000 + 2) * 20)) { - FILE *f; - f = fopen("rxdata_buff.raw", "w"); - if (f == NULL) - exit(1); - fwrite((int16_t *)gNB->common_vars.debugBuff, 2, (get_samples_per_slot(slot_rx, frame_parms) + 1000 + 4) * 20 * 2, f); - fclose(f); - exit(-1); - } -#endif pusch_decode_done = 1; - - start_meas(&gNB->rx_pusch_stats); NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; - nr_rx_pusch_tp(gNB, pusch_vars, pdu, &ulsch->unav_res, frame_rx, slot_rx, ulsch->beam_nb); - pusch_vars->ulsch_power_tot = 0; - pusch_vars->ulsch_noise_power_tot = 0; - for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { - pusch_vars->ulsch_power_tot += pusch_vars->ulsch_power[aarx]; - pusch_vars->ulsch_noise_power_tot += pusch_vars->ulsch_noise_power[aarx]; - } - if (dB_fixed_x10(pusch_vars->ulsch_power_tot) < dB_fixed_x10(pusch_vars->ulsch_noise_power_tot) + gNB->pusch_thres) { - NR_gNB_PHY_STATS_t *stats = get_phy_stats(gNB, ulsch->rnti); - LOG_D(PHY, - "PUSCH not detected in %d.%d (%d,%d,%d)\n", - frame_rx, - slot_rx, - dB_fixed_x10(pusch_vars->ulsch_power_tot), - dB_fixed_x10(pusch_vars->ulsch_noise_power_tot), - gNB->pusch_thres); - pusch_vars->ulsch_power_tot = pusch_vars->ulsch_noise_power_tot; - pusch_vars->DTX = 1; - if (stats) - stats->ulsch_stats.DTX++; - 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 */ - nfapi_nr_crc_t *crc = &UL_INFO->crc_ind.crc_list[UL_INFO->crc_ind.number_crcs++]; - nfapi_nr_rx_data_pdu_t *rx = &UL_INFO->rx_ind.pdu_list[UL_INFO->rx_ind.number_of_pdus++]; - nr_fill_indication(gNB, frame_rx, slot_rx, pusch_vars, pdu, stats, NULL, 1, crc, rx); - pusch_DTX++; - gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_DTX"); - continue; - } - } else { - LOG_D(PHY, - "PUSCH detected in %d.%d (%d,%d,%d)\n", - frame_rx, - slot_rx, - dB_fixed_x10(pusch_vars->ulsch_power_tot), - dB_fixed_x10(pusch_vars->ulsch_noise_power_tot), - gNB->pusch_thres); - - pusch_vars->DTX = 0; - } - ulsch_idx_to_decode[num_pusch++] = ULSCH_id; - stop_meas(&gNB->rx_pusch_stats); - // LOG_M("rxdataF_comp.m","rxF_comp",gNB->pusch_vars[0]->rxdataF_comp[0],6900,1,1); - // LOG_M("rxdataF_ext.m","rxF_ext",gNB->pusch_vars[0]->rxdataF_ext[0],6900,1,1); + if (handle_pusch_decode_trigger(gNB, pusch_vars, ulsch, UL_INFO, &pusch_DTX)) + ulsch_idx_to_decode[num_pusch++] = ULSCH_id; } /* Do ULSCH decoding time measurement only when number of PUSCH is limited to 1 From 138e0f95472dd3558149e3c997f64aac1c1dbb3e Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 09:39:11 +0200 Subject: [PATCH 57/72] Use spsc_q for PUSCH queue Use the spsc queue to pass jobs from TX thread (scheduler) to RX thread (handling those jobs), reducing the amount spent searching for PUSCH jobs, clarifying the design, and making it thread-safe. Since the PUSCH contexts have to be stored somewhere, there is still a single iteration over all contexts. Removing that is left for future work. Thus, this commit (together with previous commits) reduces the number of iterations over all PUSCH entries, only iterating over the actually requested PUSCH jobs. On the other hand, because only the RX thread iterates over PUSCH contexts, it should be thread-safe. Remove flag "handled", which is always 0. Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init.c | 2 + openair1/PHY/NR_TRANSPORT/nr_ulsch.c | 69 +++----------- openair1/PHY/defs_gNB.h | 16 +++- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 110 +++++++++++++++++----- 4 files changed, 113 insertions(+), 84 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 8a2a794fbd..5845b14d51 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -442,6 +442,7 @@ void init_nr_transport(PHY_VARS_gNB *gNB) int max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; gNB->pucch_queue = spsc_q_alloc(max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t)); + gNB->pusch_queue = spsc_q_alloc(gNB->max_nb_pusch, sizeof(NR_gNB_PUSCH_job_t)); gNB->srs = (NR_gNB_SRS_t *)malloc16(gNB->max_nb_srs * sizeof(NR_gNB_SRS_t)); for (int i = 0; i < gNB->max_nb_srs; i++) @@ -463,6 +464,7 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; spsc_q_free(&gNB->pucch_queue); + spsc_q_free(&gNB->pusch_queue); free(gNB->srs); for (int i = 0; i < gNB->max_nb_pusch; i++) diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c index 2d7727b55a..51d885cc1e 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch.c @@ -11,30 +11,6 @@ #include "PHY/NR_TRANSPORT/nr_ulsch.h" #include "SCHED_NR/sched_nr.h" -static NR_gNB_ULSCH_t *find_nr_ulsch(PHY_VARS_gNB *gNB, uint16_t rnti, int pid) -{ - int16_t first_free_index = -1; - AssertFatal(gNB != NULL, "gNB is null\n"); - NR_gNB_ULSCH_t *ulsch = NULL; - - for (int i = 0; i < gNB->max_nb_pusch; i++) { - ulsch = &gNB->ulsch[i]; - AssertFatal(ulsch, "gNB->ulsch[%d] is null\n", i); - if (!ulsch->active) { - if (first_free_index == -1) - first_free_index = i; - } else { - // if there is already an active ULSCH for this RNTI and HARQ_PID - if ((ulsch->harq_pid == pid) && (ulsch->rnti == rnti)) - return ulsch; - } - } - if (first_free_index != -1) - ulsch = &gNB->ulsch[first_free_index]; - - return ulsch; -} - static void dump_pusch_pdu(int instance, int frame, int slot, nfapi_nr_pusch_pdu_t *pusch_pdu) { LOG_D(PHY, @@ -117,49 +93,28 @@ static void dump_pusch_pdu(int instance, int frame, int slot, nfapi_nr_pusch_pdu void nr_fill_ulsch(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu) { dump_pusch_pdu(gNB->Mod_id, frame, slot, ulsch_pdu); - int harq_pid = ulsch_pdu->pusch_data.harq_process_id; - NR_gNB_ULSCH_t *ulsch = find_nr_ulsch(gNB, ulsch_pdu->rnti, harq_pid); - if (ulsch == NULL) { - LOG_E(NR_PHY, "No ulsch_id found for rnti %04x\n", ulsch_pdu->rnti); - return; - } + LOG_D(NR_PHY, + "%4d.%2d Programming ULSCH RNTI %04x HARQ PID %d new data indicator %d\n", + frame, + slot, + ulsch_pdu->rnti, + ulsch_pdu->pusch_data.harq_process_id, + ulsch_pdu->pusch_data.new_data_indicator); - ulsch->rnti = ulsch_pdu->rnti; - ulsch->harq_pid = harq_pid; - ulsch->handled = 0; - ulsch->active = true; - ulsch->beam_nb = 0; + NR_gNB_PUSCH_job_t pusch = {.frame = frame, .slot = slot, .pusch_pdu = *ulsch_pdu}; if (gNB->common_vars.beam_id) { int fapi_beam_idx = ulsch_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; int bitmap = SL_to_bitmap(ulsch_pdu->start_symbol_index, ulsch_pdu->nr_of_symbols); - ulsch->beam_nb = beam_index_allocation(gNB->enable_analog_das, + pusch.beam_nb = beam_index_allocation(gNB->enable_analog_das, fapi_beam_idx, &gNB->common_vars, slot, gNB->frame_parms.symbols_per_slot, bitmap); } - ulsch->frame = frame; - ulsch->slot = slot; - - NR_UL_gNB_HARQ_t *harq = ulsch->harq_process; - if (ulsch_pdu->pusch_data.new_data_indicator) - harq->harq_to_be_cleared = true; - LOG_D(NR_PHY, - "NEW ULSCH %d.%d RNTI %x HARQ PID %d new data indicator %d\n", - frame, - slot, - ulsch_pdu->rnti, - harq_pid, - ulsch_pdu->pusch_data.new_data_indicator); - - if (ulsch_pdu->pusch_data.new_data_indicator) - harq->round = 0; - else - harq->round++; - - memcpy(&ulsch->harq_process->ulsch_pdu, ulsch_pdu, sizeof(ulsch->harq_process->ulsch_pdu)); - LOG_D(PHY, "Initializing nFAPI for ULSCH, harq_pid %d, layers %d\n", harq_pid, ulsch_pdu->nrOfLayers); + bool done = spsc_q_put(&gNB->pusch_queue, &pusch, sizeof(pusch)); + if (!done) + LOG_W(NR_PHY, "PUSCH queue is full: dropping PUSCH UE %04x\n", ulsch_pdu->rnti); } void reset_active_ulsch(PHY_VARS_gNB *gNB, int frame) diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 8ab19981dd..6c805b5d16 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -128,7 +128,7 @@ typedef struct { typedef struct { /// Nfapi ULSCH PDU - nfapi_nr_pusch_pdu_t ulsch_pdu; + nfapi_nr_pusch_pdu_t ulsch_pdu; // !! /// Index of current HARQ round for this DLSCH uint8_t round; bool new_rx; @@ -183,10 +183,19 @@ typedef struct { int8_t last_iteration_cnt; /// Status Flag indicating for this ULSCH bool active; - /// Flag to indicate that the UL configuration has been handled. Used to remove a stale ULSCH when frame wraps around - uint8_t handled; } NR_gNB_ULSCH_t; +typedef struct { + // identifier for concurrent beams + int beam_nb; + /// Frame where current PUSCH pdu was sent + uint32_t frame; + /// Slot where current PUSCH pdu was sent + uint32_t slot; + /// ULSCH PDU + nfapi_nr_pusch_pdu_t pusch_pdu; +} NR_gNB_PUSCH_job_t; + typedef struct { // identifier for concurrent beams int beam_nb; @@ -379,6 +388,7 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_PRS prs_vars; NR_gNB_PUSCH *pusch_vars; spsc_q_t pucch_queue; + spsc_q_t pusch_queue; NR_gNB_SRS_t *srs; NR_gNB_ULSCH_t *ulsch; NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB]; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index dc74669532..1f330ae58e 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -450,7 +450,6 @@ static int nr_ulsch_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, int pusch_pdu->pusch_data.tb_size); nr_fill_indication(gNB, ulsch->frame, ulsch->slot, pusch, pusch_pdu, stats, NULL, 0, crc, pdu); gNBdumpScopeData(gNB, ulsch->slot, ulsch->frame, "ULSCH_NACK"); - ulsch->handled = 1; LOG_D(PHY, "ULSCH %d in error\n",ULSCH_id); ulsch->last_iteration_cnt = ulsch->max_ldpc_iterations; // Setting to max_ldpc_iterations is sufficient given that this variable is only used for checking for failure } @@ -535,7 +534,9 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, uint32_t rb_mask_ul[14][9], nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf, const NR_gNB_PUCCH_job_t *pucch, - int n_pucch) + int n_pucch, + const NR_gNB_PUSCH_job_t *pusch, + int n_pusch) { for (int symbol = 0; symbol < 14; symbol++) { for (int m = 0; m < 9; m++) { @@ -565,19 +566,15 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, } } - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { - NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; - NR_UL_gNB_HARQ_t *ulsch_harq = ulsch->harq_process; - AssertFatal(ulsch_harq != NULL, "harq_pid %d is not allocated\n", ulsch->harq_pid); - if (!(ulsch->active && ulsch->frame == now.f && ulsch->slot == now.s && !ulsch->handled)) - continue; - uint8_t symbol_start = ulsch_harq->ulsch_pdu.start_symbol_index; - uint8_t symbol_end = symbol_start + ulsch_harq->ulsch_pdu.nr_of_symbols; + for (int i = 0; i < n_pusch; i++) { + const nfapi_nr_pusch_pdu_t *pusch_pdu = &pusch[i].pusch_pdu; + uint8_t symbol_start = pusch_pdu->start_symbol_index; + uint8_t symbol_end = symbol_start + pusch_pdu->nr_of_symbols; for (int symbol = symbol_start; symbol < symbol_end; symbol++) { if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) { - LOG_D(PHY, "symbol %d Filling rb_mask_ul rb_size %d\n", symbol, ulsch_harq->ulsch_pdu.rb_size); - for (int rb = 0; rb < ulsch_harq->ulsch_pdu.rb_size; rb++) { - int rb2 = rb + ulsch_harq->ulsch_pdu.rb_start + ulsch_harq->ulsch_pdu.bwp_start; + LOG_D(PHY, "symbol %d Filling rb_mask_ul rb_size %d\n", symbol, pusch_pdu->rb_size); + for (int rb = 0; rb < pusch_pdu->rb_size; rb++) { + int rb2 = rb + pusch_pdu->rb_start + pusch_pdu->bwp_start; rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31); } } @@ -1024,10 +1021,74 @@ static bool get_current_pucch(const void *data, void *user) return fsn_equal(t, *now); } +static bool drop_old_pusch(const void *data, void *user) +{ + const NR_gNB_PUSCH_job_t *pusch = data; + const fsn_t *now = user; + const fsn_t t = {pusch->frame, pusch->slot, now->mu}; + bool drop = fsn_in_the_past(t, *now); + if (drop) + LOG_E(NR_PHY, "%4d.%2d PUSCH job for UE %04x is in the past (%4d.%2d)\n", now->f, now->s, pusch->pusch_pdu.rnti, t.f, t.s); + return drop; +} + +static bool get_current_pusch(const void *data, void *user) +{ + const NR_gNB_PUSCH_job_t *pusch = data; + const fsn_t *now = user; + const fsn_t t = {pusch->frame, pusch->slot, now->mu}; + return fsn_equal(t, *now); +} + +static int find_nr_ulsch_idx(PHY_VARS_gNB *gNB, uint16_t rnti, int pid) +{ + AssertFatal(gNB != NULL, "gNB is null\n"); + + int16_t first_free_index = -1; + for (int i = 0; i < gNB->max_nb_pusch; i++) { + const NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[i]; + AssertFatal(ulsch, "gNB->ulsch[%d] is null\n", i); + if (!ulsch->active) { + if (first_free_index == -1) + first_free_index = i; + } else { + // if there is already an active ULSCH for this RNTI and HARQ_PID + if ((ulsch->harq_pid == pid) && (ulsch->rnti == rnti)) + return i; + } + } + return first_free_index; +} +static int handle_pusch_job_trigger(PHY_VARS_gNB *gNB, const NR_gNB_PUSCH_job_t *job) +{ + const nfapi_nr_pusch_pdu_t *pdu = &job->pusch_pdu; + int pid = pdu->pusch_data.harq_process_id; + int ULSCH_id = find_nr_ulsch_idx(gNB, pdu->rnti, pid); + if (ULSCH_id < 0) { + LOG_E(NR_PHY, "%4d.%2d cannot handle PUSCH job of RNTI %04x: no space\n", job->frame, job->slot, pdu->rnti); + return -1; + } + /* (re-)initialize this PUSCH context from job data */ + NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; + ulsch->active = true; + ulsch->frame = job->frame; + ulsch->slot = job->slot; + ulsch->rnti = pdu->rnti; + ulsch->harq_pid = pid; + ulsch->beam_nb = job->beam_nb; + ulsch->harq_process->ulsch_pdu = job->pusch_pdu; + if (pdu->pusch_data.new_data_indicator) { + ulsch->harq_process->harq_to_be_cleared = true; + ulsch->harq_process->round = 0; + } else { + ulsch->harq_process->round++; + } + return ULSCH_id; +} + int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO) { /* those variables to log T_GNB_PHY_PUCCH_PUSCH_IQ only when we try to decode */ - int pusch_decode_done = 0; int pusch_DTX = 0; const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; @@ -1040,6 +1101,10 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N NR_gNB_PUCCH_job_t pucch[MAX_NUM_NR_UCI_PDUS]; int n_pucch = spsc_q_get_while(&gNB->pucch_queue, get_current_pucch, &now, pucch, sizeof(*pucch), MAX_NUM_NR_UCI_PDUS); + spsc_q_drop_while(&gNB->pusch_queue, drop_old_pusch, &now); + NR_gNB_PUSCH_job_t pusch[MAX_UL_PDUS_PER_SLOT]; + int n_pusch_jobs = spsc_q_get_while(&gNB->pusch_queue, get_current_pusch, &now, pusch, sizeof(*pusch), MAX_UL_PDUS_PER_SLOT); + LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB @@ -1047,7 +1112,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL; if (frame_parms->frame_type == TDD) slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; - fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch); + fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs); int first_symb = 0, num_symb = 0; if (frame_parms->frame_type == TDD) @@ -1080,17 +1145,14 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N UL_INFO->rx_ind.sfn = frame_rx; UL_INFO->rx_ind.slot = slot_rx; UL_INFO->rx_ind.pdu_list = UL_INFO->rx_pdu_list; + int ulsch_idx_to_decode[MAX_UL_PDUS_PER_SLOT]; int num_pusch = 0; - int ulsch_idx_to_decode[gNB->max_nb_pusch]; - for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { - ulsch_idx_to_decode[ULSCH_id] = -1; - NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; - if (!(ulsch->active && ulsch->frame == frame_rx && ulsch->slot == slot_rx && !ulsch->handled)) + for (int i = 0; i < n_pusch_jobs; ++i) { + int ULSCH_id = handle_pusch_job_trigger(gNB, &pusch[i]); + if (ULSCH_id < 0) continue; - - pusch_decode_done = 1; NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id]; - + NR_gNB_ULSCH_t *ulsch = &gNB->ulsch[ULSCH_id]; if (handle_pusch_decode_trigger(gNB, pusch_vars, ulsch, UL_INFO, &pusch_DTX)) ulsch_idx_to_decode[num_pusch++] = ULSCH_id; } @@ -1316,7 +1378,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N stop_meas(&gNB->phy_proc_rx); - if (n_pucch > 0 || pusch_decode_done) { + if (n_pucch > 0 || num_pusch > 0) { T(T_GNB_PHY_PUCCH_PUSCH_IQ, T_INT(frame_rx), T_INT(slot_rx), From 2213235fc103ac95eda6fb880a98e79c7f2af6e6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 10:31:56 +0200 Subject: [PATCH 58/72] Simplify UE SRS: no global memory required Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init_ue.c | 4 --- .../NR_UE_TRANSPORT/nr_transport_proto_ue.h | 10 ++++---- openair1/PHY/defs_nr_UE.h | 3 --- openair1/SCHED_NR_UE/phy_procedures_nr_ue.c | 25 ++++++++----------- openair1/SIMULATION/NR_PHY/srssim.c | 8 +----- 5 files changed, 16 insertions(+), 34 deletions(-) diff --git a/openair1/PHY/INIT/nr_init_ue.c b/openair1/PHY/INIT/nr_init_ue.c index d7d73e1c01..cccf4cf3a8 100644 --- a/openair1/PHY/INIT/nr_init_ue.c +++ b/openair1/PHY/INIT/nr_init_ue.c @@ -251,8 +251,6 @@ int init_nr_ue_signal(PHY_VARS_NR_UE *ue, int nb_connected_gNB) ue->nr_csi_info->csi_rs_generated_signal[i] = malloc16_clear(fp->samples_per_slot_wCP * sizeof(**ue->nr_csi_info->csi_rs_generated_signal)); } - - ue->nr_srs_info = malloc16_clear(sizeof(nr_srs_info_t)); } ue->init_averaging = 1; @@ -301,8 +299,6 @@ void term_nr_ue_signal(PHY_VARS_NR_UE *ue) free_and_zero(ue->nr_csi_info->csi_rs_generated_signal); free_and_zero(ue->nr_csi_info); - free_and_zero(ue->nr_srs_info); - free_and_zero(ue->prach_vars[gNB_id]); } diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 32d03c1010..7de87cc2ee 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -148,11 +148,11 @@ void nr_tx_rotation_and_ofdm_mod(const uint8_t slot, bool was_symbol_used[NR_SYMBOLS_PER_SLOT], bool no_phase_pre_comp); -bool ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, - const UE_nr_rxtx_proc_t *proc, - c16_t **txdataF, - nr_phy_data_tx_t *phy_data, - bool was_symbol_used[NR_SYMBOLS_PER_SLOT]); +void ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, + const UE_nr_rxtx_proc_t *proc, + c16_t **txdataF, + const fapi_nr_ul_config_srs_pdu *srs_config_pdu, + bool was_symbol_used[NR_SYMBOLS_PER_SLOT]); void clean_UE_harq(PHY_VARS_NR_UE *UE); diff --git a/openair1/PHY/defs_nr_UE.h b/openair1/PHY/defs_nr_UE.h index 63771f7125..37a4269ee2 100644 --- a/openair1/PHY/defs_nr_UE.h +++ b/openair1/PHY/defs_nr_UE.h @@ -402,9 +402,6 @@ typedef struct PHY_VARS_NR_UE_s { nrLDPC_coding_interface_t nrLDPC_coding_interface; uint8_t max_ldpc_iterations; - /// SRS variables - nr_srs_info_t *nr_srs_info; - /// CSI variables nr_csi_info_t *nr_csi_info; diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index 225634937b..f9c8f38b56 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -243,7 +243,7 @@ void ue_ta_procedures(PHY_VARS_NR_UE *ue, int slot_tx, int frame_tx) } } -static void configure_srs_info(fapi_nr_ul_config_srs_pdu *srs_config_pdu, nr_srs_info_t *nr_srs_info) +static void configure_srs_info(const fapi_nr_ul_config_srs_pdu *srs_config_pdu, nr_srs_info_t *nr_srs_info) { nr_srs_info->B_SRS = srs_config_pdu->bandwidth_index; nr_srs_info->C_SRS = srs_config_pdu->config_index; @@ -272,25 +272,17 @@ static void configure_srs_info(fapi_nr_ul_config_srs_pdu *srs_config_pdu, nr_srs * PARAMETERS : pointer to ue context * pointer to rxtx context* * -* RETURN : 0 if it is a valid slot for transmitting srs -* -1 if srs should not be transmitted -* * DESCRIPTION : ue srs procedure * send srs according to current configuration * *********************************************************************/ -bool ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, +void ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, c16_t **txdataF, - nr_phy_data_tx_t *phy_data, + const fapi_nr_ul_config_srs_pdu *srs_config_pdu, bool was_symbol_used[NR_SYMBOLS_PER_SLOT]) { - if(phy_data->srs_vars.active == false) { - return false; - } - NR_DL_FRAME_PARMS *frame_parms = &(ue->frame_parms); - fapi_nr_ul_config_srs_pdu *srs_config_pdu = &phy_data->srs_vars.srs_config_pdu; const uint8_t l0 = frame_parms->symbols_per_slot - 1 - srs_config_pdu->time_start_position; // Num consecutive SRS symbols according to 38.211 6.4.1.4.1 int num_srs_symbols[] = {1, 2, 4, 8, 12}; @@ -326,18 +318,20 @@ bool ue_srs_procedures_nr(PHY_VARS_NR_UE *ue, LOG_I(NR_PHY,"srs_config_pdu->t_offset = %u\n", srs_config_pdu->t_offset); #endif - configure_srs_info(srs_config_pdu, ue->nr_srs_info); + nr_srs_info_t nr_srs_info = {0}; + configure_srs_info(srs_config_pdu, &nr_srs_info); uint16_t symbol_offset = l0 * frame_parms->ofdm_symbol_size; bool generated = generate_srs_nr(frame_parms, txdataF, symbol_offset, srs_config_pdu->bwp_start, - ue->nr_srs_info, + &nr_srs_info, AMP, proc->frame_tx, proc->nr_slot_tx, frame_parms->nb_antennas_tx); - return generated; + DevAssert(generated); // if we can't generate despite the SRS config, there + // is a problem } void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, nr_phy_data_tx_t *phy_data, c16_t **txp) @@ -365,7 +359,8 @@ void phy_procedures_nrUE_TX(PHY_VARS_NR_UE *ue, const UE_nr_rxtx_proc_t *proc, n nr_ue_ulsch_procedures(ue, frame_tx, slot_tx, phy_data, (c16_t **)&txdataF, was_symbol_used); - ue_srs_procedures_nr(ue, proc, (c16_t **)&txdataF, phy_data, was_symbol_used); + if (phy_data->srs_vars.active) + ue_srs_procedures_nr(ue, proc, (c16_t **)&txdataF, &phy_data->srs_vars.srs_config_pdu, was_symbol_used); pucch_procedures_ue_nr(ue, proc, phy_data, (c16_t **)&txdataF, was_symbol_used); diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index afb4da5a80..6fcc763970 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -467,11 +467,6 @@ int main(int argc, char *argv[]) .beamforming.num_prgs = srs_pdu.beamforming.num_prgs, .beamforming.prg_size = srs_pdu.beamforming.prg_size}; - nr_phy_data_tx_t phy_data = {0}; - phy_data.srs_vars.active = true; - phy_data.srs_vars.srs_config_pdu = srs_config_pdu; - UE->nr_srs_info = malloc16_clear(sizeof(nr_srs_info_t)); - //----------- UE TX SRS procedures --------------------- UE_nr_rxtx_proc_t proc; @@ -492,7 +487,7 @@ int main(int argc, char *argv[]) for (i = 0; i < n_tx; i++) txd[i] = txdata[i] + slot_offset; - ue_srs_procedures_nr(UE, &proc, (c16_t **)txF, &phy_data, was_symbol_used); + ue_srs_procedures_nr(UE, &proc, (c16_t **)txF, &srs_config_pdu, was_symbol_used); //------------ TX rotation and OFDM Modulation -------------------------- nr_tx_rotation_and_ofdm_mod(slot, fp, n_tx, txF, txd, link_type_ul, was_symbol_used, false); @@ -666,7 +661,6 @@ int main(int argc, char *argv[]) phy_free_nr_gNB(gNB); free_channel_desc_scm(UE2gNB); - free_and_zero(UE->nr_srs_info); free(gNB->RU_list[0]); free(UE); From 4a47fbbef7639c922eb79d14b455c11bcebfc13a Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 10:46:18 +0200 Subject: [PATCH 59/72] Remove global gNB SRS memory This means that as of this commit, the signal has to be permanently regenerated. In fact, with the SRS queue to be introduced in a follow-up commit, we don't have an easy way to cache the sequence generation, as there is no "index" to which we could refer to (hence, caching would be tricky). Instead, the next commits further refactor this to reduce the time for SRS sequence generation as well as the memory footprint. Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init.c | 19 ------ .../PHY/NR_TRANSPORT/nr_transport_proto.h | 3 +- openair1/PHY/defs_gNB.h | 3 - openair1/PHY/defs_nr_common.h | 2 - openair1/SCHED_NR/phy_procedures_nr_gNB.c | 63 ++++++------------- openair1/SIMULATION/NR_PHY/srssim.c | 1 - 6 files changed, 21 insertions(+), 70 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 5845b14d51..7d56f8b410 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -141,16 +141,6 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) init_DLSCH_struct(gNB); - gNB->nr_srs_info = (nr_srs_info_t **)malloc16_clear(gNB->max_nb_srs * sizeof(nr_srs_info_t*)); - for (int id = 0; id < gNB->max_nb_srs; id++) { - gNB->nr_srs_info[id] = (nr_srs_info_t *)malloc16_clear(sizeof(nr_srs_info_t)); - gNB->nr_srs_info[id]->srs_generated_signal = malloc16_clear(MAX_NUM_NR_SRS_AP * sizeof(c16_t *)); - for(int ap=0; apnr_srs_info[id]->srs_generated_signal[ap] = - malloc16_clear(fp->ofdm_symbol_size * MAX_NUM_NR_SRS_SYMBOLS * sizeof(c16_t)); - } - } - /* Do NOT allocate per-antenna rxdataF: the gNB gets a pointer to the * RU to copy/recover freq-domain memory from there */ common_vars->rxdataF = (c16_t ***)malloc16(common_vars->num_beams_period * sizeof(c16_t**)); @@ -212,15 +202,6 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) PHY_MEASUREMENTS_gNB *meas = &gNB->measurements; free_and_zero(meas->n0_subband_power); - for (int id = 0; id < gNB->max_nb_srs; id++) { - for(int i=0; inr_srs_info[id]->srs_generated_signal[i]); - } - free_and_zero(gNB->nr_srs_info[id]->srs_generated_signal); - free_and_zero(gNB->nr_srs_info[id]); - } - free_and_zero(gNB->nr_srs_info); - free_ul_reference_signal_sequences(); free_gnb_lowpapr_sequences(); diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 43ecb0d006..d1431b6237 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -202,7 +202,7 @@ int nr_get_srs_signal(PHY_VARS_gNB *gNB, c16_t srs_received_signal[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)], c16_t srs_received_noise[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)]); -void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, +nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, uint8_t nb_antennas_rx, @@ -210,7 +210,6 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, NR_gNB_SRS_t *srs, - nr_srs_info_t *nr_srs_info, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 6c805b5d16..ef357508d7 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -394,9 +394,6 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB]; t_nrPolar_params **polarParams; - /// SRS variables - nr_srs_info_t **nr_srs_info; - // reference amplitude for TX int16_t TX_AMP; diff --git a/openair1/PHY/defs_nr_common.h b/openair1/PHY/defs_nr_common.h index 7cd439bcee..8b50f36290 100644 --- a/openair1/PHY/defs_nr_common.h +++ b/openair1/PHY/defs_nr_common.h @@ -77,8 +77,6 @@ typedef enum{ typedef struct { uint8_t k_0_p[MAX_NUM_NR_SRS_AP][MAX_NUM_NR_SRS_SYMBOLS]; uint8_t srs_generated_signal_bits; - c16_t **srs_generated_signal; - bool is_signal_generated; int B_SRS; int C_SRS; int b_hop; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 1f330ae58e..59285dd884 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -696,37 +696,7 @@ static void copy_srs_info(const nfapi_nr_srs_pdu_t *srs_config_pdu, nr_srs_info_ nr_srs_info->resource_type = srs_config_pdu->resource_type; } -static bool srs_has_changed(const nfapi_nr_srs_pdu_t *srs_config_pdu, nr_srs_info_t *nr_srs_info) -{ - return nr_srs_info->B_SRS != srs_config_pdu->bandwidth_index - || nr_srs_info->C_SRS != srs_config_pdu->config_index - || nr_srs_info->b_hop != srs_config_pdu->frequency_hopping - || nr_srs_info->comb_size != srs_config_pdu->comb_size - || nr_srs_info->K_TC_overbar != srs_config_pdu->comb_offset - || nr_srs_info->n_SRS_cs != srs_config_pdu->cyclic_shift - || nr_srs_info->n_ID_SRS != srs_config_pdu->sequence_id - || nr_srs_info->n_shift != srs_config_pdu->frequency_position - || nr_srs_info->n_RRC != srs_config_pdu->frequency_shift - || nr_srs_info->groupOrSequenceHopping != srs_config_pdu->group_or_sequence_hopping - || nr_srs_info->l_offset != srs_config_pdu->time_start_position - || nr_srs_info->T_SRS != srs_config_pdu->t_srs - || nr_srs_info->T_offset != srs_config_pdu->t_offset - || nr_srs_info->R != 1 << srs_config_pdu->num_repetitions - || nr_srs_info->N_symb_SRS != 1 << srs_config_pdu->num_symbols - || nr_srs_info->n_srs_ports != 1 << srs_config_pdu->num_ant_ports - || nr_srs_info->resource_type != srs_config_pdu->resource_type; -} - -static bool check_and_configure_srs_info(nfapi_nr_srs_pdu_t *srs_config_pdu, nr_srs_info_t *nr_srs_info) -{ - if (srs_has_changed(srs_config_pdu, nr_srs_info)) { - copy_srs_info(srs_config_pdu, nr_srs_info); - return true; - } - return false; -} - -void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, +nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, uint8_t nb_antennas_rx, @@ -734,7 +704,6 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, NR_gNB_SRS_t *srs, - nr_srs_info_t *nr_srs_info, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], @@ -750,12 +719,20 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, start_meas(&gNB->generate_srs_stats); - if (check_and_configure_srs_info(srs_pdu, nr_srs_info)) { + nr_srs_info_t nr_srs_info = {0}; + copy_srs_info(srs_pdu, &nr_srs_info); + + // TODO permanently allocate? + c16_t srs_generated_signal_tmp[N_ap][ofdm_symbol_size * MAX_NUM_NR_SRS_SYMBOLS]; + c16_t *srs_generated_signal[N_ap]; + for (int i = 0; i < N_ap; ++i) + srs_generated_signal[i] = srs_generated_signal_tmp[i]; + if (true) { generate_srs_nr(frame_parms, - nr_srs_info->srs_generated_signal, + srs_generated_signal, 0, srs_pdu->bwp_start, - nr_srs_info, + &nr_srs_info, AMP, frame_rx, slot_rx, @@ -765,7 +742,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, stop_meas(&gNB->generate_srs_stats); c16_t **rxdataF = gNB->common_vars.rxdataF[srs->beam_nb]; start_meas(&gNB->get_srs_signal_stats); - *srs_est = nr_get_srs_signal(gNB, rxdataF, slot_rx, srs_pdu, nr_srs_info, srs_received_signal, srs_received_noise); + *srs_est = nr_get_srs_signal(gNB, rxdataF, slot_rx, srs_pdu, &nr_srs_info, srs_received_signal, srs_received_noise); stop_meas(&gNB->get_srs_signal_stats); uint32_t signal_power_avg = 0; @@ -784,8 +761,8 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, frame_parms->first_carrier_offset, N_symb_SRS, srs_pdu, - nr_srs_info, - nr_srs_info->srs_generated_signal[p_ind], + &nr_srs_info, + srs_generated_signal[p_ind], srs_received_signal[ant_rx_ind], srs_ls_estimated_channel[ant_rx_ind][p_ind], &delay_aux); @@ -803,7 +780,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, frame_parms->first_carrier_offset, N_symb_SRS, srs_pdu, - nr_srs_info, + &nr_srs_info, srs_ls_estimated_channel[ant_rx_ind][p_ind], delay.est_delay, srs_received_noise[ant_rx_ind], @@ -848,7 +825,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, frame_parms->first_carrier_offset, N_symb_SRS, srs_pdu, - nr_srs_info, + &nr_srs_info, signal_power_avg, srs_received_noise[ant_rx_ind], &noise_power_per_ant, @@ -892,6 +869,7 @@ void nr_srs_rx_procedures(PHY_VARS_gNB *gNB, T_INT(slot_rx), T_BUFFER(timing_advance_offset_nsec, nb_antennas_rx * sizeof(int16_t))); } + return nr_srs_info; } static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_job_t *pucch, nfapi_nr_uci_t *uci) @@ -1197,7 +1175,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] __attribute__((aligned(32))); - nr_srs_rx_procedures(gNB, + nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, frame_rx, slot_rx, nb_antennas_rx, @@ -1205,7 +1183,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N N_symb_SRS, ofdm_symbol_size, srs, - gNB->nr_srs_info[i], &srs_est, srs_estimated_channel_freq, srs_estimated_channel_time, @@ -1308,7 +1285,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_srs_normalized_channel_iq_matrix_t nr_srs_channel_iq_matrix; fill_srs_channel_matrix(&nr_srs_channel_iq_matrix, srs_pdu, - gNB->nr_srs_info[i], + &srs_info, srs_pdu->srs_parameters_v4.iq_representation, nb_antennas_rx, srs_pdu->srs_parameters_v4.num_total_ue_antennas, diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index 6fcc763970..fc6186c0f1 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -577,7 +577,6 @@ int main(int argc, char *argv[]) N_symb_SRS, ofdm_symbol_size, srs, - gNB->nr_srs_info[0], &srs_est, srs_estimated_channel_freq, srs_estimated_channel_time, From cb4c3072216bc84f8e5e3230507b3437cb336c6f Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Fri, 10 Apr 2026 16:10:30 +0200 Subject: [PATCH 60/72] Speed up generate_srs_nr() - calculation in float: should be enough precision (it'll go into 16bit ints later...) - avoid round: the lower 15bits will be shifted away, the rounding will have no effect - group some multiplications to avoid re-computation of the same results. Signed-off-by: Robert Schmidt --- .../PHY/nr_phy_common/src/nr_phy_common_srs.c | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c b/openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c index 76ad47a4e2..3bd4746c92 100644 --- a/openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common_srs.c @@ -203,7 +203,7 @@ bool generate_srs_nr(const NR_DL_FRAME_PARMS *frame_parms, nr_srs_info->srs_generated_signal_bits = log2_approx(amp); } uint64_t subcarrier_offset = frame_parms->first_carrier_offset + bwp_start * NR_NB_SC_PER_RB; - double sqrt_N_ap = sqrt(N_ap); + float amp_sqrt_N_ap = amp / sqrt(N_ap); int n_b[nr_srs_info->B_SRS + 1]; // Find index of table which is for this SRS length @@ -298,14 +298,16 @@ bool generate_srs_nr(const NR_DL_FRAME_PARMS *frame_parms, uint16_t l_line_offset = l_line * frame_parms->ofdm_symbol_size; // For each port, and for each OFDM symbol, here it is computed and mapped an SRS sequence with M_sc_b_SRS symbols for (int k = 0; k < M_sc_b_SRS; k++) { - cd_t shift = {cos(alpha_i * k), sin(alpha_i * k)}; - const c16_t tmp = rv_ul_ref_sig[u][v][M_sc_b_SRS_index][k]; - cd_t r_overbar = {tmp.r, tmp.i}; + cf_t shift = {cosf(alpha_i * k), sinf(alpha_i * k)}; + const c16_t r_overbar = rv_ul_ref_sig[u][v][M_sc_b_SRS_index][k]; // cos(x+y) = cos(x)cos(y) - sin(x)sin(y) - cd_t r = cdMul(shift, r_overbar); - c16_t r_amp = {(((int32_t)round((double)amp * r.r / sqrt_N_ap)) >> 15), - (((int32_t)round((double)amp * r.i / sqrt_N_ap)) >> 15)}; + cf_t r = (cf_t) { + .r = shift.r * r_overbar.r - shift.i * r_overbar.i, + .i = shift.r * r_overbar.i + shift.i * r_overbar.r, + }; + c16_t r_amp = {(((int32_t)(amp_sqrt_N_ap * r.r)) >> 15), + (((int32_t)(amp_sqrt_N_ap * r.i)) >> 15)}; #ifdef SRS_DEBUG int subcarrier_log = subcarrier-subcarrier_offset; From 81b2fe634fc71ea48f6a1d458fd356be4b949a82 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 29 Mar 2026 13:59:11 +0200 Subject: [PATCH 61/72] Simplify SRS handling Remove useless logging defines. Move srs_estimated_channel_time to where it is used. Prepare for the next commit by moving UL_INFO to earlier inside phy_procedures_gNB_uespec_RX(). Signed-off-by: Robert Schmidt --- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 1 - openair1/SCHED_NR/phy_procedures_nr_gNB.c | 95 +++---------------- openair1/SIMULATION/NR_PHY/srssim.c | 2 - 3 files changed, 12 insertions(+), 86 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index d1431b6237..c2ec658036 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -212,7 +212,6 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, NR_gNB_SRS_t *srs, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], - c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], int16_t *snr_per_rb, uint16_t *timing_advance_offset, int16_t *timing_advance_offset_nsec); diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 59285dd884..8b37b3b8e8 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -706,13 +706,15 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, NR_gNB_SRS_t *srs, int *srs_est, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], - c16_t srs_estimated_channel_time[][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], int16_t *snr_per_rb, uint16_t *timing_advance_offset, int16_t *timing_advance_offset_nsec) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + + c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] + __attribute__((aligned(32))); c16_t srs_received_signal[nb_antennas_rx][ofdm_symbol_size * N_symb_SRS]; c16_t srs_received_noise[nb_antennas_rx][ofdm_symbol_size * N_symb_SRS]; c16_t srs_estimated_channel_time_shifted[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size]; @@ -1163,17 +1165,20 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N start_meas(&gNB->rx_srs_stats); - uint8_t N_symb_SRS = 1 << srs->srs_pdu.num_symbols; - uint8_t N_ap = 1 << srs->srs_pdu.num_ant_ports; - int16_t snr_per_rb[srs->srs_pdu.bwp_size]; - nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + UL_INFO->srs_ind.sfn = frame_rx; + UL_INFO->srs_ind.slot = slot_rx; + UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; + nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; + + const nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + uint8_t N_symb_SRS = 1 << srs_pdu->num_symbols; + uint8_t N_ap = 1 << srs_pdu->num_ant_ports; + int16_t snr_per_rb[srs_pdu->bwp_size]; uint16_t timing_advance_offset; int16_t timing_advance_offset_nsec[nb_antennas_rx]; int srs_est; c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); - c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] - __attribute__((aligned(32))); nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, frame_rx, @@ -1185,7 +1190,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N srs, &srs_est, srs_estimated_channel_freq, - srs_estimated_channel_time, snr_per_rb, &timing_advance_offset, timing_advance_offset_nsec); @@ -1194,12 +1198,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N srs_est = -1; } - UL_INFO->srs_ind.sfn = frame_rx; - UL_INFO->srs_ind.slot = slot_rx; - - // data model difficult to understand, nfapi do malloc for this pointer - UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; - nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; srs_indication->handle = srs_pdu->handle; srs_indication->rnti = srs_pdu->rnti; srs_indication->timing_advance_offset = srs_est >= 0 ? timing_advance_offset : 0xFFFF; @@ -1226,16 +1224,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N } srs_indication->report_type = srs_pdu->srs_parameters_v4.report_type[0]; -#ifdef SRS_IND_DEBUG - LOG_I(NR_PHY, "UL_INFO->srs_ind.sfn = %i\n", UL_INFO->srs_ind.sfn); - LOG_I(NR_PHY, "UL_INFO->srs_ind.slot = %i\n", UL_INFO->srs_ind.slot); - LOG_I(NR_PHY, "srs_indication->rnti = %04x\n", srs_indication->rnti); - LOG_I(NR_PHY, "srs_indication->timing_advance = %i\n", srs_indication->timing_advance_offset); - LOG_I(NR_PHY, "srs_indication->timing_advance_offset_nsec = %i\n", srs_indication->timing_advance_offset_nsec); - LOG_I(NR_PHY, "srs_indication->srs_usage = %i\n", srs_indication->srs_usage); - LOG_I(NR_PHY, "srs_indication->report_type = %i\n", srs_indication->report_type); -#endif - nfapi_srs_report_tlv_t *report_tlv = &srs_indication->report_tlv; report_tlv->tag = 0; report_tlv->length = 0; @@ -1255,26 +1243,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nr_srs_bf_report.num_reported_symbols); fill_srs_reported_symbol(&nr_srs_bf_report.reported_symbol_list[0], srs_pdu, snr_per_rb, srs_est); -#ifdef SRS_IND_DEBUG - LOG_I(NR_PHY, "nr_srs_bf_report.prg_size = %i\n", nr_srs_bf_report.prg_size); - LOG_I(NR_PHY, "nr_srs_bf_report.num_symbols = %i\n", nr_srs_bf_report.num_symbols); - LOG_I(NR_PHY, - "nr_srs_bf_report.wide_band_snr = %i (%i dB)\n", - nr_srs_bf_report.wide_band_snr, - (nr_srs_bf_report.wide_band_snr >> 1) - 64); - LOG_I(NR_PHY, "nr_srs_bf_report.num_reported_symbols = %i\n", nr_srs_bf_report.num_reported_symbols); - LOG_I(NR_PHY, - "nr_srs_bf_report.reported_symbol_list[0].num_prgs = %i\n", - nr_srs_bf_report.reported_symbol_list[0].num_prgs); - for (int prg_idx = 0; prg_idx < nr_srs_bf_report.reported_symbol_list[0].num_prgs; prg_idx++) { - LOG_I(NR_PHY, - "nr_srs_beamforming_report.reported_symbol_list[0].prg_list[%3i].rb_snr = %i (%i dB)\n", - prg_idx, - nr_srs_bf_report.reported_symbol_list[0].prg_list[prg_idx].rb_snr, - (nr_srs_bf_report.reported_symbol_list[0].prg_list[prg_idx].rb_snr >> 1) - 64); - } -#endif - report_tlv->length = pack_nr_srs_beamforming_report(&nr_srs_bf_report, report_tlv->value, sizeof(report_tlv->value)); stop_meas(&gNB->srs_beam_report_stats); break; @@ -1294,37 +1262,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N frame_parms, srs_estimated_channel_freq); -#ifdef SRS_IND_DEBUG - LOG_I(NR_PHY, - "nr_srs_channel_iq_matrix.normalized_iq_representation = %i\n", - nr_srs_channel_iq_matrix.normalized_iq_representation); - LOG_I(NR_PHY, - "nr_srs_channel_iq_matrix.num_gnb_antenna_elements = %i\n", - nr_srs_channel_iq_matrix.num_gnb_antenna_elements); - LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.num_ue_srs_ports = %i\n", nr_srs_channel_iq_matrix.num_ue_srs_ports); - LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.prg_size = %i\n", nr_srs_channel_iq_matrix.prg_size); - LOG_I(NR_PHY, "nr_srs_channel_iq_matrix.num_prgs = %i\n", nr_srs_channel_iq_matrix.num_prgs); - c16_t *channel_matrix16 = (c16_t *)nr_srs_channel_iq_matrix.channel_matrix; - c8_t *channel_matrix8 = (c8_t *)nr_srs_channel_iq_matrix.channel_matrix; - for (int uI = 0; uI < nr_srs_channel_iq_matrix.num_ue_srs_ports; uI++) { - for (int gI = 0; gI < nr_srs_channel_iq_matrix.num_gnb_antenna_elements; gI++) { - for (int pI = 0; pI < nr_srs_channel_iq_matrix.num_prgs; pI++) { - uint16_t index = uI * nr_srs_channel_iq_matrix.num_gnb_antenna_elements * nr_srs_channel_iq_matrix.num_prgs - + gI * nr_srs_channel_iq_matrix.num_prgs + pI; - LOG_I( - NR_PHY, - "(uI %i, gI %i, pI %i) channel_matrix --> real %i, imag %i\n", - uI, - gI, - pI, - nr_srs_channel_iq_matrix.normalized_iq_representation == 0 ? channel_matrix8[index].r : channel_matrix16[index].r, - nr_srs_channel_iq_matrix.normalized_iq_representation == 0 ? channel_matrix8[index].i - : channel_matrix16[index].i); - } - } - } -#endif - report_tlv->length = pack_nr_srs_normalized_channel_iq_matrix(&nr_srs_channel_iq_matrix, report_tlv->value, sizeof(report_tlv->value)); stop_meas(&gNB->srs_iq_matrix_stats); @@ -1341,14 +1278,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N } stop_meas(&gNB->srs_report_tlv_stats); -#ifdef SRS_IND_DEBUG - LOG_I(NR_PHY, "report_tlv->tag = %i\n", report_tlv->tag); - LOG_I(NR_PHY, "report_tlv->length = %i\n", report_tlv->length); - char *value = (char *)report_tlv->value; - for (int b = 0; b < report_tlv->length; b++) { - LOG_I(NR_PHY, "value[%i] = 0x%02x\n", b, value[b] & 0xFF); - } -#endif srs->active = false; stop_meas(&gNB->rx_srs_stats); } diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index fc6186c0f1..6aa3ca5e81 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -567,7 +567,6 @@ int main(int argc, char *argv[]) int16_t timing_advance_offset_nsec[n_rx]; int srs_est; c16_t srs_estimated_channel_freq[n_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); - c16_t srs_estimated_channel_time[n_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] __attribute__((aligned(32))); nr_srs_rx_procedures(gNB, frame, @@ -579,7 +578,6 @@ int main(int argc, char *argv[]) srs, &srs_est, srs_estimated_channel_freq, - srs_estimated_channel_time, snr_per_rb, &timing_advance_offset, timing_advance_offset_nsec); From 2bb1f36a2cff1c27d575ca20a121ddc453992cdc Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 29 Mar 2026 15:03:38 +0200 Subject: [PATCH 62/72] Remove SNR parameter The structure in which was snr will be a "SRS job" in a follow-up commit, so the snr parameter cannot be stored in there (and it is also not necessary to do so, as it can be local to the function handling nr_srs_rx_procedures()). Correctly read SRS SNR through FAPI message in nr_ulsim. Switch to SRS BEAMMANAGEMENT as only this message returns the actual SNR (unlike CODEBOOK). Add asserts to check what is returned is valid data in the sense of the test. Signed-off-by: Robert Schmidt --- openair1/PHY/NR_TRANSPORT/nr_transport_proto.h | 5 +++-- openair1/PHY/NR_TRANSPORT/srs_rx.c | 2 +- openair1/PHY/defs_gNB.h | 2 -- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 13 ++++++++----- openair1/SIMULATION/NR_PHY/srssim.c | 4 +++- openair1/SIMULATION/NR_PHY/ulsim.c | 11 +++++++++-- 6 files changed, 24 insertions(+), 13 deletions(-) diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index c2ec658036..54acd23351 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -197,7 +197,7 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, int nr_get_srs_signal(PHY_VARS_gNB *gNB, c16_t **rxdataF, slot_t slot, - nfapi_nr_srs_pdu_t *srs_pdu, + const nfapi_nr_srs_pdu_t *srs_pdu, nr_srs_info_t *nr_srs_info, c16_t srs_received_signal[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)], c16_t srs_received_noise[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)]); @@ -209,8 +209,9 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_ap, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, - NR_gNB_SRS_t *srs, + const NR_gNB_SRS_t *srs, int *srs_est, + int8_t *snr, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], int16_t *snr_per_rb, uint16_t *timing_advance_offset, diff --git a/openair1/PHY/NR_TRANSPORT/srs_rx.c b/openair1/PHY/NR_TRANSPORT/srs_rx.c index fb6f5f648e..96695909a9 100644 --- a/openair1/PHY/NR_TRANSPORT/srs_rx.c +++ b/openair1/PHY/NR_TRANSPORT/srs_rx.c @@ -59,7 +59,7 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, frame_t frame, slot_t slot, nfapi_nr_srs_pdu int nr_get_srs_signal(PHY_VARS_gNB *gNB, c16_t **rxdataF, slot_t slot, - nfapi_nr_srs_pdu_t *srs_pdu, + const nfapi_nr_srs_pdu_t *srs_pdu, nr_srs_info_t *nr_srs_info, c16_t srs_received_signal[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)], c16_t srs_received_noise[][gNB->frame_parms.ofdm_symbol_size * (1 << srs_pdu->num_symbols)]) diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index ef357508d7..20e820a298 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -215,8 +215,6 @@ typedef struct { uint32_t frame; /// Slot where current SRS pdu was received uint32_t slot; - /// Measured SNR - int8_t snr; /// ULSCH PDU nfapi_nr_srs_pdu_t srs_pdu; } NR_gNB_SRS_t; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 8b37b3b8e8..17eed001cb 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -703,15 +703,16 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_ap, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, - NR_gNB_SRS_t *srs, + const NR_gNB_SRS_t *srs, int *srs_est, + int8_t *snr, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], int16_t *snr_per_rb, uint16_t *timing_advance_offset, int16_t *timing_advance_offset_nsec) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; - nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + const nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; c16_t srs_estimated_channel_time[nb_antennas_rx][N_ap][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size] __attribute__((aligned(32))); @@ -836,7 +837,7 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, } noise_power_avg /= nb_antennas_rx; - gNB->srs->snr = dB_fixed(signal_power_avg) - dB_fixed(max(noise_power_avg, 1)); + *snr = dB_fixed(signal_power_avg) - dB_fixed(max(noise_power_avg, 1)); const uint16_t m_SRS_b = get_m_srs(srs_pdu->config_index, srs_pdu->bandwidth_index); for (int rb = 0; rb < m_SRS_b; rb++) { @@ -1180,6 +1181,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); + int8_t snr; nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, frame_rx, slot_rx, @@ -1189,12 +1191,13 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N ofdm_symbol_size, srs, &srs_est, + &snr, srs_estimated_channel_freq, snr_per_rb, &timing_advance_offset, timing_advance_offset_nsec); - if ((gNB->srs->snr * 10) < gNB->srs_thres) { + if ((snr * 10) < gNB->srs_thres) { srs_est = -1; } @@ -1236,7 +1239,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nr_srs_bf_report.prg_size = srs_pdu->beamforming.prg_size; nr_srs_bf_report.num_symbols = N_symb_SRS; nr_srs_bf_report.wide_band_snr = - srs_est >= 0 ? (gNB->srs->snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid + srs_est >= 0 ? (snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid nr_srs_bf_report.num_reported_symbols = N_symb_SRS; AssertFatal(nr_srs_bf_report.num_reported_symbols == 1, "nr_srs_bf_report.num_reported_symbols %i not handled yet!\n", diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index 6aa3ca5e81..144bce1ab9 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -568,6 +568,7 @@ int main(int argc, char *argv[]) int srs_est; c16_t srs_estimated_channel_freq[n_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); + int8_t snr; nr_srs_rx_procedures(gNB, frame, slot, @@ -577,12 +578,13 @@ int main(int argc, char *argv[]) ofdm_symbol_size, srs, &srs_est, + &snr, srs_estimated_channel_freq, snr_per_rb, &timing_advance_offset, timing_advance_offset_nsec); - sum_srs_snr += pow(10, (double)gNB->srs->snr / 10.0); + sum_srs_snr += pow(10, (double)snr / 10.0); int16_t delay_ns = delay * 1e9 / (fp->samples_per_frame * 100); for (int ant_idx = 0; ant_idx < n_rx; ant_idx++) { diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index 011b7ee680..7e74f26f56 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -1308,7 +1308,7 @@ int main(int argc, char *argv[]) srs_pdu->resource_type = NR_SRS_Resource__resourceType_PR_periodic; srs_pdu->t_srs = 1; srs_pdu->srs_parameters_v4.srs_bandwidth_size = m_SRS[srs_pdu->config_index]; - srs_pdu->srs_parameters_v4.usage = 1 << NR_SRS_ResourceSet__usage_codebook; + srs_pdu->srs_parameters_v4.usage = 1 << NFAPI_NR_SRS_BEAMMANAGEMENT; // to get SNR srs_pdu->srs_parameters_v4.report_type[0] = 1; srs_pdu->srs_parameters_v4.iq_representation = 1; srs_pdu->srs_parameters_v4.prg_size = 1; @@ -1748,7 +1748,14 @@ int main(int argc, char *argv[]) delay_pusch_est_count++; if (do_SRS == 1) { - sum_srs_snr += gNB->srs->snr; + DevAssert(UL_INFO.srs_ind.number_of_pdus == 1); // there must be SRS indication + const nfapi_nr_srs_indication_pdu_t *srs_ind = &UL_INFO.srs_ind.pdu_list[0]; + DevAssert(srs_ind->srs_usage == NFAPI_NR_SRS_BEAMMANAGEMENT); + nfapi_nr_srs_beamforming_report_t bf_rep; + unpack_nr_srs_beamforming_report((void *)srs_ind->report_tlv.value, srs_ind->report_tlv.length, &bf_rep, sizeof(bf_rep)); + DevAssert(bf_rep.wide_band_snr != 0xff); + int8_t snr = (bf_rep.wide_band_snr >> 1) - 64; + sum_srs_snr += snr; srs_snr_count++; } } // trial loop From a5696562da9b616141ced971db05bf528270f0d6 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sun, 29 Mar 2026 23:37:12 +0200 Subject: [PATCH 63/72] Refactor SRS processing into its own function Signed-off-by: Robert Schmidt --- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 228 +++++++++++----------- 1 file changed, 118 insertions(+), 110 deletions(-) diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 17eed001cb..3c9f3fcf02 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -875,6 +875,122 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, return nr_srs_info; } +static void handle_srs(fsn_t now, PHY_VARS_gNB *gNB, const NR_gNB_SRS_t *srs, nfapi_nr_srs_indication_pdu_t *srs_indication) +{ + const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; + const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; + const uint16_t ofdm_symbol_size = frame_parms->ofdm_symbol_size; + const nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + + uint8_t N_symb_SRS = 1 << srs_pdu->num_symbols; + uint8_t N_ap = 1 << srs_pdu->num_ant_ports; + int16_t snr_per_rb[srs_pdu->bwp_size]; + uint16_t timing_advance_offset; + int16_t timing_advance_offset_nsec[nb_antennas_rx]; + int srs_est; + + c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); + + int8_t snr; + nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, + now.f, + now.s, + nb_antennas_rx, + N_ap, + N_symb_SRS, + ofdm_symbol_size, + srs, + &srs_est, + &snr, + srs_estimated_channel_freq, + snr_per_rb, + &timing_advance_offset, + timing_advance_offset_nsec); + + if ((snr * 10) < gNB->srs_thres) { + srs_est = -1; + } + + srs_indication->handle = srs_pdu->handle; + srs_indication->rnti = srs_pdu->rnti; + srs_indication->timing_advance_offset = srs_est >= 0 ? timing_advance_offset : 0xFFFF; + // TODO: currently we fill timing_advance_offset_nsec for antenna 0. Need to extend it for other antennas + srs_indication->timing_advance_offset_nsec = srs_est >= 0 ? timing_advance_offset_nsec[0] : 0x8000; + switch (srs_pdu->srs_parameters_v4.usage) { + case 0: + LOG_W(NR_PHY, "SRS report was not requested by MAC\n"); + return; + case 1 << NFAPI_NR_SRS_BEAMMANAGEMENT: + srs_indication->srs_usage = NFAPI_NR_SRS_BEAMMANAGEMENT; + break; + case 1 << NFAPI_NR_SRS_CODEBOOK: + srs_indication->srs_usage = NFAPI_NR_SRS_CODEBOOK; + break; + case 1 << NFAPI_NR_SRS_NONCODEBOOK: + srs_indication->srs_usage = NFAPI_NR_SRS_NONCODEBOOK; + break; + case 1 << NFAPI_NR_SRS_ANTENNASWITCH: + srs_indication->srs_usage = NFAPI_NR_SRS_ANTENNASWITCH; + break; + default: + LOG_E(NR_PHY, "Invalid srs_pdu->srs_parameters_v4.usage %i\n", srs_pdu->srs_parameters_v4.usage); + } + srs_indication->report_type = srs_pdu->srs_parameters_v4.report_type[0]; + + nfapi_srs_report_tlv_t *report_tlv = &srs_indication->report_tlv; + report_tlv->tag = 0; + report_tlv->length = 0; + + start_meas(&gNB->srs_report_tlv_stats); + switch (srs_indication->srs_usage) { + case NFAPI_NR_SRS_BEAMMANAGEMENT: { + start_meas(&gNB->srs_beam_report_stats); + nfapi_nr_srs_beamforming_report_t nr_srs_bf_report; + nr_srs_bf_report.prg_size = srs_pdu->beamforming.prg_size; + nr_srs_bf_report.num_symbols = N_symb_SRS; + nr_srs_bf_report.wide_band_snr = srs_est >= 0 ? (snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid + nr_srs_bf_report.num_reported_symbols = N_symb_SRS; + AssertFatal(nr_srs_bf_report.num_reported_symbols == 1, + "nr_srs_bf_report.num_reported_symbols %i not handled yet!\n", + nr_srs_bf_report.num_reported_symbols); + fill_srs_reported_symbol(&nr_srs_bf_report.reported_symbol_list[0], srs_pdu, snr_per_rb, srs_est); + + report_tlv->length = pack_nr_srs_beamforming_report(&nr_srs_bf_report, report_tlv->value, sizeof(report_tlv->value)); + stop_meas(&gNB->srs_beam_report_stats); + break; + } + + case NFAPI_NR_SRS_CODEBOOK: { + start_meas(&gNB->srs_iq_matrix_stats); + nfapi_nr_srs_normalized_channel_iq_matrix_t nr_srs_channel_iq_matrix; + fill_srs_channel_matrix(&nr_srs_channel_iq_matrix, + srs_pdu, + &srs_info, + srs_pdu->srs_parameters_v4.iq_representation, + nb_antennas_rx, + srs_pdu->srs_parameters_v4.num_total_ue_antennas, + srs_pdu->srs_parameters_v4.prg_size, + srs_pdu->srs_parameters_v4.srs_bandwidth_size / srs_pdu->srs_parameters_v4.prg_size, + frame_parms, + srs_estimated_channel_freq); + + report_tlv->length = + pack_nr_srs_normalized_channel_iq_matrix(&nr_srs_channel_iq_matrix, report_tlv->value, sizeof(report_tlv->value)); + stop_meas(&gNB->srs_iq_matrix_stats); + break; + } + + case NFAPI_NR_SRS_NONCODEBOOK: + case NFAPI_NR_SRS_ANTENNASWITCH: + LOG_W(NR_PHY, "PHY procedures for this SRS usage are not implemented yet!\n"); + break; + + default: + AssertFatal(1 == 0, "Invalid SRS usage\n"); + } + stop_meas(&gNB->srs_report_tlv_stats); +} + static void handle_pucch(PHY_VARS_gNB *gNB, c16_t **rxdataF, const NR_gNB_PUCCH_job_t *pucch, nfapi_nr_uci_t *uci) { const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch->pucch_pdu; @@ -1074,7 +1190,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; const uint16_t ofdm_symbol_size = frame_parms->ofdm_symbol_size; - const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; fsn_t now = {frame_rx, slot_rx, frame_parms->numerology_index}; @@ -1171,115 +1286,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; - const nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; - uint8_t N_symb_SRS = 1 << srs_pdu->num_symbols; - uint8_t N_ap = 1 << srs_pdu->num_ant_ports; - int16_t snr_per_rb[srs_pdu->bwp_size]; - uint16_t timing_advance_offset; - int16_t timing_advance_offset_nsec[nb_antennas_rx]; - int srs_est; - - c16_t srs_estimated_channel_freq[nb_antennas_rx][N_ap][ofdm_symbol_size * N_symb_SRS] __attribute__((aligned(32))); - - int8_t snr; - nr_srs_info_t srs_info = nr_srs_rx_procedures(gNB, - frame_rx, - slot_rx, - nb_antennas_rx, - N_ap, - N_symb_SRS, - ofdm_symbol_size, - srs, - &srs_est, - &snr, - srs_estimated_channel_freq, - snr_per_rb, - &timing_advance_offset, - timing_advance_offset_nsec); - - if ((snr * 10) < gNB->srs_thres) { - srs_est = -1; - } - - srs_indication->handle = srs_pdu->handle; - srs_indication->rnti = srs_pdu->rnti; - srs_indication->timing_advance_offset = srs_est >= 0 ? timing_advance_offset : 0xFFFF; - // TODO: currently we fill timing_advance_offset_nsec for antenna 0. Need to extend it for other antennas - srs_indication->timing_advance_offset_nsec = srs_est >= 0 ? timing_advance_offset_nsec[0] : 0x8000; - switch (srs_pdu->srs_parameters_v4.usage) { - case 0: - LOG_W(NR_PHY, "SRS report was not requested by MAC\n"); - return 0; - case 1 << NFAPI_NR_SRS_BEAMMANAGEMENT: - srs_indication->srs_usage = NFAPI_NR_SRS_BEAMMANAGEMENT; - break; - case 1 << NFAPI_NR_SRS_CODEBOOK: - srs_indication->srs_usage = NFAPI_NR_SRS_CODEBOOK; - break; - case 1 << NFAPI_NR_SRS_NONCODEBOOK: - srs_indication->srs_usage = NFAPI_NR_SRS_NONCODEBOOK; - break; - case 1 << NFAPI_NR_SRS_ANTENNASWITCH: - srs_indication->srs_usage = NFAPI_NR_SRS_ANTENNASWITCH; - break; - default: - LOG_E(NR_PHY, "Invalid srs_pdu->srs_parameters_v4.usage %i\n", srs_pdu->srs_parameters_v4.usage); - } - srs_indication->report_type = srs_pdu->srs_parameters_v4.report_type[0]; - - nfapi_srs_report_tlv_t *report_tlv = &srs_indication->report_tlv; - report_tlv->tag = 0; - report_tlv->length = 0; - - start_meas(&gNB->srs_report_tlv_stats); - switch (srs_indication->srs_usage) { - case NFAPI_NR_SRS_BEAMMANAGEMENT: { - start_meas(&gNB->srs_beam_report_stats); - nfapi_nr_srs_beamforming_report_t nr_srs_bf_report; - nr_srs_bf_report.prg_size = srs_pdu->beamforming.prg_size; - nr_srs_bf_report.num_symbols = N_symb_SRS; - nr_srs_bf_report.wide_band_snr = - srs_est >= 0 ? (snr + 64) << 1 : 0xFF; // 0xFF will be set if this field is invalid - nr_srs_bf_report.num_reported_symbols = N_symb_SRS; - AssertFatal(nr_srs_bf_report.num_reported_symbols == 1, - "nr_srs_bf_report.num_reported_symbols %i not handled yet!\n", - nr_srs_bf_report.num_reported_symbols); - fill_srs_reported_symbol(&nr_srs_bf_report.reported_symbol_list[0], srs_pdu, snr_per_rb, srs_est); - - report_tlv->length = pack_nr_srs_beamforming_report(&nr_srs_bf_report, report_tlv->value, sizeof(report_tlv->value)); - stop_meas(&gNB->srs_beam_report_stats); - break; - } - - case NFAPI_NR_SRS_CODEBOOK: { - start_meas(&gNB->srs_iq_matrix_stats); - nfapi_nr_srs_normalized_channel_iq_matrix_t nr_srs_channel_iq_matrix; - fill_srs_channel_matrix(&nr_srs_channel_iq_matrix, - srs_pdu, - &srs_info, - srs_pdu->srs_parameters_v4.iq_representation, - nb_antennas_rx, - srs_pdu->srs_parameters_v4.num_total_ue_antennas, - srs_pdu->srs_parameters_v4.prg_size, - srs_pdu->srs_parameters_v4.srs_bandwidth_size / srs_pdu->srs_parameters_v4.prg_size, - frame_parms, - srs_estimated_channel_freq); - - report_tlv->length = - pack_nr_srs_normalized_channel_iq_matrix(&nr_srs_channel_iq_matrix, report_tlv->value, sizeof(report_tlv->value)); - stop_meas(&gNB->srs_iq_matrix_stats); - break; - } - - case NFAPI_NR_SRS_NONCODEBOOK: - case NFAPI_NR_SRS_ANTENNASWITCH: - LOG_W(NR_PHY, "PHY procedures for this SRS usage are not implemented yet!\n"); - break; - - default: - AssertFatal(1 == 0, "Invalid SRS usage\n"); - } - stop_meas(&gNB->srs_report_tlv_stats); + handle_srs(now, gNB, srs, srs_indication); srs->active = false; stop_meas(&gNB->rx_srs_stats); @@ -1288,6 +1295,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N stop_meas(&gNB->phy_proc_rx); if (n_pucch > 0 || num_pusch > 0) { + UNUSED(ofdm_symbol_size); // only used if T activated T(T_GNB_PHY_PUCCH_PUSCH_IQ, T_INT(frame_rx), T_INT(slot_rx), From f5989abd0dd0f40b51b3c1af22592c6f522626d4 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Wed, 15 Apr 2026 11:12:49 +0200 Subject: [PATCH 64/72] Use spsc_q for SRS processing Use the spsc queue to pass jobs from TX thread (scheduler) to RX thread (handling those jobs), reducing the amount spent searching for SRS jobs, clarifying the design, and making it thread-safe. Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init.c | 8 +-- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 2 +- openair1/PHY/NR_TRANSPORT/srs_rx.c | 38 +++++------- openair1/PHY/defs_gNB.h | 6 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 61 +++++++++++-------- openair1/SIMULATION/NR_PHY/srssim.c | 15 ++--- 6 files changed, 62 insertions(+), 68 deletions(-) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 7d56f8b410..2246fcdde1 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -419,15 +419,13 @@ void init_nr_transport(PHY_VARS_gNB *gNB) buffer_ul_slots = (nb_ul_slots_period < slot_ahead) ? nb_ul_slots_period : slot_ahead; gNB->max_nb_pusch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; - gNB->max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot int max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1; gNB->pucch_queue = spsc_q_alloc(max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t)); gNB->pusch_queue = spsc_q_alloc(gNB->max_nb_pusch, sizeof(NR_gNB_PUSCH_job_t)); - gNB->srs = (NR_gNB_SRS_t *)malloc16(gNB->max_nb_srs * sizeof(NR_gNB_SRS_t)); - for (int i = 0; i < gNB->max_nb_srs; i++) - gNB->srs[i].active = 0; + int max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot + gNB->srs_queue = spsc_q_alloc(max_nb_srs, sizeof(NR_gNB_SRS_job_t)); gNB->ulsch = (NR_gNB_ULSCH_t *)malloc16(gNB->max_nb_pusch * sizeof(NR_gNB_ULSCH_t)); for (int i = 0; i < gNB->max_nb_pusch; i++) { @@ -446,7 +444,7 @@ void reset_nr_transport(PHY_VARS_gNB *gNB) spsc_q_free(&gNB->pucch_queue); spsc_q_free(&gNB->pusch_queue); - free(gNB->srs); + spsc_q_free(&gNB->srs_queue); for (int i = 0; i < gNB->max_nb_pusch; i++) free_gNB_ulsch(&gNB->ulsch[i], fp->N_RB_UL); diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 54acd23351..32efa979cc 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -209,7 +209,7 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_ap, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, - const NR_gNB_SRS_t *srs, + const NR_gNB_SRS_job_t *srs, int *srs_est, int8_t *snr, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], diff --git a/openair1/PHY/NR_TRANSPORT/srs_rx.c b/openair1/PHY/NR_TRANSPORT/srs_rx.c index 96695909a9..b0436cb2d7 100644 --- a/openair1/PHY/NR_TRANSPORT/srs_rx.c +++ b/openair1/PHY/NR_TRANSPORT/srs_rx.c @@ -29,31 +29,21 @@ void nr_fill_srs(PHY_VARS_gNB *gNB, frame_t frame, slot_t slot, nfapi_nr_srs_pdu_t *srs_pdu) { - bool found = false; - for (int i = 0; i < gNB->max_nb_srs; i++) { - NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (srs->active == false) { - found = true; - srs->frame = frame; - srs->slot = slot; - srs->active = true; - srs->beam_nb = 0; - if (gNB->common_vars.beam_id) { - const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; - int bitmap = SL_to_bitmap(l0, 1 << srs_pdu->num_symbols); - int fapi_beam_idx = srs_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; - srs->beam_nb = beam_index_allocation(gNB->enable_analog_das, - fapi_beam_idx, - &gNB->common_vars, - slot, - gNB->frame_parms.symbols_per_slot, - bitmap); - } - memcpy((void *)&srs->srs_pdu, (void *)srs_pdu, sizeof(nfapi_nr_srs_pdu_t)); - break; - } + NR_gNB_SRS_job_t srs = {.frame = frame, .slot = slot, .srs_pdu = *srs_pdu}; + if (gNB->common_vars.beam_id) { + const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; + int bitmap = SL_to_bitmap(l0, 1 << srs_pdu->num_symbols); + int fapi_beam_idx = srs_pdu->beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx; + srs.beam_nb = beam_index_allocation(gNB->enable_analog_das, + fapi_beam_idx, + &gNB->common_vars, + slot, + gNB->frame_parms.symbols_per_slot, + bitmap); } - AssertFatal(found, "SRS list is full\n"); + bool found = spsc_q_put(&gNB->srs_queue, &srs, sizeof(srs)); + if (!found) + LOG_W(NR_PHY, "SRS list is full: dropping SRS UE %04x\n", srs_pdu->rnti); } int nr_get_srs_signal(PHY_VARS_gNB *gNB, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 20e820a298..479a214418 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -208,7 +208,6 @@ typedef struct { } NR_gNB_PUCCH_job_t; typedef struct { - bool active; // identifier for concurrent beams int beam_nb; /// Frame where current SRS pdu was received @@ -217,7 +216,7 @@ typedef struct { uint32_t slot; /// ULSCH PDU nfapi_nr_srs_pdu_t srs_pdu; -} NR_gNB_SRS_t; +} NR_gNB_SRS_job_t; typedef struct { /// \brief Pointers (dynamic) to the received data in the frequency domain. @@ -375,7 +374,6 @@ typedef struct PHY_VARS_gNB_s { nfapi_nr_ul_tti_request_t UL_tti_req; - int max_nb_srs; int max_nb_pdsch; int max_nb_pusch; @@ -387,7 +385,7 @@ typedef struct PHY_VARS_gNB_s { NR_gNB_PUSCH *pusch_vars; spsc_q_t pucch_queue; spsc_q_t pusch_queue; - NR_gNB_SRS_t *srs; + spsc_q_t srs_queue; NR_gNB_ULSCH_t *ulsch; NR_gNB_PHY_STATS_t phy_stats[MAX_MOBILES_PER_GNB]; t_nrPolar_params **polarParams; diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 3c9f3fcf02..f9a1100724 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -536,7 +536,9 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, const NR_gNB_PUCCH_job_t *pucch, int n_pucch, const NR_gNB_PUSCH_job_t *pusch, - int n_pusch) + int n_pusch, + const NR_gNB_SRS_job_t *srs, + int n_srs) { for (int symbol = 0; symbol < 14; symbol++) { for (int m = 0; m < 9; m++) { @@ -581,11 +583,8 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, } } - for (int i = 0; i < gNB->max_nb_srs; i++) { - NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (!(srs && srs->active && srs->frame == now.f && srs->slot == now.s)) - continue; - nfapi_nr_srs_pdu_t *srs_pdu = &srs->srs_pdu; + for (int i = 0; i < n_srs; i++) { + const nfapi_nr_srs_pdu_t *srs_pdu = &srs[i].srs_pdu; const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; for (int symbol = 0; symbol < (1 << srs_pdu->num_symbols); symbol++) { for (int rb = srs_pdu->bwp_start; rb < (srs_pdu->bwp_start + srs_pdu->bwp_size); rb++) { @@ -703,7 +702,7 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, uint8_t N_ap, uint8_t N_symb_SRS, uint16_t ofdm_symbol_size, - const NR_gNB_SRS_t *srs, + const NR_gNB_SRS_job_t *srs, int *srs_est, int8_t *snr, c16_t srs_estimated_channel_freq[][N_ap][ofdm_symbol_size * N_symb_SRS], @@ -875,7 +874,7 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, return nr_srs_info; } -static void handle_srs(fsn_t now, PHY_VARS_gNB *gNB, const NR_gNB_SRS_t *srs, nfapi_nr_srs_indication_pdu_t *srs_indication) +static void handle_srs(fsn_t now, PHY_VARS_gNB *gNB, const NR_gNB_SRS_job_t *srs, nfapi_nr_srs_indication_pdu_t *srs_indication) { const NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; const uint8_t nb_antennas_rx = frame_parms->nb_antennas_rx; @@ -1137,6 +1136,25 @@ static bool get_current_pusch(const void *data, void *user) return fsn_equal(t, *now); } +static bool drop_old_srs(const void *data, void *user) +{ + const NR_gNB_SRS_job_t *srs = data; + const fsn_t *now = user; + const fsn_t t = {srs->frame, srs->slot, now->mu}; + bool drop = fsn_in_the_past(t, *now); + if (drop) + LOG_E(NR_PHY, "%4d.%2d SRS job for UE %04x is in the past (%4d.%2d)\n", now->f, now->s, srs->srs_pdu.rnti, t.f, t.s); + return drop; +} + +static bool get_current_srs(const void *data, void *user) +{ + const NR_gNB_SRS_job_t *srs = data; + const fsn_t *now = user; + const fsn_t t = {srs->frame, srs->slot, now->mu}; + return fsn_equal(t, *now); +} + static int find_nr_ulsch_idx(PHY_VARS_gNB *gNB, uint16_t rnti, int pid) { AssertFatal(gNB != NULL, "gNB is null\n"); @@ -1201,6 +1219,10 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N NR_gNB_PUSCH_job_t pusch[MAX_UL_PDUS_PER_SLOT]; int n_pusch_jobs = spsc_q_get_while(&gNB->pusch_queue, get_current_pusch, &now, pusch, sizeof(*pusch), MAX_UL_PDUS_PER_SLOT); + spsc_q_drop_while(&gNB->srs_queue, drop_old_srs, &now); + NR_gNB_SRS_job_t srs[MAX_NUM_NR_SRS_PDUS]; + int n_srs = spsc_q_get_while(&gNB->srs_queue, get_current_srs, &now, srs, sizeof(*srs), MAX_NUM_NR_SRS_PDUS); + LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB @@ -1208,7 +1230,7 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL; if (frame_parms->frame_type == TDD) slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; - fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs); + fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs, srs, n_srs); int first_symb = 0, num_symb = 0; if (frame_parms->frame_type == TDD) @@ -1273,22 +1295,13 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N if (gNB->max_nb_pusch == 1) stop_meas(&gNB->ulsch_decoding_stats); - for (int i = 0; i < gNB->max_nb_srs; i++) { - NR_gNB_SRS_t *srs = &gNB->srs[i]; - if (!(srs && srs->active && srs->frame == frame_rx && srs->slot == slot_rx)) - continue; - LOG_D(NR_PHY, "(%d.%d) gNB is waiting for SRS, id = %i\n", frame_rx, slot_rx, i); - + UL_INFO->srs_ind.sfn = frame_rx; + UL_INFO->srs_ind.slot = slot_rx; + UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; + UL_INFO->srs_ind.number_of_pdus = n_srs; + for (int i = 0; i < n_srs; ++i) { start_meas(&gNB->rx_srs_stats); - - UL_INFO->srs_ind.sfn = frame_rx; - UL_INFO->srs_ind.slot = slot_rx; - UL_INFO->srs_ind.pdu_list = UL_INFO->srs_pdu_list; - nfapi_nr_srs_indication_pdu_t *srs_indication = UL_INFO->srs_pdu_list + UL_INFO->srs_ind.number_of_pdus++; - - handle_srs(now, gNB, srs, srs_indication); - - srs->active = false; + handle_srs(now, gNB, &srs[i], &UL_INFO->srs_ind.pdu_list[i]); stop_meas(&gNB->rx_srs_stats); } diff --git a/openair1/SIMULATION/NR_PHY/srssim.c b/openair1/SIMULATION/NR_PHY/srssim.c index 144bce1ab9..0ab34dac47 100644 --- a/openair1/SIMULATION/NR_PHY/srssim.c +++ b/openair1/SIMULATION/NR_PHY/srssim.c @@ -435,11 +435,7 @@ int main(int argc, char *argv[]) .beamforming.num_prgs = m_SRS[srs_pdu.config_index], .beamforming.prg_size = 1}; - gNB->srs->srs_pdu = srs_pdu; - gNB->srs->active = true; - gNB->srs->beam_nb = 0; - gNB->srs->frame = frame; - gNB->srs->slot = slot; + NR_gNB_SRS_job_t srs_job = {.frame = frame, .slot = slot, .srs_pdu = srs_pdu}; // Configure SRS parameters at UE fapi_nr_ul_config_srs_pdu srs_config_pdu = {.rnti = srs_pdu.rnti, @@ -559,10 +555,9 @@ int main(int argc, char *argv[]) //----------- UE RX SRS procedures --------------------- start_meas(&gNB->rx_srs_stats); - NR_gNB_SRS_t *srs = &gNB->srs[0]; - uint8_t N_symb_SRS = 1 << srs->srs_pdu.num_symbols; - uint8_t N_ap = 1 << srs->srs_pdu.num_ant_ports; - int16_t snr_per_rb[srs->srs_pdu.bwp_size]; + uint8_t N_symb_SRS = 1 << srs_pdu.num_symbols; + uint8_t N_ap = 1 << srs_pdu.num_ant_ports; + int16_t snr_per_rb[srs_pdu.bwp_size]; uint16_t timing_advance_offset; int16_t timing_advance_offset_nsec[n_rx]; int srs_est; @@ -576,7 +571,7 @@ int main(int argc, char *argv[]) N_ap, N_symb_SRS, ofdm_symbol_size, - srs, + &srs_job, &srs_est, &snr, srs_estimated_channel_freq, From 39d3477d84c1b7feb1da69d1e7d17442fa70e39c Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Mon, 24 Nov 2025 17:56:08 +0100 Subject: [PATCH 65/72] TODO: reimplement NR_gNB_PHY_STATS_t lookup Signed-off-by: Robert Schmidt --- openair1/PHY/INIT/nr_init.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index 2246fcdde1..a007bbd6d7 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -50,6 +50,8 @@ int l1_north_init_gNB() NR_gNB_PHY_STATS_t *get_phy_stats(PHY_VARS_gNB *gNB, uint16_t rnti) { + // TODO reimplement with hashtable? also called from both UL/DL => not + // thread-safe NR_gNB_PHY_STATS_t *stats; int first_free = -1; for (int i = 0; i < MAX_MOBILES_PER_GNB; i++) { From 879e374d329ec350ce57f71fc793b810214b53ee Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 9 Apr 2026 17:47:26 +0200 Subject: [PATCH 66/72] Use bytes instead of bit packing for rb_mask_ul Clarify the code, the bitpacking might even potentially be slower. Signed-off-by: Robert Schmidt --- .../PHY/NR_ESTIMATION/nr_measurements_gNB.c | 6 +-- openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h | 2 +- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 52 +++++++------------ openair1/SIMULATION/NR_PHY/pucchsim.c | 11 ++-- 4 files changed, 27 insertions(+), 44 deletions(-) diff --git a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c index a901f8dba1..f9b4991cfd 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c +++ b/openair1/PHY/NR_ESTIMATION/nr_measurements_gNB.c @@ -123,12 +123,12 @@ void dump_nr_I0_stats(FILE *fd, PHY_VARS_gNB *gNB) fprintf(fd, "\nPRACH I0 = %d.%d dB\n", gNB->measurements.prach_I0 / 10, gNB->measurements.prach_I0 % 10); } -void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][9]) +void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][MAX_BWP_SIZE]) { NR_DL_FRAME_PARMS *frame_parms = &gNB->frame_parms; NR_gNB_COMMON *common_vars = &gNB->common_vars; PHY_MEASUREMENTS_gNB *measurements = &gNB->measurements; - int nb_symb[275]={0}; + int nb_symb[MAX_BWP_SIZE] = {0}; unsigned int tmp_n0_subband[frame_parms->nb_antennas_rx][frame_parms->N_RB_UL]; memset(tmp_n0_subband, 0, sizeof(tmp_n0_subband)); @@ -137,7 +137,7 @@ void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_sy for (int s = first_symb; s < first_symb + num_symb; s++) { int offset0 = ((slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot + s) * frame_parms->ofdm_symbol_size; for (int rb = 0; rb < frame_parms->N_RB_UL; rb++) { - if ((rb_mask_ul[s][rb >> 5] & (1U << (rb & 31))) == 0 && // check that rb was not used in this subframe + if (rb_mask_ul[s][rb] == 0 && // check that rb was not used in this subframe !(I0_SKIP_DC && rb == frame_parms->N_RB_UL >> 1)) { // skip middle PRB because of artificial noise possibly created by FFT int offset = offset0 + (frame_parms->first_carrier_offset + (rb*12))%frame_parms->ofdm_symbol_size; nb_symb[rb]++; diff --git a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h index 25b9c27414..34bf64cc51 100644 --- a/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h +++ b/openair1/PHY/NR_ESTIMATION/nr_ul_estimation.h @@ -40,7 +40,7 @@ int nr_pusch_channel_estimation(PHY_VARS_gNB *gNB, void dump_nr_I0_stats(FILE *fd,PHY_VARS_gNB *gNB); -void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][9]); +void gNB_I0_measurements(PHY_VARS_gNB *gNB, int slot, int first_symb, int num_symb, uint32_t rb_mask_ul[14][MAX_BWP_SIZE]); void nr_est_srs_timing_advance_offset(uint16_t ofdm_symbol_size, const c16_t srs_estimated_channel_time[][NR_SRS_IDFT_OVERSAMP_FACTOR * ofdm_symbol_size], diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index f9a1100724..686f88a5fa 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -531,8 +531,7 @@ static void nr_fill_indication(const PHY_VARS_gNB *gNB, // Function to fill UL RB mask to be used for N0 measurements static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, fsn_t now, - uint32_t rb_mask_ul[14][9], - nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf, + uint32_t rb_mask_ul[14][MAX_BWP_SIZE], const NR_gNB_PUCCH_job_t *pucch, int n_pucch, const NR_gNB_PUSCH_job_t *pusch, @@ -541,29 +540,20 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, int n_srs) { for (int symbol = 0; symbol < 14; symbol++) { - for (int m = 0; m < 9; m++) { - rb_mask_ul[symbol][m] = 0; - for (int i = 0; i < 32; i++) { - int prbpos = (m * 32) + i; - if (prbpos > gNB->frame_parms.N_RB_UL) - break; - rb_mask_ul[symbol][m] |= (gNB->ulprbbl[prbpos] > 0 ? 1U : 0) << i; - } + for (int prb = 0; prb < MAX_BWP_SIZE; prb++) { + rb_mask_ul[symbol][prb] = gNB->ulprbbl[prb] > 0; } } for (int i = 0; i < n_pucch; ++i) { const nfapi_nr_pucch_pdu_t *pucch_pdu = &pucch[i].pucch_pdu; const int start = pucch_pdu->start_symbol_index; - for (int symbol = start; symbol < (start + pucch_pdu->nr_of_symbols); symbol++) { - if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) { - for (int rb = 0; rb < pucch_pdu->prb_size; rb++) { - int rb2 = - rb + pucch_pdu->bwp_start - + ((symbol < start + (pucch_pdu->nr_of_symbols >> 1)) || (pucch_pdu->freq_hop_flag == 0) ? pucch_pdu->prb_start - : pucch_pdu->second_hop_prb); - rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31); - } + for (int symbol = start; symbol < start + pucch_pdu->nr_of_symbols; symbol++) { + bool first_hop = symbol < start + (pucch_pdu->nr_of_symbols >> 1); + int prb_first_second = first_hop || pucch_pdu->freq_hop_flag == 0 ? pucch_pdu->prb_start : pucch_pdu->second_hop_prb; + for (int rb = 0; rb < pucch_pdu->prb_size; rb++) { + int prb = rb + pucch_pdu->bwp_start + prb_first_second; + rb_mask_ul[symbol][prb] = 1; } } } @@ -573,12 +563,9 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, uint8_t symbol_start = pusch_pdu->start_symbol_index; uint8_t symbol_end = symbol_start + pusch_pdu->nr_of_symbols; for (int symbol = symbol_start; symbol < symbol_end; symbol++) { - if (gNB->frame_parms.frame_type == FDD || (gNB->frame_parms.frame_type == TDD && slot_conf[symbol].slot_config.value == 1)) { - LOG_D(PHY, "symbol %d Filling rb_mask_ul rb_size %d\n", symbol, pusch_pdu->rb_size); - for (int rb = 0; rb < pusch_pdu->rb_size; rb++) { - int rb2 = rb + pusch_pdu->rb_start + pusch_pdu->bwp_start; - rb_mask_ul[symbol][rb2 >> 5] |= 1U << (rb2 & 31); - } + for (int rb = 0; rb < pusch_pdu->rb_size; rb++) { + int prb = rb + pusch_pdu->rb_start + pusch_pdu->bwp_start; + rb_mask_ul[symbol][prb] = 1; } } } @@ -588,7 +575,7 @@ static void fill_ul_rb_mask(PHY_VARS_gNB *gNB, const uint8_t l0 = gNB->frame_parms.symbols_per_slot - 1 - srs_pdu->time_start_position; for (int symbol = 0; symbol < (1 << srs_pdu->num_symbols); symbol++) { for (int rb = srs_pdu->bwp_start; rb < (srs_pdu->bwp_start + srs_pdu->bwp_size); rb++) { - rb_mask_ul[l0 + symbol][rb >> 5] |= 1U << (rb & 31); + rb_mask_ul[l0 + symbol][rb] = 1; } } } @@ -1226,14 +1213,12 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N LOG_D(PHY,"phy_procedures_gNB_uespec_RX frame %d, slot %d\n",frame_rx,slot_rx); { // Mask of occupied RBs, per symbol and PRB - uint32_t rb_mask_ul[14][9]; - nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = NULL; - if (frame_parms->frame_type == TDD) - slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; - fill_ul_rb_mask(gNB, now, rb_mask_ul, slot_conf, pucch, n_pucch, pusch, n_pusch_jobs, srs, n_srs); + uint32_t rb_mask_ul[14][MAX_BWP_SIZE] = {0}; + fill_ul_rb_mask(gNB, now, rb_mask_ul, pucch, n_pucch, pusch, n_pusch_jobs, srs, n_srs); int first_symb = 0, num_symb = 0; - if (frame_parms->frame_type == TDD) + if (frame_parms->frame_type == TDD) { + const nfapi_nr_max_num_of_symbol_per_slot_t *slot_conf = gNB->gNB_config.tdd_table.max_tdd_periodicity_list[slot_rx].max_num_of_symbol_per_slot_list; for (int symbol_count = 0; symbol_count < frame_parms->symbols_per_slot; symbol_count++) { if (slot_conf[symbol_count].slot_config.value == 1) { if (num_symb == 0) @@ -1241,8 +1226,9 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N num_symb++; } } - else + } else { num_symb = frame_parms->symbols_per_slot; + } gNB_I0_measurements(gNB, slot_rx, first_symb, num_symb, rb_mask_ul); } diff --git a/openair1/SIMULATION/NR_PHY/pucchsim.c b/openair1/SIMULATION/NR_PHY/pucchsim.c index 6674a94b14..40c6e454aa 100644 --- a/openair1/SIMULATION/NR_PHY/pucchsim.c +++ b/openair1/SIMULATION/NR_PHY/pucchsim.c @@ -630,27 +630,24 @@ int main(int argc, char **argv) rxlev_pucch += signal_energy((int32_t *)&rxdataF[aarx][startingSymbolIndex * frame_parms->ofdm_symbol_size], 12); // set UL mask for pucch allocation - uint32_t rb_mask_ul[14][9] = {0}; + uint32_t rb_mask_ul[14][MAX_BWP_SIZE] = {0}; if (format == 1) { for (int s = 0; s < frame_parms->symbols_per_slot; s++) { if (s >= startingSymbolIndex && s < (startingSymbolIndex + nrofSymbols / 2)) for (int rb = 0; rb < N_RB; rb++) { - int rb2 = rb + startingPRB; - rb_mask_ul[s][rb2 >> 5] |= (1 << (rb2 & 31)); + rb_mask_ul[s][rb + startingPRB] = 1; } else if (s >= (startingSymbolIndex + nrofSymbols / 2) && s < startingSymbolIndex + nrofSymbols) for (int rb = 0; rb < N_RB; rb++) { - int rb2 = rb + startingPRB_intraSlotHopping; - rb_mask_ul[s][rb2 >> 5] |= (1 << (rb2 & 31)); + rb_mask_ul[s][rb + startingPRB_intraSlotHopping] = 1; } } } else { for (int s = 0; s < frame_parms->symbols_per_slot; s++) { if (s >= startingSymbolIndex && s < (startingSymbolIndex + nrofSymbols)) for (int rb = 0; rb < N_RB; rb++) { - int rb2 = rb + startingPRB; - rb_mask_ul[s][rb2 >> 5] |= (1 << (rb2 & 31)); + rb_mask_ul[s][rb + startingPRB] = 1; } } } From 22783bb3a75173862d0b0c8d4048c16e3910f12f Mon Sep 17 00:00:00 2001 From: Guido Casati Date: Tue, 3 Mar 2026 14:54:08 +0100 Subject: [PATCH 67/72] doc (RRC): document neighbour configuration, SIB3/4 and MeasGaps design Expand RRC usage and handover documentation to describe the two-level neighbour configuration layout, lookup-key semantics, and config-time SIB4 grouping behaviour. Expand the example config to include a `frequency_list` block and per-neighbour SIB3/SIB4 offset fields. Add SIB2 config example. Update handover-tutorial.md to describe the same nested model and note that F1 and N2 handover share the same serving-cell keyed mapping. Add documentation about SIB3/SIB4 and MeasGaps implementation in OAI with stress on the shared neighbour configuration data model. Update FEATURE_SET. --- doc/FEATURE_SET.md | 6 +- doc/RRC/rrc-dev.md | 92 ++++++++++++++++++++++ doc/RRC/rrc-usage.md | 164 +++++++++++++++++++++++++++++++++++++-- doc/handover-tutorial.md | 12 ++- 4 files changed, 263 insertions(+), 11 deletions(-) diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index 95aa32166f..bc6ba6b4a3 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -154,7 +154,7 @@ These modes of operation are supported: - RedCap SIB1 v17 IEs supported - Coexistence of RedCap and Normal UEs - Handling of RedCap capability for small PDCP/RLC SN size -- Scheduling of other SIBs (2, 19) +- Scheduling of other SIBs (2, 3, 4, 19) - NTN - Support downlinkHARQ-FeedbackDisabled-r17 - Support for 32 PDSCH and PUSCH HARQ processes per UE @@ -194,7 +194,7 @@ These modes of operation are supported: - NR RRC (38.331) Rel 17 messages using new [asn1c](https://github.com/mouse07410/asn1c) - LTE RRC (36.331) also updated to Rel 15 -- Generation of system information (SIB2) +- Generation of system information (SIB2, SIB3, SIB4) - RRC can configure PDCP and SDAP (through E1), and RLC and MAC (through F1) - Interface with GTP-U (tunnel creation/handling for S1-U (NSA), N3 (SA), F1 interfaces) - Integration of RRC messages and procedures supporting UE 5G SA connection @@ -380,7 +380,7 @@ These modes of operation are supported: - MIB processing - Scheduling of system information block 1 (SIB1) reception * Other system information - - Scheduling of other system information blocks reception + - Scheduling of other system information blocks reception (SIB2, SIB3, SIB4) * Random access procedure (needs improvement, there is still not a clear separation between MAC and PHY) - Mapping SSBs to multiple ROs - Scheduling of PRACH diff --git a/doc/RRC/rrc-dev.md b/doc/RRC/rrc-dev.md index eee1b861fa..a218997d3a 100644 --- a/doc/RRC/rrc-dev.md +++ b/doc/RRC/rrc-dev.md @@ -633,6 +633,98 @@ sequenceDiagram Note over TargetCU: Then: encode Handover Command, send NGAP HANDOVER REQUEST ACKNOWLEDGE ``` +### Neighbour cells + +#### SIB3/SIB4 and measurement-gap implementation + +The following section documents the implementation-level control flow +for SIB3/SIB4 and measurement-gap handling in current OAI, using the configured +neighbor cell list as a shared input model. + +Briefly, the three procedures are: +- SIB3: CU derives and provides intra-frequency neighbour SI, which DU broadcasts + and UE uses for autonomous idle/inactive intra-frequency reselection. +- SIB4: CU derives and provides inter-frequency carrier/neighbour SI, which DU + broadcasts and UE uses for autonomous idle/inactive inter-frequency reselection. +- MeasGap: CU/DU coordinate dedicated `MeasGapConfig` in UE `MeasConfig`, DU + scheduler interrupts transmission (`nr_measgap_scheduling()`) for the UE to + apply the MeasGap configuration in connected mode for gap-based measurements that + are sent as `MeasurementReport` and processed at CU-CP (`rrc_gNB_process_MeasurementReport()`). + Event-driven reports (for example A3) are used by CU-side mobility logic and can + trigger handover procedures. + +### MeasGap + +```mermaid +sequenceDiagram + participant DM as Data model (neighbour_cell_configuration) + participant CU as CU-CP (RRC) + participant DU as DU (MAC/RRC) + participant UE as UE + + CU-->>DM: Read serving + neighbour frequency/PCI/band data + DM-->>CU: Provide neighbour/frequency inputs for MeasConfig + CU->>CU: nr_rrc_get_measconfig + Note over CU: Build UE MeasConfig + CU->>CU: get_meas_timing_config (cell.mtc, ue.measConfig) + Note over CU: Check distinct ssbFrequency + alt one frequency only + Note over CU: returns NULL, no meas_timing_config sent to DU + else multiple frequencies + CU->>DU: F1AP UE Context Setup Request + Note over CU,DU: Includes cu_to_du_rrc_info.meas_timing_config + DU->>DU: create_measgap_config + DU->>DU: encode_measgap_config + DU-->>CU: F1AP UE Context Setup Response + Note over DU,CU: Includes du_to_cu_rrc_info.meas_gap_config + CU->>CU: get_meas_gap_config + Note over CU: Decode/store gap in UE MeasConfig (UE.measConfig.measGapConfig) + CU->>UE: RRCReconfiguration + Note over CU,UE: Includes MeasConfig.measGapConfig + rect rgba(210, 235, 255, 0.35) + Note over DU,UE: Measurement-gap window + DU->>DU: nr_measgap_scheduling + Note over DU: Interrupt transmission for meas gap + UE->>UE: nr_rrc_handle_meas_indication + Note over UE: Perform gap-based neighbour/inter-frequency measurements + UE-->>CU: MeasurementReport + end + Note over UE,CU: UL-DCCH MeasurementReport for configured events (e.g. A3) + CU->>CU: rrc_gNB_process_MeasurementReport + Note over CU: Run mobility decision logic (may trigger handover) + end +``` + +### SIB3/SIB4 + +```mermaid +sequenceDiagram + participant DM as Data model (neighbour_cell_configuration) + participant CU as CU-CP (RRC) + participant DU as DU + participant UE as UE + + CU->>CU: cp_f1_served_cell_info_to_cell + CU->>DM: get_cell_neighbour_list + DM-->>CU: neighbour_cell_configuration for serving cell + CU->>CU: get_ssb_arfcn + Note over CU: Derive serving_ssb_arfcn from cell MTC + loop for each configured neighbour + Note over CU: Compare neighbour.absoluteFrequencySSB vs serving_ssb_arfcn + alt equal + CU->>CU: get_sib3_intra_freq_neighbors + else different + CU->>CU: get_sib4_inter_freq_neighbors + end + end + CU-->>DU: F1AP F1 Setup Response + DU-->>UE: BCCH-DL-SCH-Message + Note over DU,UE: Broadcast SystemInformation including configured SIB3/SIB4 + UE->>UE: nr_rrc_ue_decode_NR_BCCH_DL_SCH_Message + Note over UE: Evaluate idle/inactive reselection criteria and timers + Note over UE,DU: After reselection, access/registration continues on selected cell DU as needed +``` + ## UE Context UE context information is stored in `gNB_RRC_UE_t`, which includes: diff --git a/doc/RRC/rrc-usage.md b/doc/RRC/rrc-usage.md index 46ff9f061c..e828001d1b 100644 --- a/doc/RRC/rrc-usage.md +++ b/doc/RRC/rrc-usage.md @@ -94,7 +94,81 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration. deactivated, a transparent SDAP header is prepended to packets, but no further processing is being done. - `cu_sibs` (default: `[]`) list of SIBs to give to the DU for transmission. - Currently, SIB2 is supported. + Currently supported: + - SIB2: serving-cell reselection parameters (configured in `sib2_config`) + - SIB3: intra-frequency neighbour cell list (neighbours on the same SSB ARFCN as the serving cell) + - SIB4: inter-frequency carriers + neighbour lists, grouped per `(absoluteFrequencySSB, subcarrierSpacing)`. Per-frequency fields (e.g. `cellReselectionPriority`, `threshX_HighP`, `threshX_LowP`, `q_OffsetFreq`) come from `frequency_list` + +Example activation: +``` +cu_sibs = ( 2, 3, 4 ); +``` +SIB2 is configured per-gNB in `sib2_config` (see below). SIB3/SIB4 are derived from the neighbour configuration: +- `neighbour_list` / `neighbour_cell_configuration`: neighbour identity + per-neighbour offsets (`q_OffsetCell`, etc.) +- `frequency_list`: per-frequency SIB4 reselection parameters (priority/thresholds/`q_OffsetFreq`) + +Example `gNBs.[0].sib2_config`: +``` +cu_sibs = ( 2 ); + +sib2_config : { + q_Hyst = 0; + cellReselectionPriority = 0; + threshServingLowP = 0; + threshServingLowQ = 4; + s_NonIntraSearchP = 10; + s_NonIntraSearchQ = 8; + q_RxLevMin = -56; + q_QualMin = -18; + s_IntraSearchP = 22; + s_IntraSearchQ = 20; + t_ReselectionNR = 1; + deriveSSB_IndexFromCell = 1; + + speed_t_Evaluation = 0; + speed_t_HystNormal = 0; + speed_n_CellChangeMedium = 1; + speed_n_CellChangeHigh = 2; + speed_sf_Medium = 1; + speed_sf_High = 0; +}; +``` + +#### SIB3/SIB4 and measurement gaps + +This section summarizes how SIB3/SIB4 and measurement gaps relate in NR and how OAI currently implements them. + +From 3GPP TS 38.331: + +- `SIB3` carries intra-frequency reselection information (`intraFreqNeighCellList`). +- `SIB4` carries inter-frequency reselection information (`interFreqCarrierFreqList` + and per-carrier neighbour lists). +- `MeasGapConfig` is part of dedicated `MeasConfig` (typically sent in + `RRCReconfiguration`) and controls measurement gaps in connected mode. + +In other words: + +- SIB3/SIB4 are broadcast SI for idle/inactive (`RRC_IDLE` / `RRC_INACTIVE`) + reselection behavior: UE performs autonomous cell reselection using broadcast + SI (SIB3 for intra-frequency, SIB4 for inter-frequency). +- `MeasGapConfig` is a dedicated UE measurement behavior and applies to + connected mode (`RRC_CONNECTED`): the network configures what the UE + measures and reports (periodic and event-based, e.g., A3), and those reports + are used by CU-CP mobility logic (including handover decisions). + +MeasGap does not depend on SIB3/SIB4, however they share the same underlying +neighbour/frequency data model, which is the common source of serving + +neighbour frequency information (see also [Neighbor-gNB configuration](#neighbour-gnb-configuration)): + +- SIB3/SIB4 generation is done on the CU-CP side (inside `rrc_gNB_du.c`) from + neighbour/frequency configuration and serving-cell MTC-derived ARFCN. +- Measurement-gap configuration also starts from the same neighbour/frequency + model: CU uses neighbour fields (frequency/PCI/band) to build UE + `MeasConfig` measurement objects, then DU derives/encodes gap parameters + from CU-provided timing (`meas_timing_config`) and returns `meas_gap_config` + for CU forwarding in dedicated `RRCReconfiguration`. + +Detailed implementation flow and sequence diagrams are documented in [`rrc-dev.md`](./rrc-dev.md). #### UE-specific configuration @@ -105,12 +179,64 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration. Refer to the [handover tutorial](../handover-tutorial.md) for detailed information about gNB neighbors and handover procedures. +##### Configuration structure and key semantics + +The neighbour configuration is a 2-level structure: + +- Outer list: `neighbour_list` + - Key: `nr_cellid` of the serving cell + - One entry per serving cell +- Inner list: `neighbour_cell_configuration` + - Actual neighbour cells for that serving cell + - Contains neighbour fields such as `gNB_ID`, neighbour `nr_cellid`, `physical_cellId`, frequency, PLMN, etc. + +This same core configuration model is reused by multiple RRC procedures. + +- SIB3/SIB4 generation: uses neighbour identity/frequency/offset fields to derive + intra/inter-frequency SI. +- Connected-mode measurement config: uses neighbour frequency/PCI/band fields to build + UE `MeasConfig` measurement objects. +- Handover-related procedures: reuse neighbour identity fields (e.g., cell ID/PCI/PLMN + TAC, gNB ID) for target selection and for populating target-cell information carried + in NGAP handover messages. + +Conceptually, for each serving cell the RRC keeps: + +- A per-frequency table (`inter_freqs`): one entry per `(absoluteFrequencySSB, + subcarrierSpacing)` used for SIB4, containing the SIB4 per-frequency fields + (priority, thresholds, `q_OffsetFreq`, `q_RxLevMin`, `t_ReselectionNR`). +- A per-neighbour list (`neighbour_cells`): one entry per neighbour, with + identity (cell ID, PCI, PLMN, TAC), frequency (`absoluteFrequencySSB`, + `subcarrierSpacing`, `band`), and SIB3/SIB4 per-neighbour offsets + (`q_OffsetCell`, `q_RxLevMinOffsetCell`, `q_QualMinOffsetCell`). +- A link from neighbours to frequencies: each neighbour implicitly points to + the matching `inter_freqs` entry via its `(absoluteFrequencySSB, + subcarrierSpacing)`; if no such frequency exists, it is treated as having no + SIB4 per-frequency configuration. + +Notes: + +- In `neighbour_list`, only `nr_cellid` is used as the key for lookup. +- `physical_cellId` belongs to neighbour-cell entries in `neighbour_cell_configuration` (inner list). +- `nr_cellid` entries in `neighbour_list` should be unique to avoid ambiguous lookup. +- Intra-frequency neighbours (SIB3) are derived only from the per-cell + `neighbour_cell_configuration` on the serving carrier. + +At configuration time (`gnb_config.c`), neighbours are parsed into `neighbour_cells`, +per-neighbour SIB3/SIB4 offsets are validated, and a per-frequency array `inter_freqs` +is built by grouping neighbours by `(absoluteFrequencySSB, subcarrierSpacing)` and +in-range SIB4 per-frequency fields (`cellReselectionPriority`, `threshX_HighP/L`, +`q_OffsetFreq`) across neighbours on the same ARFCN. At SIB4 build time, the RRC uses +`inter_freqs` to create one `InterFreqCarrierFreqInfo` per ARFCN for inter-frequency +carriers (ARFCN different from the serving SSB ARFCN) and attaches all neighbours whose +`inter_freq_idx` points to that frequency entry. + ##### Required configuration parameters -To define a neighbor cell in the configuration file, the following parameters are required: +To define a neighbour cell in the configuration file, the following parameters are required: -- `gNB_ID` - identifier of the neighbor gNB (e.g., `0xe01`) -- `nr_cellid` - cell identifier of the neighbor cell (e.g., `11111111`) +- `gNB_ID` - identifier of the neighbour gNB (e.g., `0xe01`) +- `nr_cellid` - cell identifier of the neighbour cell (e.g., `11111111`) - `physical_cellId` - physical cell ID for radio identification (e.g., `1`) - `absoluteFrequencySSB` - SSB frequency in ARFCN notation (e.g., `643296`) - `subcarrierSpacing` - numerology index: 0=15kHz, 1=30kHz, 2=60kHz, 3=120kHz @@ -123,19 +249,45 @@ To define a neighbor cell in the configuration file, the following parameters ar Example configuration structure: ``` +# Per-frequency SIB4 configuration (one entry per ARFCN), shared by all cells +frequency_list = ( + { + absoluteFrequencySSB = 643296; + subcarrierSpacing = 1; # 30 kHz + band = 78; + + frequency_config = ( + { + cellReselectionPriority = 5; + threshX_HighP = 10; + threshX_LowP = 6; + q_OffsetFreq = 0; + # Optional: threshX_HighQ, threshX_LowQ, etc. + } + ); + } +); + +# Per-cell neighbour configuration; neighbours reference frequency_list via ARFCN/SCS neighbour_list = ( { nr_cellid = 12345678; + neighbour_cell_configuration = ( { gNB_ID = 0xe01; nr_cellid = 11111111; physical_cellId = 1; - absoluteFrequencySSB = 643296; - subcarrierSpacing = 1; # 30 kHz + absoluteFrequencySSB = 643296; # ARFCN used to look up matching entry in frequency_list + subcarrierSpacing = 1; # 30 kHz band = 78; plmn = { mcc = 001; mnc = 01; mnc_length = 2 }; tracking_area_code = 1; + + # Example per-neighbour offsets (SIB3/SIB4) + q_OffsetCell = 0; + q_RxLevMinOffsetCell = -1; + q_QualMinOffsetCell = -1; } ); } diff --git a/doc/handover-tutorial.md b/doc/handover-tutorial.md index 68e685c83d..02736da931 100644 --- a/doc/handover-tutorial.md +++ b/doc/handover-tutorial.md @@ -512,8 +512,16 @@ configuration, e.g. [neighbour-config-rfsim.conf](../../ci-scripts/conf_files/ne This configuration can also be present in a different file and included in the gNB configuration file with `@include "neighbour-config-rfsim.conf"`. -For each gNB there is a `neighbour_cell_configuration` linked to its serving -cell ID. +The neighbor configuration is nested: + +- `neighbour_list` outer entries are keyed by serving `nr_cellid` +- each outer entry contains `neighbour_cell_configuration`, i.e., the list of neighbor cells for that serving cell + +In this model: + +- outer `nr_cellid` entries should be unique +- neighbor `physical_cellId` values are defined in inner neighbor entries +- the same serving-cell keyed neighbor mapping is used for both F1 and N2 handover logic See the example above for `neighbour-config-ho.conf`. The same configuration is for both F1 and N2 handover. From 03096dd4957a8254400c74f8a2999a11bf5d7439 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Sat, 11 Apr 2026 11:32:04 +0200 Subject: [PATCH 68/72] Use spsc_q to process PRACH jobs Use two queues for PRACH processing: - prach_ru_queue: pass jobs from TX thread (scheduler) to RU thread. For split 8, the RU thread itself handles PRACH processing; for split 7.2, it is the library that is responsible for handling PRACH messages (see oran_fh_if4p5_south_in()) - prach_l1rx_queue: after jobs have been handled in RU thread, use this queue to pass jobs to the RX thread. There, preamble detection is performed, and RACH.indication FAPI messages are filled if a preamble has been detected. Together, these queues replace a linear search in a global array that has been modified by three threads at the same time. The design ensures that access now is thread-safe, and with less overhead. Signed-off-by: Robert Schmidt --- executables/nr-gnb.c | 7 +- executables/nr-ru.c | 14 +- openair1/PHY/INIT/nr_init.c | 3 +- openair1/PHY/NR_TRANSPORT/nr_prach.c | 130 ++++++++---------- .../PHY/NR_TRANSPORT/nr_transport_proto.h | 14 +- openair1/PHY/defs_gNB.h | 11 +- openair1/SCHED_NR/nr_prach_procedures.c | 17 +-- openair1/SCHED_NR/phy_procedures_nr_gNB.c | 4 +- openair1/SCHED_NR/sched_nr.h | 2 +- openair1/SIMULATION/NR_PHY/prachsim.c | 19 ++- radio/fhi_72/oran_isolate.c | 22 ++- 11 files changed, 118 insertions(+), 125 deletions(-) diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index abbfa05df1..1b27b2e4e6 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -161,7 +161,12 @@ static void rx_func(processingData_L1_t *info) NR_UL_IND_t UL_INFO = {.frame = frame_rx, .slot = slot_rx, .module_id = gNB->Mod_id, .CC_id = gNB->CC_id}; // Do PRACH RU processing UL_INFO.rach_ind.pdu_list = UL_INFO.prach_pdu_indication_list; - L1_nr_prach_procedures(gNB, frame_rx, slot_rx, &UL_INFO.rach_ind); + UL_INFO.rach_ind.number_of_pdus = 0; + // even if processing is late, we might collect all PRACH + // the last PRACH's frame/slot is when all UE's appear to have accessed + prach_item_t p; + while (spsc_q_get(&gNB->prach_l1rx_queue, &p, sizeof(p))) + L1_nr_prach_procedures(gNB, &p, &UL_INFO.rach_ind); //WA: comment rotation in tx/rx if (gNB->phase_comp) { diff --git a/executables/nr-ru.c b/executables/nr-ru.c index 7554c8a9a6..a0bc7c2b5d 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -15,6 +15,7 @@ #include "common/utils/nr/nr_common.h" #include "common/utils/assertions.h" #include "common/utils/system.h" +#include "common/utils/fsn.h" #include "common/ran_context.h" #include "radio/COMMON/common_lib.h" @@ -980,11 +981,16 @@ void *ru_thread(void *param) proc->tti_rx * gNB->frame_parms.samples_per_slot_wCP); // Do PRACH RU processing - prach_item_t *p = - find_nr_prach(&gNB->prach_list, proc->frame_rx, proc->tti_rx, gNB->frame_parms.nb_antennas_rx, NR_SEARCH_EXIST); - if (p) { + fsn_t now = {.f = proc->frame_rx, .s = proc->tti_rx, .mu = fp->numerology_index}; + prach_item_t p; + while (get_next_nr_prach(&gNB->prach_ru_queue, &now, &p)) { // need to extract RACH data for later processing by rx_nr_prach() - rx_nr_prach_ru(p, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); + rx_nr_prach_ru(&p, ru->common.rxdata, ru->nr_frame_parms, ru->N_TA_offset); + bool success = spsc_q_put(&gNB->prach_l1rx_queue, &p, sizeof(p)); + // assume prach_l1rx_queue never full: prach_ru_queue filled at + // constant pace, but prach_l1rx_queue emptied as fast as possible, + // see rx_func() + DevAssert(success); } // end if (prach_id >= 0) } // end if (ru->feprx) } // end if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) { diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index a007bbd6d7..35b27468f6 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -166,7 +166,7 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) common_vars->debugBuff_sample_offset = 0; // PRACH - init_prach_list(&gNB->prach_list); + init_nr_prach(gNB); int N_RB_UL = cfg->carrier_config.ul_grid_size[cfg->ssb_config.scs_common.value].value; int n_buf = Prx*max_ul_mimo_layers; @@ -208,6 +208,7 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB) free_gnb_lowpapr_sequences(); reset_nr_transport(gNB); + reset_nr_prach(gNB); destroy_DLSCH_struct(gNB); diff --git a/openair1/PHY/NR_TRANSPORT/nr_prach.c b/openair1/PHY/NR_TRANSPORT/nr_prach.c index c54c9ccc29..dc3cb1950d 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_prach.c +++ b/openair1/PHY/NR_TRANSPORT/nr_prach.c @@ -12,72 +12,73 @@ #include "PHY/NR_TRANSPORT/nr_transport_common_proto.h" #include "openair1/PHY/NR_TRANSPORT/nr_prach.h" -void init_prach_list(prach_list_t *l) +void init_nr_prach(PHY_VARS_gNB *gNB) { - pthread_mutex_init(&l->prach_list_mutex, NULL); - memset(l->list, 0, sizeof(l->list)); + int num_prach = 8; + gNB->prach_ru_queue = spsc_q_alloc(num_prach, sizeof(prach_item_t)); + gNB->prach_l1rx_queue = spsc_q_alloc(num_prach, sizeof(prach_item_t)); } -void free_nr_prach_entry(prach_list_t *l, prach_item_t *p) +void reset_nr_prach(PHY_VARS_gNB *gNB) { - pthread_mutex_lock(&l->prach_list_mutex); - if (p->frame == -1) - LOG_E(NR_PHY_RACH, "Freeing a not allocated prach entry\n"); - *p = (prach_item_t){.frame = -1, .slot = -1, .num_slots = -1, .nb_rx = p->nb_rx, .prach_buf = (void *)(p + 1)}; - pthread_mutex_unlock(&l->prach_list_mutex); + spsc_q_free(&gNB->prach_ru_queue); + spsc_q_free(&gNB->prach_l1rx_queue); } -prach_item_t *find_nr_prach(prach_list_t *l, int frame, int slot, int nb_rx, nr_find_type_t type) +void free_nr_prach_entry(prach_item_t *p) { - pthread_mutex_lock(&l->prach_list_mutex); - AssertFatal(nb_rx, "Error! Number of antennas set to 0.\n"); - prach_item_t **p = l->list; - prach_item_t **end = p + NUMBER_OF_NR_PRACH_MAX; - for (; p < end; p++) - if ((*p) && (*p)->frame == frame && ((*p)->slot + (*p)->num_slots - 1) == slot) - break; - if (p == end) { - if (type == NR_SEARCH_EXIST) { - pthread_mutex_unlock(&l->prach_list_mutex); - return NULL; - } - for (p = l->list; p < end; p++) - if (!(*p) || ((*p)->frame == -1 && (*p)->slot == -1)) - break; - } - if (p == end) { - pthread_mutex_unlock(&l->prach_list_mutex); - return NULL; - } - // mark it used, it will be filled later - if (*p && (*p)->nb_rx != nb_rx) { - LOG_W(PHY, "rach procedure nb_rx ant changed from %d to %d\n", (*p)->nb_rx, nb_rx); - free(*p); - *p = NULL; - } - if (!(*p)) { - *p = calloc(1, sizeof(prach_item_t) + sizeof(c16_t) * nb_rx * NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX * NR_PRACH_SEQ_LEN_L); - (*p)->prach_buf = (void *)((*p) + 1); - } - (*p)->nb_rx = nb_rx; - (*p)->frame = frame; - pthread_mutex_unlock(&l->prach_list_mutex); - return (*p); + free(p->prach_buf); } -prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) +static bool drop_old_prach(const void *data, void *user) +{ + const prach_item_t *p = data; + const fsn_t *now = user; + const fsn_t t = {p->frame, p->slot, now->mu}; + bool drop = fsn_in_the_past(t, *now); + if (drop) + LOG_E(NR_PHY, "%4d.%2d PRACH job is in the past (%4d.%2d)\n", now->f, now->s, t.f, t.s); + return drop; +} + +static bool get_current_prach(const void *data, void *user) +{ + const prach_item_t *p = data; + const fsn_t *now = user; + const fsn_t t = {p->frame, p->slot, now->mu}; + return fsn_equal(t, *now); +} + +bool get_next_nr_prach(spsc_q_t *q, const fsn_t *now, prach_item_t *p) +{ + spsc_q_drop_while(q, drop_old_prach, (void *)now); + return spsc_q_get_if(q, get_current_prach, (void *)now, p, sizeof(*p)); +} + +void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu) { - prach_item_t *prach = find_nr_prach(&gNB->prach_list, SFN, Slot, gNB->frame_parms.nb_antennas_rx, NR_SEARCH_EXIST_OR_FREE); - if (!prach) { - LOG_W(PHY, "no free space for a new detected rach, discarding\n"); - return NULL; - } const int fmt = prach_pdu->prach_format; - NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; - // we set only elements as the whole structure has been cleaned when we release it - prach->frame = SFN; - prach->slot = Slot; - prach->num_slots = fmt < 4 ? get_long_prach_dur(fmt, fp->numerology_index) : 1; + const NR_DL_FRAME_PARMS *fp = &gNB->frame_parms; + const nfapi_nr_prach_config_t *cfg = &gNB->gNB_config.prach_config; + const nfapi_nr_num_prach_fd_occasions_t *occ = &cfg->num_prach_fd_occasions_list[prach_pdu->num_ra]; + prach_item_t prach = { + .frame = SFN, + .slot = Slot, + .num_slots = fmt < 4 ? get_long_prach_dur(fmt, fp->numerology_index) : 1, + .pdu = *prach_pdu, + .rootSequenceIndex = occ->prach_root_sequence_index.value, + .numrootSequenceIndex = occ->num_root_sequences.value, + .msg1_frequencystart = occ->k1.value, + .mu = cfg->prach_sub_c_spacing.value, + .prach_sequence_length = cfg->prach_sequence_length.value, + .restricted_set = cfg->restricted_set_config.value, + .numerology_index = fp->numerology_index, + .nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value, + .Xu = gNB->X_u, + .rx_prach = &gNB->rx_prach, + // TODO can be made permanently allocated? + .prach_buf = calloc_or_fail(1, sizeof(c16_t) * prach.nb_rx * NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX * NR_PRACH_SEQ_LEN_L), + }; if (gNB->common_vars.beam_id) { int n_symb = get_nr_prach_duration(prach_pdu->prach_format); AssertFatal(prach_pdu->beamforming.dig_bf_interface < NFAPI_MAX_NUM_BG_IF, @@ -87,7 +88,7 @@ prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_n int fapi_beam_idx = prach_pdu->beamforming.prgs_list[0].dig_bf_interface_list[i].beam_idx; int start_symb = prach_pdu->prach_start_symbol + i * n_symb; int bitmap = SL_to_bitmap(start_symb, n_symb); - prach->beams[i] = beam_index_allocation(gNB->enable_analog_das, + prach.beams[i] = beam_index_allocation(gNB->enable_analog_das, fapi_beam_idx, &gNB->common_vars, Slot, @@ -95,20 +96,9 @@ prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_n bitmap); } } - prach->pdu = *prach_pdu; - nfapi_nr_prach_config_t *cfg = &gNB->gNB_config.prach_config; - nfapi_nr_num_prach_fd_occasions_t *occ = &cfg->num_prach_fd_occasions_list[prach_pdu->num_ra]; - prach->rootSequenceIndex = occ->prach_root_sequence_index.value; - prach->numrootSequenceIndex = occ->num_root_sequences.value; - prach->msg1_frequencystart = occ->k1.value; - prach->mu = cfg->prach_sub_c_spacing.value; - prach->prach_sequence_length = cfg->prach_sequence_length.value; - prach->restricted_set = cfg->restricted_set_config.value; - prach->numerology_index = fp->numerology_index; - prach->nb_rx = gNB->gNB_config.carrier_config.num_rx_ant.value; - prach->Xu = gNB->X_u; - prach->rx_prach = &gNB->rx_prach; - return prach; + bool found = spsc_q_put(&gNB->prach_ru_queue, &prach, sizeof(prach)); + if (!found) + LOG_W(NR_PHY, "%4d.%2d PRACH occ queue is full: dropping PRACH request\n", SFN, Slot); } static void rx_nr_prach_ru_internal(prach_item_t *p, diff --git a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h index 32efa979cc..863903c242 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h +++ b/openair1/PHY/NR_TRANSPORT/nr_transport_proto.h @@ -11,6 +11,7 @@ #include "PHY/defs_nr_common.h" #include "PHY/defs_gNB.h" +#include "common/utils/fsn.h" #define NR_PBCH_PDU_BITS 24 @@ -167,7 +168,7 @@ void nr_fill_ulsch(PHY_VARS_gNB *gNB, int slot, nfapi_nr_pusch_pdu_t *ulsch_pdu); -prach_item_t *nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); +void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_pdu_t *prach_pdu); typedef struct rx_prach_out { uint16_t max_preamble; @@ -178,12 +179,6 @@ rx_prach_out_t rx_nr_prach(const prach_item_t *, int occasion); void rx_nr_prach_ru(prach_item_t *, int32_t **, NR_DL_FRAME_PARMS *frame_parms, int N_TA_offset); -typedef enum { - NR_SEARCH_EXIST = 0, - NR_SEARCH_EXIST_OR_FREE -} nr_find_type_t; - -prach_item_t *find_nr_prach(prach_list_t *, int frame, int slot, int nb_rx, nr_find_type_t type); void nr_fill_pucch(PHY_VARS_gNB *gNB, int frame, int slot, @@ -219,7 +214,10 @@ nr_srs_info_t nr_srs_rx_procedures(PHY_VARS_gNB *gNB, int get_nr_prach_duration(uint8_t prach_format); -void free_nr_prach_entry(prach_list_t *, prach_item_t *); +void init_nr_prach(PHY_VARS_gNB *gNB); +void reset_nr_prach(PHY_VARS_gNB *gNB); +void free_nr_prach_entry(prach_item_t *); +bool get_next_nr_prach(spsc_q_t *q, const fsn_t *now, prach_item_t *p); void nr_decode_pucch1(PHY_VARS_gNB *gNB, c16_t **rxdataF, diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index 479a214418..3e627c38cd 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -46,14 +46,6 @@ typedef struct { c16_t (*prach_buf)[NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX][NR_PRACH_SEQ_LEN_L]; } prach_item_t; -typedef struct { - /// prach commands - prach_item_t *list[NUMBER_OF_NR_PRACH_MAX]; - /// mutex for prach_list access - pthread_mutex_t prach_list_mutex; -} prach_list_t; -void init_prach_list(prach_list_t *); - typedef struct { int nb_id; int Nid[MAX_PUCCH0_NID]; @@ -378,7 +370,8 @@ typedef struct PHY_VARS_gNB_s { int max_nb_pusch; NR_gNB_COMMON common_vars; - prach_list_t prach_list; + spsc_q_t prach_ru_queue; + spsc_q_t prach_l1rx_queue; // TODO: can we remove c from NR_gNB_DLSCH_t and put it on the stack? NR_gNB_DLSCH_t *dlsch; NR_gNB_PRS prs_vars; diff --git a/openair1/SCHED_NR/nr_prach_procedures.c b/openair1/SCHED_NR/nr_prach_procedures.c index fcaf2f1c1b..929bf476f3 100644 --- a/openair1/SCHED_NR/nr_prach_procedures.c +++ b/openair1/SCHED_NR/nr_prach_procedures.c @@ -21,20 +21,14 @@ int get_nr_prach_duration(uint8_t prach_format) return val[prach_format]; } -void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rach_indication_t *rach_ind) +void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, prach_item_t *prach_id, nfapi_nr_rach_indication_t *rach_ind) { + const frame_t frame = prach_id->frame; + const slot_t slot = prach_id->slot; rach_ind->sfn = frame; rach_ind->slot = slot; - rach_ind->number_of_pdus = 0; - - prach_item_t *prach_id = find_nr_prach(&gNB->prach_list, frame, slot, gNB->frame_parms.nb_antennas_rx, NR_SEARCH_EXIST); - if (!prach_id) { - return; - } - nfapi_nr_prach_pdu_t *prach_pdu = &prach_id->pdu; LOG_D(NR_PHY_RACH, "%d.%d, prachstart slot %d prach entry occas %d\n", frame, slot, prach_id->slot, prach_pdu->num_prach_ocas); - const int prach_start_slot = prach_id->slot; int N_dur = get_nr_prach_duration(prach_pdu->prach_format); for (int prach_oc = 0; prach_oc < prach_pdu->num_prach_ocas; prach_oc++) { @@ -63,7 +57,7 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac "[RAPROC] %d.%d Initiating RA procedure with preamble %d, energy %d.%d dB (I0 %d, thres %d), delay %d start symbol " "%u freq index %u\n", frame, - prach_start_slot, + slot, res.max_preamble, res.max_preamble_energy / 10, res.max_preamble_energy % 10, @@ -100,7 +94,6 @@ void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rac if (gNB->prach_energy_counter < NUM_PRACH_RX_FOR_NOISE_ESTIMATE) gNB->prach_energy_counter++; } // if prach_id>0 - rach_ind->slot = prach_start_slot; LOG_D(NR_PHY_RACH, "Freeing PRACH entry\n"); - free_nr_prach_entry(&gNB->prach_list, prach_id); + free_nr_prach_entry(prach_id); } diff --git a/openair1/SCHED_NR/phy_procedures_nr_gNB.c b/openair1/SCHED_NR/phy_procedures_nr_gNB.c index 686f88a5fa..33ee17f374 100644 --- a/openair1/SCHED_NR/phy_procedures_nr_gNB.c +++ b/openair1/SCHED_NR/phy_procedures_nr_gNB.c @@ -1330,9 +1330,7 @@ void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req break; case NFAPI_NR_UL_CONFIG_PRACH_PDU_TYPE: { nfapi_nr_prach_pdu_t *prach_pdu = &UL_tti_req->pdus_list[i].prach_pdu; - prach_item_t *prach = nr_schedule_rx_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); - if (!prach) - LOG_W(NR_PHY_RACH, "Error in scheduling rach\n"); + nr_schedule_rx_prach(gNB, UL_tti_req->SFN, UL_tti_req->Slot, prach_pdu); } break; case NFAPI_NR_UL_CONFIG_SRS_PDU_TYPE: nr_fill_srs(gNB, UL_tti_req->SFN, UL_tti_req->Slot, &UL_tti_req->pdus_list[i].srs_pdu); diff --git a/openair1/SCHED_NR/sched_nr.h b/openair1/SCHED_NR/sched_nr.h index 7ff044b46c..ba1d00457b 100644 --- a/openair1/SCHED_NR/sched_nr.h +++ b/openair1/SCHED_NR/sched_nr.h @@ -18,7 +18,7 @@ void phy_procedures_gNB_TX(PHY_VARS_gNB *gNB, int slot); void nr_save_ul_tti_req(PHY_VARS_gNB *gNB, nfapi_nr_ul_tti_request_t *UL_tti_req); int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, NR_UL_IND_t *UL_INFO); -void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, int frame, int slot, nfapi_nr_rach_indication_t *rach_ind); +void L1_nr_prach_procedures(PHY_VARS_gNB *gNB, prach_item_t *prach_id, nfapi_nr_rach_indication_t *rach_ind); void nr_common_signal_procedures (PHY_VARS_gNB *gNB,int frame,int slot, const nfapi_nr_dl_tti_ssb_pdu *ssb_pdu); void nr_feptx_ofdm(RU_t *ru,int frame_tx,int tti_tx); void nr_feptx0(RU_t *ru,int tti_tx,int first_symbol, int num_symbols, int aa); diff --git a/openair1/SIMULATION/NR_PHY/prachsim.c b/openair1/SIMULATION/NR_PHY/prachsim.c index 6e29abd9a5..b47b35b470 100644 --- a/openair1/SIMULATION/NR_PHY/prachsim.c +++ b/openair1/SIMULATION/NR_PHY/prachsim.c @@ -547,12 +547,10 @@ int main(int argc, char **argv){ phy_init_nr_gNB(gNB); nr_phy_init_RU(ru); - prach_item_t *prach_data = calloc(1,sizeof(prach_item_t)); - gNB->prach_list.list[0] = prach_data; - nfapi_nr_prach_pdu_t *prach_pdu = &prach_data->pdu; - prach_pdu->num_cs = get_NCS(NCS_config, format0, restrictedSetConfig); - prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value = 1+(64/(N_ZC/prach_pdu->num_cs)); - prach_pdu->prach_format = prach_format; + nfapi_nr_prach_pdu_t prach_pdu = {0}; + prach_pdu.num_cs = get_NCS(NCS_config, format0, restrictedSetConfig); + prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value = 1+(64/(N_ZC/prach_pdu.num_cs)); + prach_pdu.prach_format = prach_format; // Configure UE UE = malloc(sizeof(PHY_VARS_NR_UE)); @@ -749,14 +747,14 @@ int main(int argc, char **argv){ } nfapi_nr_prach_config_t *cfg = &gNB->gNB_config.prach_config; - nfapi_nr_num_prach_fd_occasions_t *occ = &cfg->num_prach_fd_occasions_list[prach_pdu->num_ra]; - prach_pdu->num_prach_ocas=1; + nfapi_nr_num_prach_fd_occasions_t *occ = &cfg->num_prach_fd_occasions_list[prach_pdu.num_ra]; + prach_pdu.num_prach_ocas=1; prach_item_t *in = malloc(sizeof(prach_item_t) + sizeof(c16_t) * gNB->gNB_config.carrier_config.num_rx_ant.value * NUMBER_OF_NR_RU_PRACH_OCCASIONS_MAX * NR_PRACH_SEQ_LEN_L); *in = (prach_item_t){.frame = frame, .slot = slot, - .pdu = *prach_pdu, + .pdu = prach_pdu, .rootSequenceIndex = occ->prach_root_sequence_index.value, .numrootSequenceIndex = occ->num_root_sequences.value, .msg1_frequencystart = occ->k1.value, @@ -772,7 +770,7 @@ int main(int argc, char **argv){ if (n_frames == 1) LOG_I(PHY, "ncs %d,num_seq %d\n", - prach_pdu->num_cs, + prach_pdu.num_cs, prach_config->num_prach_fd_occasions_list[fd_occasion].num_root_sequences.value); rx_prach_out_t out = rx_nr_prach(in, prachOccasion); @@ -823,7 +821,6 @@ int main(int argc, char **argv){ nr_phy_free_RU(ru); free(RC.ru[0]); free(RC.ru); - free(prach_data); phy_free_nr_gNB(gNB); // allocated in set_tdd_config_nr() int nb_slots_to_set = (1<gNB_list[0]->prach_list, *frame, *slot, ru->nr_frame_parms->nb_antennas_rx, SEARCH_EXIST); - if (prach_id) { + prach_item_t p; + PHY_VARS_gNB *gNB = ru->gNB_list[0]; + fsn_t now = {.f = *frame, .s = *slot, .mu = gNB->frame_parms.numerology_index}; + if (get_next_nr_prach(&gNB->prach_ru_queue, &now, &p)) { struct xran_fh_config *fh_cfg = get_xran_fh_config(0); int slots_per_subframe = 1 << fh_cfg->frame_conf.nNumerology; uint32_t subframe = *slot / slots_per_subframe; // `slot` = slot in which PRACH is received // PRACH occasion in a frame if and only if SFN % x == y, TS 38.211 Table 6.3.3.2-2/3/4 nr_prach_info_t prach_info = get_prach_info(0); bool is_prach_frame = (*frame % prach_info.x == prach_info.y); - bool is_prach_slot = is_prach_frame && xran_is_prach_slot(0, subframe, (prach_id->slot % slots_per_subframe)); // `prach_id->slot` = slot in which PRACH is scheduled + bool is_prach_slot = is_prach_frame && xran_is_prach_slot(0, subframe, (p.slot % slots_per_subframe)); // `p.slot` = slot in which PRACH is scheduled if (is_prach_slot) { - ru_info.prach_buf = prach_id->prach_buf; + ru_info.prach_buf = p.prach_buf; } else { - LOG_W(HW, "[%d.%d] Expected PRACH reception of scheduled slot %d\n", *frame, *slot, prach_id->slot); + LOG_W(HW, "[%d.%d] Expected PRACH reception of scheduled slot %d\n", *frame, *slot, p.slot); } } @@ -232,6 +235,15 @@ void oran_fh_if4p5_south_in(RU_t *ru, int *frame, int *slot) printf("ORAN: %d.%d ORAN_fh_if4p5_south_in ERROR in RX function \n", f, sl); } + if (ru_info.prach_buf) { + // xran_fh_rx_read_slot() should have read PRACH, write back to queue + bool success = spsc_q_put(&gNB->prach_l1rx_queue, &p, sizeof(p)); + // assume prach_l1rx_queue never full: prach_ru_queue filled at + // constant pace, but prach_l1rx_queue emptied as fast as possible, + // see rx_func() + DevAssert(success); + } + int slots_per_frame = 10 << (ru->openair0_cfg.nr_scs_for_raster); proc->tti_rx = sl; proc->frame_rx = f; From aa5bd641fd56d19c087bd933696a1c6efd553743 Mon Sep 17 00:00:00 2001 From: rmagueta Date: Wed, 15 Apr 2026 10:35:47 +0100 Subject: [PATCH 69/72] 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 70/72] 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); From 1c512f3dc56d83025dd0ffaae17120739acf6d29 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Tue, 3 Mar 2026 18:09:20 +0100 Subject: [PATCH 71/72] Remove cppcheck from CI, add to tools/ We don't enforce cppcheck through the CI, although it's there since years. It runs on Ubuntu 18/20, so it's old. For folks, it's likely not discoverable on how to run it locally. Let's make a fresh start. This removes cppcheck from all CI-related code. Instead, it adds it under tools/cppcheck/, including documentation on how to run it locally, bare-matel or in docker. Signed-off-by: Robert Schmidt --- ci-scripts/Jenkinsfile-GitLab-Container | 22 ---- ci-scripts/cls_oai_html.py | 84 ------------ ci-scripts/cls_static_code_analysis.py | 120 ------------------ ci-scripts/docker/Dockerfile.cppcheck.xenial | 41 ------ ci-scripts/main.py | 3 - ci-scripts/xml_class_list.yml | 1 - ci-scripts/xml_files/cppcheck.xml | 20 --- doc/README.md | 1 + doc/TESTBenches.md | 4 - tools/cppcheck/README.md | 28 ++++ tools/cppcheck/docker-compose.yaml | 14 ++ tools/cppcheck/run-cppcheck.sh | 11 ++ .../cppcheck/suppressions.list | 0 13 files changed, 54 insertions(+), 295 deletions(-) delete mode 100644 ci-scripts/docker/Dockerfile.cppcheck.xenial delete mode 100644 ci-scripts/xml_files/cppcheck.xml create mode 100644 tools/cppcheck/README.md create mode 100644 tools/cppcheck/docker-compose.yaml create mode 100755 tools/cppcheck/run-cppcheck.sh rename ci-scripts/cppcheck_suppressions.list => tools/cppcheck/suppressions.list (100%) diff --git a/ci-scripts/Jenkinsfile-GitLab-Container b/ci-scripts/Jenkinsfile-GitLab-Container index d75b1888d4..721c89b77f 100644 --- a/ci-scripts/Jenkinsfile-GitLab-Container +++ b/ci-scripts/Jenkinsfile-GitLab-Container @@ -219,28 +219,6 @@ pipeline { } } } - stage ("cppcheck") { - steps { - script { - triggerSlaveJob ('RAN-cppcheck', 'cppcheck') - } - } - post { - always { - script { - // Using a unique variable name for each test stage to avoid overwriting on a global variable - // due to parallel-time concurrency - cppcheckStatus = finalizeSlaveJob('RAN-cppcheck') - } - } - failure { - script { - currentBuild.result = 'FAILURE' - failingStages += cppcheckStatus - } - } - } - } stage ("ARM-Cross-Compile") { steps { script { diff --git a/ci-scripts/cls_oai_html.py b/ci-scripts/cls_oai_html.py index 2f59ae1852..66d4b804ae 100644 --- a/ci-scripts/cls_oai_html.py +++ b/ci-scripts/cls_oai_html.py @@ -379,90 +379,6 @@ class HTMLManagement(): self.htmlFile.write(' \n') self.htmlFile.close() - def CreateHtmlTestRowCppCheckResults(self, CCR): - if (self.htmlFooterCreated or (not self.htmlHeaderCreated)): - return - self.htmlFile = open('test_results.html', 'a') - vId = 0 - for version in CCR.versions: - self.htmlFile.write(' \n') - self.htmlFile.write(' Results for cppcheck v ' + version + ' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' NB ERRORS\n') - if CCR.nbErrors[vId] == 0: - myColor = 'lightgreen' - elif CCR.nbErrors[vId] < 20: - myColor = 'orange' - else: - myColor = 'lightcoral' - self.htmlFile.write(' ' + str(CCR.nbErrors[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' NB WARNINGS\n') - if CCR.nbWarnings[vId] == 0: - myColor = 'lightgreen' - elif CCR.nbWarnings[vId] < 20: - myColor = 'orange' - else: - myColor = 'lightcoral' - self.htmlFile.write(' ' + str(CCR.nbWarnings[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' ----------------- \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Memory leak\n') - self.htmlFile.write(' ' + str(CCR.nbMemLeaks[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Possible null pointer deference\n') - self.htmlFile.write(' ' + str(CCR.nbNullPtrs[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Uninitialized variable\n') - self.htmlFile.write(' ' + str(CCR.nbUninitVars[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Undefined behaviour shifting\n') - self.htmlFile.write(' ' + str(CCR.nbTooManyBitsShift[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Signed integer overflow\n') - self.htmlFile.write(' ' + str(CCR.nbIntegerOverflow[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Printf formatting issues\n') - self.htmlFile.write(' ' + str(CCR.nbInvalidPrintf[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Modulo result is predetermined\n') - self.htmlFile.write(' ' + str(CCR.nbModuloAlways[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Opposite Condition -> dead code\n') - self.htmlFile.write(' ' + str(CCR.nbOppoInnerCondition[vId]) + '\n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' \n') - self.htmlFile.write(' Wrong Scanf Nb Args\n') - self.htmlFile.write(' ' + str(CCR.nbWrongScanfArg[vId]) + '\n') - self.htmlFile.write(' \n') - vId += 1 - def CreateHtmlTestRowPhySimTestResult(self, testSummary, testResult): if (self.htmlFooterCreated or (not self.htmlHeaderCreated)): return diff --git a/ci-scripts/cls_static_code_analysis.py b/ci-scripts/cls_static_code_analysis.py index 603df581e3..4e388847c7 100644 --- a/ci-scripts/cls_static_code_analysis.py +++ b/ci-scripts/cls_static_code_analysis.py @@ -27,28 +27,6 @@ import constants as CONST import cls_cmd from cls_ci_helper import archiveArtifact -#----------------------------------------------------------- -# Class Declaration -#----------------------------------------------------------- -class CppCheckResults(): - - def __init__(self): - - self.variants = ['bionic', 'focal'] - self.versions = ['',''] - self.nbErrors = [0,0] - self.nbWarnings = [0,0] - self.nbNullPtrs = [0,0] - self.nbMemLeaks = [0,0] - self.nbUninitVars = [0,0] - self.nbInvalidPrintf = [0,0] - self.nbModuloAlways = [0,0] - self.nbTooManyBitsShift = [0,0] - self.nbIntegerOverflow = [0,0] - self.nbWrongScanfArg = [0,0] - self.nbPtrAddNotNull = [0,0] - self.nbOppoInnerCondition = [0,0] - class StaticCodeAnalysis(): def __init__(self): @@ -60,104 +38,6 @@ class StaticCodeAnalysis(): self.ranTargetBranch = '' self.eNBSourceCodePath = '' - def CppCheckAnalysis(self, ctx, node, HTML): - if self.ranRepository == '' or self.ranBranch == '' or self.ranCommitID == '': - HELP.GenericHelp(CONST.Version) - sys.exit('Insufficient Parameter') - lSourcePath = self.eNBSourceCodePath - - if not lSourcePath or not node: - raise ValueError(f"{lSourcePath=} {node=}") - logging.debug('Building on server: ' + node) - cmd = cls_cmd.getConnection(node) - # on RedHat/CentOS .git extension is mandatory - result = re.search(r'([a-zA-Z0-9\:\-\.\/])+\.git', self.ranRepository) - if result is not None: - full_ran_repo_name = self.ranRepository.replace('git/', 'git') - else: - full_ran_repo_name = self.ranRepository + '.git' - - cmd.cd(lSourcePath) - logDir = f'{lSourcePath}/cmake_targets/log' - cmd.run(f'mkdir -p {logDir}') - cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal') - cmd.run(f'sed -e "s@xenial@bionic@" {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.xenial > {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.bionic') - cmd.run(f'docker build --tag oai-cppcheck:bionic --file {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.bionic . > {logDir}/cppcheck-bionic.txt 2>&1') - cmd.run(f'sed -e "s@xenial@focal@" {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.xenial > {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.focal') - cmd.run(f'docker build --tag oai-cppcheck:focal --file {lSourcePath}/ci-scripts/docker/Dockerfile.cppcheck.focal . > {logDir}/cppcheck-focal.txt 2>&1') - cmd.run('docker image rm oai-cppcheck:bionic oai-cppcheck:focal') - - bionic = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-bionic.txt') - focal = archiveArtifact(cmd, ctx, f'{logDir}/cppcheck-focal.txt') - cmd.close() - - CCR = CppCheckResults() - CCR_ref = CppCheckResults() - vId = 0 - for variant in CCR.variants: - filename = ctx.baseFilename() + '-cppcheck-'+ variant + '.txt' - logging.info(f"will check file '{filename}'") - if not os.path.isfile(filename): - raise FileNotFoundError(f"{filename} is not a file") - else: - xmlStart = False - with open(filename, 'r') as logfile: - for line in logfile: - ret = re.search(r'cppcheck version="(?P[0-9\.]+)"', str(line)) - if ret is not None: - CCR.versions[vId] = ret.group('version') - if re.search('RUN cat cmake_targets/log/cppcheck.xml', str(line)) is not None: - xmlStart = True - if xmlStart: - if re.search('severity="error"', str(line)) is not None: - CCR.nbErrors[vId] += 1 - if re.search('severity="warning"', str(line)) is not None: - CCR.nbWarnings[vId] += 1 - if re.search('id="memleak"', str(line)) is not None: - CCR.nbMemLeaks[vId] += 1 - if re.search('id="nullPointer"', str(line)) is not None: - CCR.nbNullPtrs[vId] += 1 - if re.search('id="uninitvar"', str(line)) is not None: - CCR.nbUninitVars[vId] += 1 - if re.search('id="invalidPrintfArgType_sint"|id="invalidPrintfArgType_uint"', str(line)) is not None: - CCR.nbInvalidPrintf[vId] += 1 - if re.search('id="moduloAlwaysTrueFalse"', str(line)) is not None: - CCR.nbModuloAlways[vId] += 1 - if re.search('id="shiftTooManyBitsSigned"', str(line)) is not None: - CCR.nbTooManyBitsShift[vId] += 1 - if re.search('id="integerOverflow"', str(line)) is not None: - CCR.nbIntegerOverflow[vId] += 1 - if re.search('id="wrongPrintfScanfArgNum"|id="invalidScanfArgType_int"', str(line)) is not None: - CCR.nbWrongScanfArg[vId] += 1 - if re.search('id="pointerAdditionResultNotNull"', str(line)) is not None: - CCR.nbPtrAddNotNull[vId] += 1 - if re.search('id="oppositeInnerCondition"', str(line)) is not None: - CCR.nbOppoInnerCondition[vId] += 1 - vMsg = '' - vMsg += '======== Variant ' + variant + ' - ' + CCR.versions[vId] + ' ========\n' - vMsg += ' ' + str(CCR.nbErrors[vId]) + ' errors\n' - vMsg += ' ' + str(CCR.nbWarnings[vId]) + ' warnings\n' - vMsg += ' -- Details --\n' - vMsg += ' Memory leak: ' + str(CCR.nbMemLeaks[vId]) + '\n' - vMsg += ' Possible null pointer deference: ' + str(CCR.nbNullPtrs[vId]) + '\n' - vMsg += ' Uninitialized variable: ' + str(CCR.nbUninitVars[vId]) + '\n' - vMsg += ' Undefined behaviour shifting: ' + str(CCR.nbTooManyBitsShift[vId]) + '\n' - vMsg += ' Signed integer overflow: ' + str(CCR.nbIntegerOverflow[vId]) + '\n' - vMsg += '\n' - vMsg += ' Printf formatting issue: ' + str(CCR.nbInvalidPrintf[vId]) + '\n' - vMsg += ' Modulo result is predetermined: ' + str(CCR.nbModuloAlways[vId]) + '\n' - vMsg += ' Opposite Condition -> dead code: ' + str(CCR.nbOppoInnerCondition[vId]) + '\n' - vMsg += ' Wrong Scanf Nb Args: ' + str(CCR.nbWrongScanfArg[vId]) + '\n' - for vLine in vMsg.split('\n'): - logging.debug(vLine) - vId += 1 - - HTML.CreateHtmlTestRow('N/A', 'OK', CONST.ALL_PROCESSES_OK) - HTML.CreateHtmlTestRowCppCheckResults(CCR) - logging.info('\u001B[1m Static Code Analysis Pass\u001B[0m') - - return True - def LicenceAndFormattingCheck(self, ctx, node, HTML): # Workspace is no longer recreated from scratch. # It implies that this method shall be called last within a build pipeline diff --git a/ci-scripts/docker/Dockerfile.cppcheck.xenial b/ci-scripts/docker/Dockerfile.cppcheck.xenial deleted file mode 100644 index fcb7f814ae..0000000000 --- a/ci-scripts/docker/Dockerfile.cppcheck.xenial +++ /dev/null @@ -1,41 +0,0 @@ -# SPDX-License-Identifier: LicenseRef-CSSL-1.0 - -#--------------------------------------------------------------------- -# -# Dockerfile for the Open-Air-Interface BUILD service -# Valid for Ubuntu 22.04 -# -#--------------------------------------------------------------------- -FROM ubuntu:xenial AS oai-cppcheck - - -ENV DEBIAN_FRONTEND=noninteractive - -RUN apt-get update && \ - apt-get upgrade --yes && \ - apt-get install --yes \ - build-essential \ - vim \ - cppcheck - -WORKDIR /oai-ran -COPY . . - -WORKDIR /oai-ran/common/utils/T -RUN make - -WORKDIR /oai-ran -RUN mkdir -p cmake_targets/log && \ - cppcheck --enable=warning --force --xml --xml-version=2 \ - --inline-suppr \ - -i openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c \ - --suppressions-list=ci-scripts/cppcheck_suppressions.list \ - -I common/utils \ - -I openair3/NAS/COMMON/UTIL \ - -j`nproc` . 2> cmake_targets/log/cppcheck.xml 1> cmake_targets/log/cppcheck_build.txt - -RUN grep -E -c 'severity="error' cmake_targets/log/cppcheck.xml - -RUN grep -E -c 'severity="warning' cmake_targets/log/cppcheck.xml - -RUN cat cmake_targets/log/cppcheck.xml diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 27cf36bcf8..906f73f433 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -198,9 +198,6 @@ def ExecuteActionWithParam(action, ctx, node): elif action == 'LicenceAndFormattingCheck': success = SCA.LicenceAndFormattingCheck(ctx, node, HTML) - elif action == 'Cppcheck_Analysis': - success = SCA.CppCheckAnalysis(ctx, node, HTML) - elif action == 'Push_Local_Registry': tag_prefix = test.findtext('tag_prefix') or "" success = CONTAINERS.Push_Image_to_Local_Registry(node, HTML, tag_prefix) diff --git a/ci-scripts/xml_class_list.yml b/ci-scripts/xml_class_list.yml index 52b44e0e96..d762c4b316 100755 --- a/ci-scripts/xml_class_list.yml +++ b/ci-scripts/xml_class_list.yml @@ -23,7 +23,6 @@ - Deploy_Object - Stop_Object - Undeploy_Object - - Cppcheck_Analysis - Deploy_Run_OC_PhySim - Build_Deploy_PhySim - LicenceAndFormattingCheck diff --git a/ci-scripts/xml_files/cppcheck.xml b/ci-scripts/xml_files/cppcheck.xml deleted file mode 100644 index 061c39b5b8..0000000000 --- a/ci-scripts/xml_files/cppcheck.xml +++ /dev/null @@ -1,20 +0,0 @@ - - - - build-tab - CPPCHECK Analysis - wrench - - - Create_Workspace - Create new Workspace - carabe - - - - Cppcheck_Analysis - Static Code Analysis with cppcheck - carabe - - - diff --git a/doc/README.md b/doc/README.md index 37fe2a980d..d1dfc2dc4d 100644 --- a/doc/README.md +++ b/doc/README.md @@ -135,6 +135,7 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs. - [clang-format.md](./clang-format.md): how to format the code. See also the next entry for an error detection tool. - [formatting](../tools/formatting/README.md) is a clang-format error detection tool +- [cppcheck](../tools/cppcheck/README.md) for static code analysis - [sanitizers.md](./dev_tools/sanitizers.md): how to run with ASan/UBSan/MemSAN/TSan - [iwyu](../tools/iwyu/README.md) is a tool to detect `#include` errors - [docker-dev-env](../tools/docker-dev-env/README.md) is a ubuntu24 docker development environment diff --git a/doc/TESTBenches.md b/doc/TESTBenches.md index d348ffe355..361d774215 100644 --- a/doc/TESTBenches.md +++ b/doc/TESTBenches.md @@ -96,10 +96,6 @@ information on how the images are built. - orion: Cross-compilation from Intel to ARM - base image from `Dockerfile.base.ubuntu.cross-arm64` - build image from `Dockerfile.build.ubuntu.cross-arm64` (no target images) -- [RAN-cppcheck](https://jenkins-oai.eurecom.fr/job/RAN-cppcheck/) - ~BUILD-ONLY ~4G-LTE ~5G-NR ~nrUE - - bellatrix - - performs static code analysis, currently not actively enforced - [RAN-RHEL8-Cluster-Image-Builder](https://jenkins-oai.eurecom.fr/job/RAN-RHEL8-Cluster-Image-Builder/) ~BUILD-ONLY ~4G-LTE ~5G-NR ~nrUE - cluster (`Asterix-OC-oaicicd-session` resource): RHEL image build using the OpenShift Cluster (using gcc/clang) diff --git a/tools/cppcheck/README.md b/tools/cppcheck/README.md new file mode 100644 index 0000000000..414f35a68a --- /dev/null +++ b/tools/cppcheck/README.md @@ -0,0 +1,28 @@ + + +# Static code analysis + +How to run static code analysis tool(s). + +[[_TOC_]] + +## cppcheck Overview + +This can run a (dockerized) cppcheck static code analysis. The docker-compose +file represents the status as run as in the CI until it's removal (because it +was not enforced). It is now here so that people could use it to find bugs (and +use the tool more easily). + +## cppcheck Usage + +From the openairinterface5g root, simply run + + ./tools/cppcheck/run-cppcheck.sh + +This will run cppcheck with options listed in this file, assuming you have it +installed. Alternatively, you can run the CI version as of Ubuntu 20 with + + cd tools/cppcheck && docker compose up --no-log-prefix + +You can upgrade to a newer version by modifying the `FROM` line in the +docker-compose file. diff --git a/tools/cppcheck/docker-compose.yaml b/tools/cppcheck/docker-compose.yaml new file mode 100644 index 0000000000..8b8c5878d0 --- /dev/null +++ b/tools/cppcheck/docker-compose.yaml @@ -0,0 +1,14 @@ +# SPDX-License-Identifier: MIT + +services: + cppcheck: + image: cppcheck + build: + dockerfile_inline: | + FROM ubuntu:focal + RUN DEBIAN_FRONTEND=noninteractive apt update && apt install -y cppcheck + volumes: + - ../../:/code/ + command: + bash -c "cd /code/ && ./tools/cppcheck/run-cppcheck.sh" + diff --git a/tools/cppcheck/run-cppcheck.sh b/tools/cppcheck/run-cppcheck.sh new file mode 100755 index 0000000000..5a5df0c752 --- /dev/null +++ b/tools/cppcheck/run-cppcheck.sh @@ -0,0 +1,11 @@ +#!/bin/bash +# SPDX-License-Identifier: MIT + +cppcheck --enable=warning --force \ + --inline-suppr \ + --quiet \ + -i openair1/PHY/CODING/nrLDPC_decoder/nrLDPC_decoder.c \ + --suppressions-list=tools/cppcheck/suppressions.list \ + -I common/utils \ + -I openair3/NAS/COMMON/UTIL \ + -j`nproc` . diff --git a/ci-scripts/cppcheck_suppressions.list b/tools/cppcheck/suppressions.list similarity index 100% rename from ci-scripts/cppcheck_suppressions.list rename to tools/cppcheck/suppressions.list From ff97f0dea611dc476a523b0edfef449e7eb7ab88 Mon Sep 17 00:00:00 2001 From: Robert Schmidt Date: Thu, 16 Apr 2026 00:28:50 +0200 Subject: [PATCH 72/72] LicenceAndFormattingCheck: remove global variable, pass directly Signed-off-by: Robert Schmidt --- ci-scripts/args_parse.py | 8 +------ ci-scripts/cls_static_code_analysis.py | 30 +++++++++----------------- ci-scripts/main.py | 8 +++---- 3 files changed, 14 insertions(+), 32 deletions(-) diff --git a/ci-scripts/args_parse.py b/ci-scripts/args_parse.py index 6206c67d69..16e23113ab 100644 --- a/ci-scripts/args_parse.py +++ b/ci-scripts/args_parse.py @@ -22,7 +22,7 @@ import constants as CONST #----------------------------------------------------------- -def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): +def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,CLUSTER): force_local = False date_fmt = None @@ -53,7 +53,6 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): RAN.ranRepository=matchReg.group(1) HTML.ranRepository=matchReg.group(1) CONTAINERS.ranRepository=matchReg.group(1) - SCA.ranRepository=matchReg.group(1) CLUSTER.ranRepository=matchReg.group(1) elif re.match(r'^\-\-eNB_AllowMerge=(.+)$|^\-\-ranAllowMerge=(.+)$', myArgv, re.IGNORECASE): if re.match(r'^\-\-eNB_AllowMerge=(.+)$', myArgv, re.IGNORECASE): @@ -66,7 +65,6 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): RAN.ranAllowMerge=True HTML.ranAllowMerge=True CONTAINERS.ranAllowMerge=True - SCA.ranAllowMerge=True CLUSTER.ranAllowMerge=True elif re.match(r'^\-\-eNBBranch=(.+)$|^\-\-ranBranch=(.+)$', myArgv, re.IGNORECASE): if re.match(r'^\-\-eNBBranch=(.+)$', myArgv, re.IGNORECASE): @@ -77,7 +75,6 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): RAN.ranBranch=matchReg.group(1) HTML.ranBranch=matchReg.group(1) CONTAINERS.ranBranch=matchReg.group(1) - SCA.ranBranch=matchReg.group(1) CLUSTER.ranBranch=matchReg.group(1) elif re.match(r'^\-\-eNBCommitID=(.*)$|^\-\-ranCommitID=(.*)$', myArgv, re.IGNORECASE): if re.match(r'^\-\-eNBCommitID=(.*)$', myArgv, re.IGNORECASE): @@ -88,7 +85,6 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): RAN.ranCommitID=matchReg.group(1) HTML.ranCommitID=matchReg.group(1) CONTAINERS.ranCommitID=matchReg.group(1) - SCA.ranCommitID=matchReg.group(1) CLUSTER.ranCommitID=matchReg.group(1) elif re.match(r'^\-\-eNBTargetBranch=(.*)$|^\-\-ranTargetBranch=(.*)$', myArgv, re.IGNORECASE): if re.match(r'^\-\-eNBTargetBranch=(.*)$', myArgv, re.IGNORECASE): @@ -99,7 +95,6 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): RAN.ranTargetBranch=matchReg.group(1) HTML.ranTargetBranch=matchReg.group(1) CONTAINERS.ranTargetBranch=matchReg.group(1) - SCA.ranTargetBranch=matchReg.group(1) CLUSTER.ranTargetBranch=matchReg.group(1) elif re.match(r'^\-\-eNBIPAddress=(.+)$|^\-\-eNB[1-2]IPAddress=(.+)$', myArgv, re.IGNORECASE): print("parameters --eNB*IPAddress ignored") @@ -112,7 +107,6 @@ def ArgsParse(argvs,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER): matchReg = re.match(r'^\-\-eNBSourceCodePath=(.+)$', myArgv, re.IGNORECASE) RAN.eNBSourceCodePath=matchReg.group(1) CONTAINERS.eNBSourceCodePath=matchReg.group(1) - SCA.eNBSourceCodePath=matchReg.group(1) CLUSTER.eNBSourceCodePath=matchReg.group(1) elif re.match(r'^\-\-eNB1SourceCodePath=(.+)$', myArgv, re.IGNORECASE): print("parameter --eNB1SourceCodePath ignored") diff --git a/ci-scripts/cls_static_code_analysis.py b/ci-scripts/cls_static_code_analysis.py index 4e388847c7..3b4ee10f9b 100644 --- a/ci-scripts/cls_static_code_analysis.py +++ b/ci-scripts/cls_static_code_analysis.py @@ -29,38 +29,28 @@ from cls_ci_helper import archiveArtifact class StaticCodeAnalysis(): - def __init__(self): - - self.ranRepository = '' - self.ranBranch = '' - self.ranAllowMerge = False - self.ranCommitID = '' - self.ranTargetBranch = '' - self.eNBSourceCodePath = '' - - def LicenceAndFormattingCheck(self, ctx, node, HTML): + def LicenceAndFormattingCheck(ctx, node, HTML, d, branch, allowMerge, targetBranch): # Workspace is no longer recreated from scratch. # It implies that this method shall be called last within a build pipeline # where workspace is already created - lSourcePath = self.eNBSourceCodePath - if not node or not lSourcePath: - raise ValueError(f"{lSourcePath=} {node=}") + if not node or not d: + raise ValueError(f"{d=} {node=}") logging.debug('Building on server: ' + node) cmd = cls_cmd.getConnection(node) check_options = '' - if self.ranAllowMerge: - check_options = f'--build-arg MERGE_REQUEST=true --build-arg SRC_BRANCH={self.ranBranch}' - if self.ranTargetBranch == '': - if self.ranBranch != 'develop' and self.ranBranch != 'origin/develop': + if allowMerge: + check_options = f'--build-arg MERGE_REQUEST=true --build-arg SRC_BRANCH={branch}' + if targetBranch == '': + if branch != 'develop' and branch != 'origin/develop': check_options += ' --build-arg TARGET_BRANCH=develop' else: - check_options += f' --build-arg TARGET_BRANCH={self.ranTargetBranch}' + check_options += f' --build-arg TARGET_BRANCH={targetBranch}' - logDir = f'{lSourcePath}/cmake_targets/log/' + logDir = f'{d}/cmake_targets/log/' cmd.run(f'mkdir -p {logDir}') cmd.run('docker image rm oai-formatting-check:latest') - cmd.run(f'docker build --target oai-formatting-check --tag oai-formatting-check:latest {check_options} --file {lSourcePath}/ci-scripts/docker/Dockerfile.formatting.ubuntu {lSourcePath} > {logDir}/oai-formatting-check.txt 2>&1') + cmd.run(f'docker build --target oai-formatting-check --tag oai-formatting-check:latest {check_options} --file {d}/ci-scripts/docker/Dockerfile.formatting.ubuntu {d} > {logDir}/oai-formatting-check.txt 2>&1') cmd.run('docker image rm oai-formatting-check:latest') cmd.run('docker image prune --force') diff --git a/ci-scripts/main.py b/ci-scripts/main.py index 906f73f433..66e8b2091b 100644 --- a/ci-scripts/main.py +++ b/ci-scripts/main.py @@ -22,7 +22,7 @@ import constants as CONST import cls_oaicitest #main class for OAI CI test framework import cls_containerize #class Containerize for all container-based operations on RAN/UE objects -import cls_static_code_analysis #class for static code analysis +import cls_static_code_analysis as SCA import cls_cluster # class for building/deploying on cluster import cls_native # class for all native/source-based operations from cls_ci_helper import TestCaseCtx @@ -64,7 +64,6 @@ def ExecuteActionWithParam(action, ctx, node): global RAN global HTML global CONTAINERS - global SCA global CLUSTER if action == 'Build_eNB' or action == 'Build_Image' or action == 'Build_Proxy' or action == "Build_Cluster_Image" or action == "Build_Run_Tests": RAN.Build_eNB_args=test.findtext('Build_eNB_args') @@ -196,7 +195,7 @@ def ExecuteActionWithParam(action, ctx, node): success = CONTAINERS.Create_Workspace(node, HTML) elif action == 'LicenceAndFormattingCheck': - success = SCA.LicenceAndFormattingCheck(ctx, node, HTML) + success = SCA.StaticCodeAnalysis.LicenceAndFormattingCheck(ctx, node, HTML, RAN.eNBSourceCodePath, RAN.ranBranch, RAN.ranAllowMerge, RAN.ranTargetBranch) elif action == 'Push_Local_Registry': tag_prefix = test.findtext('tag_prefix') or "" @@ -294,7 +293,6 @@ CiTestObj = cls_oaicitest.OaiCiTest() RAN = ran.RANManagement() HTML = cls_oai_html.HTMLManagement() CONTAINERS = cls_containerize.Containerize() -SCA = cls_static_code_analysis.StaticCodeAnalysis() CLUSTER = cls_cluster.Cluster() #----------------------------------------------------------- @@ -304,7 +302,7 @@ CLUSTER = cls_cluster.Cluster() import args_parse # Force local execution, move all execution targets to localhost force_local = False -mode, force_local, date_fmt = args_parse.ArgsParse(sys.argv,CiTestObj,RAN,HTML,CONTAINERS,HELP,SCA,CLUSTER) +mode, force_local, date_fmt = args_parse.ArgsParse(sys.argv,CiTestObj,RAN,HTML,CONTAINERS,HELP,CLUSTER) fmt = "%(levelname)8s: %(message)s" if date_fmt: fmt = "[%(asctime)s] %(levelname)s %(message)s"